client_sync_multiprotocol_test.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package service
  2. import (
  3. "path/filepath"
  4. "testing"
  5. "github.com/mhsanaei/3x-ui/v3/database"
  6. "github.com/mhsanaei/3x-ui/v3/database/model"
  7. )
  8. func TestSyncInbound_PreservesCredentialsAcrossProtocols(t *testing.T) {
  9. dbDir := t.TempDir()
  10. t.Setenv("XUI_DB_FOLDER", dbDir)
  11. if err := database.InitDB(filepath.Join(dbDir, "3x-ui.db")); err != nil {
  12. t.Fatalf("InitDB: %v", err)
  13. }
  14. t.Cleanup(func() { _ = database.CloseDB() })
  15. db := database.GetDB()
  16. vlessInbound := &model.Inbound{Tag: "vless-in", Enable: true, Port: 10001, Protocol: model.VLESS}
  17. if err := db.Create(vlessInbound).Error; err != nil {
  18. t.Fatalf("create vless inbound: %v", err)
  19. }
  20. hysteriaInbound := &model.Inbound{Tag: "hy-in", Enable: true, Port: 10002, Protocol: model.Hysteria2}
  21. if err := db.Create(hysteriaInbound).Error; err != nil {
  22. t.Fatalf("create hysteria inbound: %v", err)
  23. }
  24. svc := ClientService{}
  25. const sharedEmail = "[email protected]"
  26. const wantUUID = "ce8d33df-3a64-4f10-8f9b-91c3a8e0c001"
  27. const wantAuth = "h2-auth-token"
  28. const wantFlow = "xtls-rprx-vision"
  29. vlessClient := model.Client{Email: sharedEmail, ID: wantUUID, Enable: true, Flow: wantFlow}
  30. if err := svc.SyncInbound(nil, vlessInbound.Id, []model.Client{vlessClient}); err != nil {
  31. t.Fatalf("vless SyncInbound: %v", err)
  32. }
  33. hysteriaClient := model.Client{Email: sharedEmail, Auth: wantAuth, Enable: true}
  34. if err := svc.SyncInbound(nil, hysteriaInbound.Id, []model.Client{hysteriaClient}); err != nil {
  35. t.Fatalf("hysteria SyncInbound: %v", err)
  36. }
  37. var row model.ClientRecord
  38. if err := db.Where("email = ?", sharedEmail).First(&row).Error; err != nil {
  39. t.Fatalf("lookup client row: %v", err)
  40. }
  41. if row.UUID != wantUUID {
  42. t.Errorf("UUID was clobbered by Hysteria sync: got %q, want %q", row.UUID, wantUUID)
  43. }
  44. if row.Auth != wantAuth {
  45. t.Errorf("Auth not persisted: got %q, want %q", row.Auth, wantAuth)
  46. }
  47. vlessList, err := svc.ListForInbound(nil, vlessInbound.Id)
  48. if err != nil {
  49. t.Fatalf("ListForInbound(vless): %v", err)
  50. }
  51. if len(vlessList) != 1 || vlessList[0].Flow != wantFlow {
  52. t.Errorf("VLESS inbound should still report flow=%q via FlowOverride, got %#v", wantFlow, vlessList)
  53. }
  54. hysteriaList, err := svc.ListForInbound(nil, hysteriaInbound.Id)
  55. if err != nil {
  56. t.Fatalf("ListForInbound(hysteria): %v", err)
  57. }
  58. if len(hysteriaList) != 1 || hysteriaList[0].Flow != "" {
  59. t.Errorf("Hysteria inbound should report empty flow, got %#v", hysteriaList)
  60. }
  61. }
  62. func TestSyncInbound_AllowsClearingFlow(t *testing.T) {
  63. dbDir := t.TempDir()
  64. t.Setenv("XUI_DB_FOLDER", dbDir)
  65. if err := database.InitDB(filepath.Join(dbDir, "3x-ui.db")); err != nil {
  66. t.Fatalf("InitDB: %v", err)
  67. }
  68. t.Cleanup(func() { _ = database.CloseDB() })
  69. db := database.GetDB()
  70. vless := &model.Inbound{Tag: "vless-in", Enable: true, Port: 10003, Protocol: model.VLESS}
  71. if err := db.Create(vless).Error; err != nil {
  72. t.Fatalf("create vless inbound: %v", err)
  73. }
  74. svc := ClientService{}
  75. const email = "[email protected]"
  76. const uid = "ce8d33df-3a64-4f10-8f9b-91c3a8e0c002"
  77. withFlow := model.Client{Email: email, ID: uid, Enable: true, Flow: "xtls-rprx-vision"}
  78. if err := svc.SyncInbound(nil, vless.Id, []model.Client{withFlow}); err != nil {
  79. t.Fatalf("vless SyncInbound (set flow): %v", err)
  80. }
  81. cleared := model.Client{Email: email, ID: uid, Enable: true, Flow: ""}
  82. if err := svc.SyncInbound(nil, vless.Id, []model.Client{cleared}); err != nil {
  83. t.Fatalf("vless SyncInbound (clear flow): %v", err)
  84. }
  85. list, err := svc.ListForInbound(nil, vless.Id)
  86. if err != nil {
  87. t.Fatalf("ListForInbound: %v", err)
  88. }
  89. if len(list) != 1 {
  90. t.Fatalf("expected 1 client, got %d", len(list))
  91. }
  92. if list[0].Flow != "" {
  93. t.Errorf("flow should be clearable on the owning inbound, got %q (Copilot review on #4545)", list[0].Flow)
  94. }
  95. }