package runtime import ( "encoding/json" "testing" "github.com/mhsanaei/3x-ui/v3/database/model" ) // cacheGetTag must resolve a remote inbound id even when the n- prefix // sits on only one side: the node may store the bare tag while the central // panel pushes the prefixed form, or vice versa. Without this a mismatch makes // the push create a duplicate inbound on the node. func TestCacheGetTag_PrefixAgnostic(t *testing.T) { cases := []struct { name string cacheTag string lookup string wantID int wantFound bool }{ {"exact", "n1-in-443-tcp", "n1-in-443-tcp", 7, true}, {"node bare, lookup prefixed", "in-443-tcp", "n1-in-443-tcp", 7, true}, {"node prefixed, lookup bare", "n1-in-443-tcp", "in-443-tcp", 7, true}, {"unrelated tag", "in-443-tcp", "in-999-tcp", 0, false}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { r := NewRemote(&model.Node{Id: 1, Name: "n1"}) r.cacheSet(c.cacheTag, 7) id, ok := r.cacheGetTag(c.lookup) if ok != c.wantFound || id != c.wantID { t.Fatalf("cacheGetTag(%q) = (%d, %v), want (%d, %v)", c.lookup, id, ok, c.wantID, c.wantFound) } }) } } func TestSanitizeStreamSettingsForRemote(t *testing.T) { tests := []struct { name string input string // wantCertFile / wantKeyFile: expected presence after sanitize wantCertFile bool wantKeyFile bool }{ { name: "file paths only — kept intact (remote node paths)", input: `{ "tlsSettings": { "certificates": [{ "certificateFile": "/etc/ssl/cert.crt", "keyFile": "/etc/ssl/key.key" }] } }`, wantCertFile: true, wantKeyFile: true, }, { name: "inline content only — unchanged", input: `{ "tlsSettings": { "certificates": [{ "certificate": ["-----BEGIN CERTIFICATE-----"], "key": ["-----BEGIN PRIVATE KEY-----"] }] } }`, wantCertFile: false, wantKeyFile: false, }, { name: "both file paths and inline content — file paths stripped (redundant)", input: `{ "tlsSettings": { "certificates": [{ "certificateFile": "/etc/ssl/cert.crt", "keyFile": "/etc/ssl/key.key", "certificate": ["-----BEGIN CERTIFICATE-----"], "key": ["-----BEGIN PRIVATE KEY-----"] }] } }`, wantCertFile: false, wantKeyFile: false, }, { name: "empty stream settings", input: "", // empty input returns empty, nothing to check }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if tc.input == "" { if got := sanitizeStreamSettingsForRemote(tc.input); got != "" { t.Errorf("expected empty string, got %q", got) } return } got := sanitizeStreamSettingsForRemote(tc.input) var out map[string]any if err := json.Unmarshal([]byte(got), &out); err != nil { t.Fatalf("output is not valid JSON: %v\noutput: %s", err, got) } tls, _ := out["tlsSettings"].(map[string]any) certs, _ := tls["certificates"].([]any) if len(certs) == 0 { t.Fatal("certificates array missing in output") } cert, _ := certs[0].(map[string]any) _, hasCertFile := cert["certificateFile"] _, hasKeyFile := cert["keyFile"] if hasCertFile != tc.wantCertFile { t.Errorf("certificateFile present=%v, want %v", hasCertFile, tc.wantCertFile) } if hasKeyFile != tc.wantKeyFile { t.Errorf("keyFile present=%v, want %v", hasKeyFile, tc.wantKeyFile) } }) } }