Add SwiftUI + Network Extension (LibXray/hev), parse Clash Meta YAML from subscription URLs into vless/trojan/hy2 share links, keep the selected server on connect, and ship Navis-3.8.2.13.ipa with versioned changelog. Co-authored-by: Cursor <cursoragent@cursor.com>
145 lines
3.5 KiB
Go
145 lines
3.5 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|