1
0

links_test.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. package sub
  2. import (
  3. "reflect"
  4. "strings"
  5. "testing"
  6. "github.com/mhsanaei/3x-ui/v3/internal/database"
  7. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  8. )
  9. func TestSplitLinkLines(t *testing.T) {
  10. cases := []struct {
  11. name string
  12. in string
  13. want []string
  14. }{
  15. {"single_line", "vless://abc", []string{"vless://abc"}},
  16. {"two_lines", "vless://abc\nvmess://xyz", []string{"vless://abc", "vmess://xyz"}},
  17. {"trims_each_line", " vless://abc \n\tvmess://xyz\t", []string{"vless://abc", "vmess://xyz"}},
  18. {"skips_blank_lines", "vless://abc\n\n\nvmess://xyz\n", []string{"vless://abc", "vmess://xyz"}},
  19. }
  20. for _, c := range cases {
  21. t.Run(c.name, func(t *testing.T) {
  22. got := splitLinkLines(c.in)
  23. if !reflect.DeepEqual(got, c.want) {
  24. t.Fatalf("splitLinkLines(%q) = %#v, want %#v", c.in, got, c.want)
  25. }
  26. })
  27. }
  28. }
  29. func TestSplitLinkLines_EmptyInputIsNil(t *testing.T) {
  30. if got := splitLinkLines(""); got != nil {
  31. t.Fatalf("splitLinkLines(\"\") = %#v, want nil", got)
  32. }
  33. }
  34. func TestSplitLinkLines_WhitespaceOnlyHasNoEntries(t *testing.T) {
  35. got := splitLinkLines(" \n\t \n")
  36. if len(got) != 0 {
  37. t.Fatalf("splitLinkLines(whitespace) = %#v, want empty slice", got)
  38. }
  39. }
  40. func TestLinksForClient_UsesHostEndpoints(t *testing.T) {
  41. seedSubDB(t)
  42. inbound := seedSubInbound(t, "s-gate", "gate", 4431, 1, `{"network":"tcp","security":"none"}`)
  43. seedHost(t, &model.Host{
  44. InboundId: inbound.Id, Remark: "public", Address: "proxy.example.com",
  45. Port: 443, Security: "same",
  46. })
  47. links := NewLinkProvider().LinksForClient("req.example.com", inbound, "gate@e")
  48. if len(links) != 1 {
  49. t.Fatalf("links = %d, want 1: %v", len(links), links)
  50. }
  51. if !strings.Contains(links[0], "proxy.example.com:443") {
  52. t.Fatalf("link = %q, want the host endpoint proxy.example.com:443", links[0])
  53. }
  54. }
  55. // LinksForClient (per-client QR / links API) must use the clients-table UUID
  56. // when the inbound settings JSON still embeds a stale id — same source as
  57. // /inbounds/list and allLinks (#6436).
  58. func TestLinksForClient_UsesClientsTableUUIDWhenSettingsStale(t *testing.T) {
  59. seedSubDB(t)
  60. db := database.GetDB()
  61. stale := "11111111-1111-1111-1111-111111111111"
  62. fresh := "22222222-2222-2222-2222-222222222222"
  63. settings := `{"clients":[{"id":"` + stale + `","email":"stale@e","subId":"subStale","enable":true}],"decryption":"none"}`
  64. ib := &model.Inbound{
  65. UserId: 1, Tag: "stale-uuid-qr", Enable: true, Listen: "203.0.113.5", Port: 4433,
  66. Protocol: model.VLESS, Remark: "StaleQR", Settings: settings,
  67. StreamSettings: `{"network":"tcp","security":"none","tcpSettings":{"header":{"type":"none"}}}`,
  68. }
  69. if err := db.Create(ib).Error; err != nil {
  70. t.Fatalf("seed inbound: %v", err)
  71. }
  72. client := &model.ClientRecord{Email: "stale@e", SubID: "subStale", UUID: fresh, Enable: true}
  73. if err := db.Create(client).Error; err != nil {
  74. t.Fatalf("seed client: %v", err)
  75. }
  76. if err := db.Create(&model.ClientInbound{ClientId: client.Id, InboundId: ib.Id}).Error; err != nil {
  77. t.Fatalf("seed client_inbound: %v", err)
  78. }
  79. links := NewLinkProvider().LinksForClient("req.example.com", ib, "stale@e")
  80. if len(links) != 1 {
  81. t.Fatalf("links = %d, want 1: %v", len(links), links)
  82. }
  83. if !strings.Contains(links[0], fresh) {
  84. t.Fatalf("link missing fresh UUID %q: %s", fresh, links[0])
  85. }
  86. if strings.Contains(links[0], stale) {
  87. t.Fatalf("link still carries stale settings UUID %q: %s", stale, links[0])
  88. }
  89. }