mtproto_fake_test.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. package service
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "strings"
  7. "testing"
  8. "time"
  9. "github.com/mhsanaei/3x-ui/v3/internal/database"
  10. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  11. "github.com/mhsanaei/3x-ui/v3/internal/mtproto"
  12. "github.com/mhsanaei/3x-ui/v3/internal/xray"
  13. )
  14. const (
  15. mtprotoTestSecretA = "ee00112233445566778899aabbccddeeff6578616d706c652e636f6d"
  16. mtprotoTestSecretB = "ee101112131415161718191a1b1c1d1e1f6578616d706c652e636f6d"
  17. mtprotoTestSecretC = "ee202122232425262728292a2b2c2d2e2f6578616d706c652e636f6d"
  18. mtprotoTestSecretD = "ee303132333435363738393a3b3c3d3e3f6578616d706c652e636f6d"
  19. )
  20. func seedClientTraffic(t *testing.T, inboundId int, email string, enable bool) {
  21. t.Helper()
  22. row := xray.ClientTraffic{InboundId: inboundId, Email: email, Enable: enable}
  23. if err := database.GetDB().Create(&row).Error; err != nil {
  24. t.Fatalf("seed traffic %s: %v", email, err)
  25. }
  26. }
  27. func loadInboundByTag(t *testing.T, tag string) *model.Inbound {
  28. t.Helper()
  29. var ib model.Inbound
  30. if err := database.GetDB().Where("tag = ?", tag).First(&ib).Error; err != nil {
  31. t.Fatalf("load inbound %s: %v", tag, err)
  32. }
  33. return &ib
  34. }
  35. // fakeMtgChildMain is what the re-executed test binary runs when posing as an
  36. // mtg child process: it appends its pid to the file named by MTG_FAKE_PIDFILE
  37. // so tests can count spawns, then blocks until the manager kills it.
  38. func fakeMtgChildMain() {
  39. if f, err := os.OpenFile(os.Getenv("MTG_FAKE_PIDFILE"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644); err == nil {
  40. fmt.Fprintf(f, "%d\n", os.Getpid())
  41. f.Close()
  42. }
  43. select {}
  44. }
  45. // installFakeMtg points the mtproto manager at a copy of the running test
  46. // binary posing as mtg (via the MTG_FAKE_CHILD gate in TestMain) and returns
  47. // the pid file whose line count equals the number of processes spawned so far.
  48. func installFakeMtg(t *testing.T) string {
  49. t.Helper()
  50. binDir := t.TempDir()
  51. self, err := os.Executable()
  52. if err != nil {
  53. t.Fatalf("locate test binary: %v", err)
  54. }
  55. payload, err := os.ReadFile(self)
  56. if err != nil {
  57. t.Fatalf("read test binary: %v", err)
  58. }
  59. if err := os.WriteFile(filepath.Join(binDir, mtproto.GetBinaryName()), payload, 0o755); err != nil {
  60. t.Fatalf("install fake mtg: %v", err)
  61. }
  62. pidFile := filepath.Join(binDir, "mtg-pids.txt")
  63. t.Setenv("XUI_BIN_FOLDER", binDir)
  64. t.Setenv("MTG_FAKE_CHILD", "1")
  65. t.Setenv("MTG_FAKE_PIDFILE", pidFile)
  66. return pidFile
  67. }
  68. func countSpawns(t *testing.T, pidFile string) int {
  69. t.Helper()
  70. data, err := os.ReadFile(pidFile)
  71. if os.IsNotExist(err) {
  72. return 0
  73. }
  74. if err != nil {
  75. t.Fatalf("read pid file: %v", err)
  76. }
  77. return len(strings.Fields(string(data)))
  78. }
  79. // waitForSpawns polls until exactly want processes have registered, failing
  80. // fast when the count overshoots and on timeout.
  81. func waitForSpawns(t *testing.T, pidFile string, want int) {
  82. t.Helper()
  83. deadline := time.Now().Add(5 * time.Second)
  84. for {
  85. got := countSpawns(t, pidFile)
  86. if got == want {
  87. return
  88. }
  89. if got > want {
  90. t.Fatalf("expected %d mtg spawn(s), got %d", want, got)
  91. }
  92. if time.Now().After(deadline) {
  93. t.Fatalf("expected %d mtg spawn(s), still %d after timeout", want, got)
  94. }
  95. time.Sleep(20 * time.Millisecond)
  96. }
  97. }
  98. // assertNoNewSpawns gives a wrongly spawned child time to register, then
  99. // asserts the spawn count is still exactly want.
  100. func assertNoNewSpawns(t *testing.T, pidFile string, want int) {
  101. t.Helper()
  102. time.Sleep(500 * time.Millisecond)
  103. if got := countSpawns(t, pidFile); got != want {
  104. t.Fatalf("expected the mtg process to be kept (%d spawn(s)), got %d", want, got)
  105. }
  106. }