external_config_test.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package sub
  2. import (
  3. "encoding/base64"
  4. "net/url"
  5. "strings"
  6. "testing"
  7. "github.com/goccy/go-json"
  8. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  9. )
  10. func TestApplyRemarkToLinkRewritesFragment(t *testing.T) {
  11. link := "vless://[email protected]:443?security=reality&pbk=abc&sid=12#old-name"
  12. out := applyRemarkToLink(link, "DE-Provider")
  13. u, err := url.Parse(out)
  14. if err != nil {
  15. t.Fatalf("parse: %v", err)
  16. }
  17. if u.Fragment != "DE-Provider" {
  18. t.Fatalf("fragment = %q, want DE-Provider", u.Fragment)
  19. }
  20. // Everything before the fragment must be byte-for-byte preserved.
  21. if !strings.HasPrefix(out, "vless://[email protected]:443?security=reality&pbk=abc&sid=12#") {
  22. t.Fatalf("link body altered: %s", out)
  23. }
  24. }
  25. func TestApplyRemarkToLinkVmessSetsPs(t *testing.T) {
  26. payload := map[string]any{"v": "2", "ps": "old", "add": "1.2.3.4", "port": "443", "id": "uuid"}
  27. b, _ := json.Marshal(payload)
  28. link := "vmess://" + base64.StdEncoding.EncodeToString(b)
  29. out := applyRemarkToLink(link, "NL-Node")
  30. raw, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(out, "vmess://"))
  31. if err != nil {
  32. t.Fatalf("decode: %v", err)
  33. }
  34. var got map[string]any
  35. if err := json.Unmarshal(raw, &got); err != nil {
  36. t.Fatalf("unmarshal: %v", err)
  37. }
  38. if got["ps"] != "NL-Node" {
  39. t.Fatalf("ps = %v, want NL-Node", got["ps"])
  40. }
  41. if got["id"] != "uuid" {
  42. t.Fatalf("credentials lost: %v", got)
  43. }
  44. }
  45. func TestApplyRemarkEmptyKeepsLinkVerbatim(t *testing.T) {
  46. link := "trojan://[email protected]:8443?security=tls#orig"
  47. if out := applyRemarkToLink(link, ""); out != link {
  48. t.Fatalf("empty remark altered link: %s", out)
  49. }
  50. }
  51. func TestParsedExternalOutboundTagsProxy(t *testing.T) {
  52. link := "vless://[email protected]:443?type=tcp&security=reality&pbk=abc&sid=12&fp=chrome#srv"
  53. data := parsedExternalOutbound(link)
  54. if data == nil {
  55. t.Fatal("expected an outbound, got nil")
  56. }
  57. var ob map[string]any
  58. if err := json.Unmarshal(data, &ob); err != nil {
  59. t.Fatalf("unmarshal: %v", err)
  60. }
  61. if ob["tag"] != "proxy" {
  62. t.Fatalf("tag = %v, want proxy", ob["tag"])
  63. }
  64. if ob["protocol"] != "vless" {
  65. t.Fatalf("protocol = %v, want vless", ob["protocol"])
  66. }
  67. }
  68. func TestDecodeSubscriptionBodyBase64(t *testing.T) {
  69. plain := "vless://[email protected]:443#one\ntrojan://[email protected]:8443#two\n"
  70. body := []byte(base64.StdEncoding.EncodeToString([]byte(plain)))
  71. links := decodeSubscriptionBody(body)
  72. if len(links) != 2 || links[0] != "vless://[email protected]:443#one" || links[1] != "trojan://[email protected]:8443#two" {
  73. t.Fatalf("decoded links = %#v", links)
  74. }
  75. }
  76. func TestDecodeSubscriptionBodyPlainSkipsComments(t *testing.T) {
  77. body := []byte("# header\nvmess://abc\n\nnot-a-link\nss://def#x\n")
  78. links := decodeSubscriptionBody(body)
  79. if len(links) != 2 || links[0] != "vmess://abc" || links[1] != "ss://def#x" {
  80. t.Fatalf("decoded links = %#v", links)
  81. }
  82. }
  83. func TestExpandEntryLinkAppliesRemark(t *testing.T) {
  84. got := expandEntry(externalLinkEntry{Kind: model.ExternalLinkKindLink, Value: "trojan://[email protected]:8443#orig", Remark: "DE"})
  85. if len(got) != 1 || got[0].Name != "DE" {
  86. t.Fatalf("expandEntry = %#v", got)
  87. }
  88. }
  89. func TestClashProxyFromExternalTrojanReality(t *testing.T) {
  90. link := "trojan://[email protected]:8443?type=tcp&security=reality&sni=aws.amazon.com&pbk=PBK&sid=298b44&fp=chrome#srv"
  91. svc := NewSubClashService(false, "", NewSubService(false, "-io"))
  92. proxy := svc.clashProxyFromExternal(link, "DE-Provider")
  93. if proxy == nil {
  94. t.Fatal("expected a clash proxy, got nil")
  95. }
  96. if proxy["type"] != "trojan" {
  97. t.Fatalf("type = %v, want trojan", proxy["type"])
  98. }
  99. if proxy["server"] != "37.27.201.56" {
  100. t.Fatalf("server = %v", proxy["server"])
  101. }
  102. if proxy["password"] != "provider-pass" {
  103. t.Fatalf("password = %v", proxy["password"])
  104. }
  105. if proxy["name"] != "DE-Provider" {
  106. t.Fatalf("name = %v", proxy["name"])
  107. }
  108. if proxy["tls"] != true {
  109. t.Fatalf("expected reality→tls true, got %v", proxy["tls"])
  110. }
  111. }