client_external_link_test.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. package service
  2. import (
  3. "testing"
  4. "github.com/mhsanaei/3x-ui/v3/internal/database"
  5. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  6. )
  7. func externalLinkBool(v bool) *bool {
  8. return &v
  9. }
  10. func TestSetExternalLinksPersistsEnableState(t *testing.T) {
  11. setupBulkDB(t)
  12. db := database.GetDB()
  13. svc := &ClientService{}
  14. rec := model.ClientRecord{Email: "[email protected]", SubID: "sub-links", UUID: "uuid", Enable: true}
  15. if err := db.Create(&rec).Error; err != nil {
  16. t.Fatalf("create client: %v", err)
  17. }
  18. if err := svc.SetExternalLinksForRecord(rec.Id, []ExternalLinkInput{
  19. {Kind: model.ExternalLinkKindLink, Value: "trojan://[email protected]:443#on", Remark: "Primary", Enable: externalLinkBool(true), ExpiryTime: 1767225600000},
  20. {Kind: model.ExternalLinkKindSubscription, Value: "https://provider.example/sub", Remark: "Provider", Enable: externalLinkBool(false), NamePrefix: "[zjh] "},
  21. {Kind: model.ExternalLinkKindLink, Value: "trojan://[email protected]:443#default"},
  22. }); err != nil {
  23. t.Fatalf("set external links: %v", err)
  24. }
  25. rows, err := svc.GetExternalLinksForRecord(rec.Id)
  26. if err != nil {
  27. t.Fatalf("get external links: %v", err)
  28. }
  29. if len(rows) != 3 {
  30. t.Fatalf("rows = %d, want 3", len(rows))
  31. }
  32. if rows[0].Enable == nil || *rows[0].Enable != true {
  33. t.Fatalf("first row enable = %#v, want true", rows[0].Enable)
  34. }
  35. if rows[1].Enable == nil || *rows[1].Enable != false {
  36. t.Fatalf("second row enable = %#v, want false", rows[1].Enable)
  37. }
  38. if rows[2].Enable == nil || *rows[2].Enable != true {
  39. t.Fatalf("omitted enable should default true, got %#v", rows[2].Enable)
  40. }
  41. if rows[0].Remark != "Primary" || rows[0].ExpiryTime != 1767225600000 {
  42. t.Fatalf("first row fields not persisted: %#v", rows[0])
  43. }
  44. if rows[1].Remark != "Provider" || rows[1].NamePrefix != "[zjh] " {
  45. t.Fatalf("subscription fields not persisted: %#v", rows[1])
  46. }
  47. }
  48. func TestSetExternalLinksPreservesFetchStatus(t *testing.T) {
  49. setupBulkDB(t)
  50. db := database.GetDB()
  51. svc := &ClientService{}
  52. rec := model.ClientRecord{Email: "[email protected]", SubID: "sub-status", UUID: "uuid", Enable: true}
  53. if err := db.Create(&rec).Error; err != nil {
  54. t.Fatalf("create client: %v", err)
  55. }
  56. row := model.ClientExternalLink{
  57. ClientId: rec.Id,
  58. Kind: model.ExternalLinkKindSubscription,
  59. Value: "https://provider.example/sub",
  60. Remark: "old",
  61. LastFetchAt: 1767220000000,
  62. LastFetchError: "timeout",
  63. SortIndex: 0,
  64. }
  65. if err := db.Create(&row).Error; err != nil {
  66. t.Fatalf("create external link: %v", err)
  67. }
  68. if err := svc.SetExternalLinksForRecord(rec.Id, []ExternalLinkInput{
  69. {Kind: row.Kind, Value: row.Value, Remark: "new", Enable: externalLinkBool(true)},
  70. }); err != nil {
  71. t.Fatalf("set external links: %v", err)
  72. }
  73. rows, err := svc.GetExternalLinksForRecord(rec.Id)
  74. if err != nil {
  75. t.Fatalf("get external links: %v", err)
  76. }
  77. if len(rows) != 1 {
  78. t.Fatalf("rows = %d, want 1", len(rows))
  79. }
  80. if rows[0].LastFetchAt != row.LastFetchAt || rows[0].LastFetchError != row.LastFetchError {
  81. t.Fatalf("fetch status not preserved: %#v", rows[0])
  82. }
  83. if rows[0].Remark != "new" {
  84. t.Fatalf("editable fields not updated: %#v", rows[0])
  85. }
  86. }
  87. func TestSetExternalLinksRejectsNegativeExpiry(t *testing.T) {
  88. setupBulkDB(t)
  89. db := database.GetDB()
  90. svc := &ClientService{}
  91. rec := model.ClientRecord{Email: "[email protected]", SubID: "sub-negative", UUID: "uuid", Enable: true}
  92. if err := db.Create(&rec).Error; err != nil {
  93. t.Fatalf("create client: %v", err)
  94. }
  95. err := svc.SetExternalLinksForRecord(rec.Id, []ExternalLinkInput{
  96. {Kind: model.ExternalLinkKindLink, Value: "trojan://[email protected]:443#neg", ExpiryTime: -86400000},
  97. })
  98. want := "external link expiryTime must be 0 (never) or a future unix millisecond timestamp: trojan://[email protected]:443#neg\n"
  99. if err == nil || err.Error() != want {
  100. t.Fatalf("err = %v, want %q", err, want)
  101. }
  102. rows, err := svc.GetExternalLinksForRecord(rec.Id)
  103. if err != nil {
  104. t.Fatalf("get external links: %v", err)
  105. }
  106. if len(rows) != 0 {
  107. t.Fatalf("rows = %d, want the rejected save to persist nothing", len(rows))
  108. }
  109. }