1
0

remote_test.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package runtime
  2. import (
  3. "encoding/json"
  4. "testing"
  5. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  6. )
  7. // cacheGetTag must resolve a remote inbound id even when the n<id>- prefix
  8. // sits on only one side: the node may store the bare tag while the central
  9. // panel pushes the prefixed form, or vice versa. Without this a mismatch makes
  10. // the push create a duplicate inbound on the node.
  11. func TestCacheGetTag_PrefixAgnostic(t *testing.T) {
  12. cases := []struct {
  13. name string
  14. cacheTag string
  15. lookup string
  16. wantID int
  17. wantFound bool
  18. }{
  19. {"exact", "n1-in-443-tcp", "n1-in-443-tcp", 7, true},
  20. {"node bare, lookup prefixed", "in-443-tcp", "n1-in-443-tcp", 7, true},
  21. {"node prefixed, lookup bare", "n1-in-443-tcp", "in-443-tcp", 7, true},
  22. {"unrelated tag", "in-443-tcp", "in-999-tcp", 0, false},
  23. }
  24. for _, c := range cases {
  25. t.Run(c.name, func(t *testing.T) {
  26. r := NewRemote(&model.Node{Id: 1, Name: "n1"})
  27. r.cacheSet(c.cacheTag, 7)
  28. id, ok := r.cacheGetTag(c.lookup)
  29. if ok != c.wantFound || id != c.wantID {
  30. t.Fatalf("cacheGetTag(%q) = (%d, %v), want (%d, %v)", c.lookup, id, ok, c.wantID, c.wantFound)
  31. }
  32. })
  33. }
  34. }
  35. func TestWireInboundIncludesShareAddressFields(t *testing.T) {
  36. values := wireInbound(&model.Inbound{
  37. ShareAddrStrategy: "custom",
  38. ShareAddr: "edge.example.com",
  39. })
  40. if got := values.Get("shareAddrStrategy"); got != "custom" {
  41. t.Fatalf("shareAddrStrategy = %q, want custom", got)
  42. }
  43. if got := values.Get("shareAddr"); got != "edge.example.com" {
  44. t.Fatalf("shareAddr = %q, want edge.example.com", got)
  45. }
  46. }
  47. func TestWireInboundDefaultsShareAddressStrategy(t *testing.T) {
  48. values := wireInbound(&model.Inbound{})
  49. if got := values.Get("shareAddrStrategy"); got != "node" {
  50. t.Fatalf("shareAddrStrategy = %q, want node", got)
  51. }
  52. values = wireInbound(&model.Inbound{ShareAddrStrategy: "auto"})
  53. if got := values.Get("shareAddrStrategy"); got != "node" {
  54. t.Fatalf("invalid shareAddrStrategy = %q, want node", got)
  55. }
  56. }
  57. func TestSanitizeStreamSettingsForRemote(t *testing.T) {
  58. tests := []struct {
  59. name string
  60. input string
  61. // wantCertFile / wantKeyFile: expected presence after sanitize
  62. wantCertFile bool
  63. wantKeyFile bool
  64. }{
  65. {
  66. name: "file paths only — kept intact (remote node paths)",
  67. input: `{
  68. "tlsSettings": {
  69. "certificates": [{
  70. "certificateFile": "/etc/ssl/cert.crt",
  71. "keyFile": "/etc/ssl/key.key"
  72. }]
  73. }
  74. }`,
  75. wantCertFile: true,
  76. wantKeyFile: true,
  77. },
  78. {
  79. name: "inline content only — unchanged",
  80. input: `{
  81. "tlsSettings": {
  82. "certificates": [{
  83. "certificate": ["-----BEGIN CERTIFICATE-----"],
  84. "key": ["-----BEGIN PRIVATE KEY-----"]
  85. }]
  86. }
  87. }`,
  88. wantCertFile: false,
  89. wantKeyFile: false,
  90. },
  91. {
  92. name: "both file paths and inline content — file paths stripped (redundant)",
  93. input: `{
  94. "tlsSettings": {
  95. "certificates": [{
  96. "certificateFile": "/etc/ssl/cert.crt",
  97. "keyFile": "/etc/ssl/key.key",
  98. "certificate": ["-----BEGIN CERTIFICATE-----"],
  99. "key": ["-----BEGIN PRIVATE KEY-----"]
  100. }]
  101. }
  102. }`,
  103. wantCertFile: false,
  104. wantKeyFile: false,
  105. },
  106. {
  107. name: "empty stream settings",
  108. input: "",
  109. // empty input returns empty, nothing to check
  110. },
  111. }
  112. for _, tc := range tests {
  113. t.Run(tc.name, func(t *testing.T) {
  114. if tc.input == "" {
  115. if got := sanitizeStreamSettingsForRemote(tc.input); got != "" {
  116. t.Errorf("expected empty string, got %q", got)
  117. }
  118. return
  119. }
  120. got := sanitizeStreamSettingsForRemote(tc.input)
  121. var out map[string]any
  122. if err := json.Unmarshal([]byte(got), &out); err != nil {
  123. t.Fatalf("output is not valid JSON: %v\noutput: %s", err, got)
  124. }
  125. tls, _ := out["tlsSettings"].(map[string]any)
  126. certs, _ := tls["certificates"].([]any)
  127. if len(certs) == 0 {
  128. t.Fatal("certificates array missing in output")
  129. }
  130. cert, _ := certs[0].(map[string]any)
  131. _, hasCertFile := cert["certificateFile"]
  132. _, hasKeyFile := cert["keyFile"]
  133. if hasCertFile != tc.wantCertFile {
  134. t.Errorf("certificateFile present=%v, want %v", hasCertFile, tc.wantCertFile)
  135. }
  136. if hasKeyFile != tc.wantKeyFile {
  137. t.Errorf("keyFile present=%v, want %v", hasKeyFile, tc.wantKeyFile)
  138. }
  139. })
  140. }
  141. }