package subscription import ( "encoding/base64" "os" "path/filepath" "strings" "testing" ) func TestParseBodyPlain(t *testing.T) { body := `# comment hysteria2://pass@ex.com:443/?sni=ex.com&obfs=salamander&obfs-password=x#EU naive+https://u:p@n.example.com#naive1 ` items, err := ParseBody(body) if err != nil { t.Fatal(err) } if len(items) != 2 { t.Fatalf("got %d items", len(items)) } } func TestParseBodyBase64(t *testing.T) { plain := "hysteria2://secret@host.example:443/?sni=host.example#HY\nhttps://user:pass@naive.example.com#NV\n" body := base64.StdEncoding.EncodeToString([]byte(plain)) items, err := ParseBody(body) if err != nil { t.Fatal(err) } if len(items) != 2 { t.Fatalf("got %d items: %+v", len(items), items) } } func TestParseBodyClashYAML(t *testing.T) { body := ` proxies: - name: "DE HY2" type: hysteria2 server: de.example.com port: 443 password: secret sni: de.example.com obfs: salamander obfs-password: obfspass - name: CZ Vless type: vless server: cz.example.com port: 443 uuid: 7e1c0fbf-1758-4781-97f6-ae8715ed3f60 network: xhttp tls: true servername: cz.example.com alpn: - h2 - http/1.1 xhttp-opts: path: /xhttp host: cz.example.com mode: auto client-fingerprint: chrome - name: CZ Trojan type: trojan server: cz.example.com port: 20000 password: secretpass network: ws tls: true sni: cz.example.com ws-opts: path: /ws proxy-groups: - name: Auto icon: https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Proxy.png type: select ` items, err := ParseBody(body) if err != nil { t.Fatal(err) } if len(items) != 3 { t.Fatalf("got %d items: %+v", len(items), items) } byProto := map[string]int{} var vlessURI string for _, it := range items { byProto[string(it.Protocol)]++ if it.Protocol == "naive" { t.Fatalf("unexpected naive: %s %s", it.Name, it.URI) } if it.Protocol == "vless" { vlessURI = it.URI } } if byProto["hysteria2"] != 1 || byProto["vless"] != 1 || byProto["trojan"] != 1 { t.Fatalf("protos %+v", byProto) } if !strings.Contains(items[0].URI+items[1].URI+items[2].URI, "de.example.com:443") { t.Fatalf("missing hy2 host in %+v", items) } if !strings.Contains(vlessURI, "alpn=h2") { t.Fatalf("alpn list broken in %s", vlessURI) } if strings.Contains(vlessURI, "alpn=-") { t.Fatalf("alpn bled list marker in %s", vlessURI) } if !strings.Contains(vlessURI, "mode=auto") { t.Fatalf("xhttp mode missing in %s", vlessURI) } } func TestNaiveAuthIgnoresPathAt(t *testing.T) { if naiveLinkHasAuth("https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Proxy.png") { t.Fatal("path @master must not count as auth") } if !naiveLinkHasAuth("https://user:pass@naive.example.com") { t.Fatal("real auth required") } } func TestParseBodyRealClashSubscriptionFile(t *testing.T) { path := filepath.Join("testdata", "evilfox_clash.yaml") raw, err := os.ReadFile(path) if err != nil { t.Skip("optional fixture missing:", path) } items, err := ParseBody(string(raw)) if err != nil { t.Fatal(err) } if len(items) < 80 { t.Fatalf("expected ~89 proxies, got %d", len(items)) } for _, it := range items { if it.Protocol == "naive" { t.Fatalf("naive noise: %s %s", it.Name, it.URI) } if strings.EqualFold(it.Name, "master") || strings.HasPrefix(strings.ToLower(it.Name), "master-") { t.Fatalf("icon false-positive name: %s", it.Name) } } }