package core import ( "io" "net/http" "net/http/httptest" "path/filepath" "strings" "testing" "vpnclient/internal/config" ) func newTestManager(t *testing.T) *Manager { t.Helper() dir := t.TempDir() cfgPath := filepath.Join(dir, "configs", "config.json") cfg := config.Example() if err := config.Save(cfgPath, cfg); err != nil { t.Fatal(err) } loaded, err := config.Load(cfgPath) if err != nil { t.Fatal(err) } m, err := NewManager(cfgPath, loaded, io.Discard) if err != nil { t.Fatal(err) } return m } const testSubBody = "vless://7e1c0fbf-1758-4781-97f6-ae8715ed3f60@de.example.com:443?encryption=none&type=tcp&security=tls&sni=de.example.com#DE-1\n" + "hysteria2://pass@nl.example.com:9000/?sni=nl.example.com#NL-1\n" // A credential-less https URL pasted into the "add server link" field must be // treated as a subscription URL and imported — never fail with // "proxy URI missing username" (regression for the naive proxy-URI parser). func TestSaveProfileRoutesSubscriptionURL(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = io.WriteString(w, testSubBody) })) defer ts.Close() m := newTestManager(t) if err := m.SaveProfile("whatever", ts.URL+"/ZKszcVcC3xbWb8qj"); err != nil { t.Fatalf("SaveProfile(subscription URL) failed: %v", err) } names := map[string]bool{} for _, p := range m.Profiles() { names[p.Name] = true } if !names["DE-1"] || !names["NL-1"] { t.Fatalf("subscription profiles not imported, got %v", names) } if m.Config().SubscriptionURL == "" { t.Fatal("subscription URL not persisted") } } func TestSaveActiveProfileRoutesSubscriptionURL(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = io.WriteString(w, testSubBody) })) defer ts.Close() m := newTestManager(t) if err := m.SaveActiveProfile("naive-main", ts.URL+"/short"); err != nil { t.Fatalf("SaveActiveProfile(subscription URL) failed: %v", err) } if len(m.Profiles()) < 3 { // naive-main + 2 imported t.Fatalf("expected imported profiles, got %d", len(m.Profiles())) } } // A real naive share link (with credentials) must still be saved as a proxy. func TestSaveProfileKeepsNaiveLink(t *testing.T) { m := newTestManager(t) if err := m.SaveProfile("naive-1", "https://user:pass@proxy.example.com:443"); err != nil { t.Fatal(err) } p, err := m.Config().ActiveProfile() if err != nil { t.Fatal(err) } if p.Protocol != config.ProtocolNaive || !strings.Contains(p.Proxy, "user:pass@") { t.Fatalf("naive profile broken: %+v", p) } }