endpoint_test.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package sub
  2. import (
  3. "fmt"
  4. "testing"
  5. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  6. )
  7. // N1 — externalProxyToEndpoint maps the scalar fields and carries the source
  8. // entry so delegated TLS application reproduces the legacy presence-tracked
  9. // overrides (absent key never clobbers an upstream value).
  10. func TestExternalProxyToEndpoint(t *testing.T) {
  11. ep := map[string]any{
  12. "forceTls": "tls",
  13. "dest": "cdn.example.com",
  14. "port": float64(8443),
  15. "remark": "R",
  16. "sni": "s.example.com",
  17. }
  18. e := externalProxyToEndpoint(ep)
  19. if e.Address != "cdn.example.com" {
  20. t.Fatalf("Address = %q, want cdn.example.com", e.Address)
  21. }
  22. if e.Port != 8443 {
  23. t.Fatalf("Port = %d, want 8443", e.Port)
  24. }
  25. if e.ForceTls != "tls" {
  26. t.Fatalf("ForceTls = %q, want tls", e.ForceTls)
  27. }
  28. if e.Remark != "R" {
  29. t.Fatalf("Remark = %q, want R", e.Remark)
  30. }
  31. if e.ep == nil {
  32. t.Fatalf("ep not carried; delegated TLS application would lose the source entry")
  33. }
  34. // Delegation preserves the sni override and does not invent absent fields.
  35. params := map[string]string{}
  36. applyEndpointTLSParams(e, params, "tls")
  37. if params["sni"] != "s.example.com" {
  38. t.Fatalf("delegated sni = %q, want s.example.com", params["sni"])
  39. }
  40. if _, ok := params["fp"]; ok {
  41. t.Fatalf("absent fingerprint must not be set, got fp=%q", params["fp"])
  42. }
  43. }
  44. // N2 — inboundDefaultEndpoint reproduces the no-externalProxy default: resolved
  45. // address + inbound port, forceTls "same", empty remark, no source entry.
  46. func TestInboundDefaultEndpoint(t *testing.T) {
  47. in := &model.Inbound{Listen: "198.51.100.7", Port: 8080}
  48. s := &SubService{}
  49. e := s.inboundDefaultEndpoint(in)
  50. if e.Address != "198.51.100.7" {
  51. t.Fatalf("Address = %q, want 198.51.100.7", e.Address)
  52. }
  53. if e.Port != 8080 {
  54. t.Fatalf("Port = %d, want 8080", e.Port)
  55. }
  56. if e.ForceTls != "same" {
  57. t.Fatalf("ForceTls = %q, want same", e.ForceTls)
  58. }
  59. if e.Remark != "" {
  60. t.Fatalf("Remark = %q, want empty", e.Remark)
  61. }
  62. if e.ep != nil {
  63. t.Fatalf("default endpoint must not carry a source externalProxy entry")
  64. }
  65. }
  66. // N3 — buildEndpointLinks renders the param-form path: one link per endpoint,
  67. // TLS override applied for tls, fields stripped + security overridden for none,
  68. // joined by "\n", in order.
  69. func TestBuildEndpointLinks_ParamForm(t *testing.T) {
  70. s := &SubService{}
  71. in := &model.Inbound{Remark: "ib"}
  72. params := map[string]string{"type": "tcp", "security": "tls", "sni": "base.sni", "fp": "chrome"}
  73. eps := []ShareEndpoint{
  74. externalProxyToEndpoint(map[string]any{"forceTls": "tls", "dest": "a.example.com", "port": float64(8443), "remark": "A", "sni": "a.sni"}),
  75. externalProxyToEndpoint(map[string]any{"forceTls": "none", "dest": "b.example.com", "port": float64(80), "remark": "B"}),
  76. }
  77. got := s.buildEndpointLinks(eps, params, "tls",
  78. func(dest string, port int) string { return fmt.Sprintf("vless://uid@%s", joinHostPort(dest, port)) },
  79. func(e ShareEndpoint) string { return s.genRemark(in, "user", e.Remark) },
  80. )
  81. want := "vless://[email protected]:8443?fp=chrome&security=tls&sni=a.sni&type=tcp#ib-A\n" +
  82. "vless://[email protected]:80?security=none&type=tcp#ib-B"
  83. if got != want {
  84. t.Fatalf("N3 mismatch.\n got: %q\nwant: %q", got, want)
  85. }
  86. }
  87. // N4 — buildEndpointVmessLinks renders the object-form path: base obj cloned per
  88. // endpoint, add/port/tls rewritten, sni override applied, none-strip honored.
  89. func TestBuildEndpointVmessLinks(t *testing.T) {
  90. s := &SubService{}
  91. in := &model.Inbound{Remark: "ib"}
  92. baseObj := map[string]any{
  93. "v": "2", "add": "base.example.com", "port": 443, "type": "none",
  94. "id": "uid", "scy": "auto", "net": "tcp",
  95. "tls": "tls", "sni": "base.sni", "alpn": "h2", "fp": "chrome",
  96. }
  97. eps := []ShareEndpoint{
  98. externalProxyToEndpoint(map[string]any{"forceTls": "same", "dest": "a.example.com", "port": float64(8443), "remark": "A", "sni": "a.sni"}),
  99. externalProxyToEndpoint(map[string]any{"forceTls": "none", "dest": "b.example.com", "port": float64(80), "remark": "B"}),
  100. }
  101. got := s.buildEndpointVmessLinks(eps, baseObj, in, "user")
  102. want := "vmess://ewogICJhZGQiOiAiYS5leGFtcGxlLmNvbSIsCiAgImFscG4iOiAiaDIiLAogICJmcCI6ICJjaHJvbWUiLAogICJpZCI6ICJ1aWQiLAogICJuZXQiOiAidGNwIiwKICAicG9ydCI6IDg0NDMsCiAgInBzIjogImliLUEiLAogICJzY3kiOiAiYXV0byIsCiAgInNuaSI6ICJhLnNuaSIsCiAgInRscyI6ICJ0bHMiLAogICJ0eXBlIjogIm5vbmUiLAogICJ2IjogIjIiCn0=\n" +
  103. "vmess://ewogICJhZGQiOiAiYi5leGFtcGxlLmNvbSIsCiAgImlkIjogInVpZCIsCiAgIm5ldCI6ICJ0Y3AiLAogICJwb3J0IjogODAsCiAgInBzIjogImliLUIiLAogICJzY3kiOiAiYXV0byIsCiAgInRscyI6ICJub25lIiwKICAidHlwZSI6ICJub25lIiwKICAidiI6ICIyIgp9"
  104. if got != want {
  105. t.Fatalf("N4 mismatch.\n got: %q\nwant: %q", got, want)
  106. }
  107. }