Files
ss/internal/subscription/uri_parse_test.go
2026-07-19 20:01:38 +08:00

179 lines
5.4 KiB
Go

package subscription
import (
"encoding/base64"
"reflect"
"testing"
"gopkg.in/yaml.v3"
)
func TestParseSS_PlainUserinfo(t *testing.T) {
uri := "ss://aes-256-gcm:password123@example.com:8388#My%20Node"
got := parseSS(uri)
want := map[string]interface{}{
"name": "My Node",
"type": "ss",
"server": "example.com",
"port": 8388,
"cipher": "aes-256-gcm",
"password": "password123",
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("parseSS() = %#v, want %#v", got, want)
}
}
func TestParseSS_Base64Userinfo(t *testing.T) {
userinfo := base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString([]byte("aes-256-gcm:password123"))
uri := "ss://" + userinfo + "@example.com:8388#node"
got := parseSS(uri)
want := map[string]interface{}{
"name": "node",
"type": "ss",
"server": "example.com",
"port": 8388,
"cipher": "aes-256-gcm",
"password": "password123",
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("parseSS() = %#v, want %#v", got, want)
}
}
func TestParseSS_WholeBodyBase64(t *testing.T) {
body := base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString([]byte("aes-256-gcm:password123@example.com:8388"))
uri := "ss://" + body + "#node"
got := parseSS(uri)
want := map[string]interface{}{
"name": "node",
"type": "ss",
"server": "example.com",
"port": 8388,
"cipher": "aes-256-gcm",
"password": "password123",
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("parseSS() = %#v, want %#v", got, want)
}
}
func TestParseSS_NotSS(t *testing.T) {
if got := parseSS("trojan://foo@bar:443"); got != nil {
t.Fatalf("parseSS() = %#v, want nil", got)
}
}
func TestParseTrojan_Basic(t *testing.T) {
uri := "trojan://mypassword@example.com:443?sni=sni.example.com&allowInsecure=1#My%20Trojan"
got := parseTrojan(uri)
want := map[string]interface{}{
"name": "My Trojan",
"type": "trojan",
"server": "example.com",
"port": 443,
"password": "mypassword",
"udp": true,
"sni": "sni.example.com",
"skip-cert-verify": true,
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("parseTrojan() = %#v, want %#v", got, want)
}
}
func TestParseTrojan_PeerFallback(t *testing.T) {
uri := "trojan://mypassword@example.com:443?peer=peer.example.com"
got := parseTrojan(uri)
if got["sni"] != "peer.example.com" {
t.Fatalf("expected sni from peer fallback, got %#v", got)
}
}
func TestParseVless_TLS(t *testing.T) {
uri := "vless://uuid-1234@example.com:443?security=tls&sni=sni.example.com&fp=chrome&type=ws#node"
got := parseVless(uri)
want := map[string]interface{}{
"name": "node",
"type": "vless",
"server": "example.com",
"port": 443,
"uuid": "uuid-1234",
"udp": true,
"tls": true,
"network": "ws",
"servername": "sni.example.com",
"client-fingerprint": "chrome",
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("parseVless() = %#v, want %#v", got, want)
}
}
func TestParseVless_Reality(t *testing.T) {
uri := "vless://uuid-1234@example.com:443?security=reality&pbk=publickey&sid=shortid&sni=sni.example.com"
got := parseVless(uri)
realityOpts, ok := got["reality-opts"].(map[string]interface{})
if !ok {
t.Fatalf("expected reality-opts map, got %#v", got["reality-opts"])
}
if realityOpts["public-key"] != "publickey" || realityOpts["short-id"] != "shortid" {
t.Fatalf("unexpected reality-opts: %#v", realityOpts)
}
if got["servername"] != "sni.example.com" {
t.Fatalf("expected servername set, got %#v", got["servername"])
}
if got["tls"] != true {
t.Fatalf("expected tls=true for reality, got %#v", got["tls"])
}
}
func TestParseVless_DefaultsWithoutQuery(t *testing.T) {
uri := "vless://uuid-1234@example.com:443"
got := parseVless(uri)
if got["tls"] != false || got["network"] != "tcp" || got["udp"] != true {
t.Fatalf("unexpected defaults: %#v", got)
}
}
func TestConvertContent_PassthroughExistingClashYAML(t *testing.T) {
content := "proxies:\n - name: foo\n type: ss\n"
if got := convertContent(content); got != content {
t.Fatalf("expected passthrough, got %q", got)
}
}
func TestConvertContent_PlainURIList(t *testing.T) {
content := "ss://aes-256-gcm:pw@example.com:8388#node1\ntrojan://pw2@example.com:443#node2\n"
out := convertContent(content)
var parsed map[string]interface{}
if err := yaml.Unmarshal([]byte(out), &parsed); err != nil {
t.Fatalf("output is not valid YAML: %v", err)
}
proxies, ok := parsed["proxies"].([]interface{})
if !ok || len(proxies) != 2 {
t.Fatalf("expected 2 proxies, got %#v", parsed["proxies"])
}
}
func TestConvertContent_WholeBodyBase64List(t *testing.T) {
raw := "ss://aes-256-gcm:pw@example.com:8388#node1\ntrojan://pw2@example.com:443#node2\n"
encoded := base64.StdEncoding.EncodeToString([]byte(raw))
out := convertContent(encoded)
var parsed map[string]interface{}
if err := yaml.Unmarshal([]byte(out), &parsed); err != nil {
t.Fatalf("output is not valid YAML: %v", err)
}
proxies, ok := parsed["proxies"].([]interface{})
if !ok || len(proxies) != 2 {
t.Fatalf("expected 2 proxies, got %#v", parsed["proxies"])
}
}
func TestConvertContent_UnrecognizedPassthrough(t *testing.T) {
content := "not a uri list and not yaml: [unterminated"
if got := convertContent(content); got != content {
t.Fatalf("expected passthrough of unrecognized content, got %q", got)
}
}