293 lines
8.5 KiB
Go
293 lines
8.5 KiB
Go
package proxy
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/peterqiu0516/sub-store/internal/model"
|
|
)
|
|
|
|
func TestParseSS(t *testing.T) {
|
|
plain := "aes-256-gcm:password123@1.2.3.4:8388"
|
|
encoded := base64.RawURLEncoding.EncodeToString([]byte(plain))
|
|
uri := "ss://" + encoded + "#TestSS"
|
|
nodes := ParseProxies(uri)
|
|
if len(nodes) != 1 {
|
|
t.Fatalf("expected 1 node, got %d", len(nodes))
|
|
}
|
|
n := nodes[0]
|
|
if n["type"] != "ss" {
|
|
t.Errorf("expected type ss, got %v", n["type"])
|
|
}
|
|
if n["server"] != "1.2.3.4" {
|
|
t.Errorf("expected server 1.2.3.4, got %v", n["server"])
|
|
}
|
|
if n["name"] != "TestSS" {
|
|
t.Errorf("expected name TestSS, got %v", n["name"])
|
|
}
|
|
if n["cipher"] != "aes-256-gcm" {
|
|
t.Errorf("expected cipher aes-256-gcm, got %v", n["cipher"])
|
|
}
|
|
if n["password"] != "password123" {
|
|
t.Errorf("expected password password123, got %v", n["password"])
|
|
}
|
|
port, _ := n["port"].(float64)
|
|
if port != 8388 {
|
|
t.Errorf("expected port 8388, got %v", n["port"])
|
|
}
|
|
}
|
|
|
|
func TestParseSSURLForm(t *testing.T) {
|
|
uri := "ss://aes-256-gcm:password123@1.2.3.4:8388#TestSS"
|
|
nodes := ParseProxies(uri)
|
|
if len(nodes) != 1 {
|
|
t.Fatalf("expected 1 node, got %d", len(nodes))
|
|
}
|
|
n := nodes[0]
|
|
if n["type"] != "ss" {
|
|
t.Errorf("expected type ss, got %v", n["type"])
|
|
}
|
|
if n["cipher"] != "aes-256-gcm" {
|
|
t.Errorf("expected cipher aes-256-gcm, got %v", n["cipher"])
|
|
}
|
|
}
|
|
|
|
func TestParseSSR(t *testing.T) {
|
|
// SSR format: base64(server:port:protocol:method:obfs:base64(password)/?remarks=base64(name))
|
|
ssrMain := "1.2.3.4:8388:auth_aes128_sha1:aes-256-cfb:http_simple:" + base64.RawURLEncoding.EncodeToString([]byte("pass"))
|
|
encoded := base64.RawURLEncoding.EncodeToString([]byte(ssrMain + "/?remarks=" + base64.RawURLEncoding.EncodeToString([]byte("TestSSR"))))
|
|
uri := "ssr://" + encoded
|
|
nodes := ParseProxies(uri)
|
|
if len(nodes) != 1 {
|
|
t.Fatalf("expected 1 node, got %d", len(nodes))
|
|
}
|
|
n := nodes[0]
|
|
if n["type"] != "ssr" {
|
|
t.Errorf("expected type ssr, got %v", n["type"])
|
|
}
|
|
if n["server"] != "1.2.3.4" {
|
|
t.Errorf("expected server 1.2.3.4, got %v", n["server"])
|
|
}
|
|
if n["obfs"] != "http_simple" {
|
|
t.Errorf("expected obfs http_simple, got %v", n["obfs"])
|
|
}
|
|
}
|
|
|
|
func TestParseVMess(t *testing.T) {
|
|
vmJSON := `{"v":"2","ps":"TestVM","add":"1.2.3.4","port":"443","id":"uuid-1234","aid":"0","scy":"auto","net":"ws","type":"none","host":"example.com","path":"/path","tls":"tls","sni":"example.com"}`
|
|
encoded := base64.RawURLEncoding.EncodeToString([]byte(vmJSON))
|
|
uri := "vmess://" + encoded
|
|
nodes := ParseProxies(uri)
|
|
if len(nodes) != 1 {
|
|
t.Fatalf("expected 1 node, got %d", len(nodes))
|
|
}
|
|
n := nodes[0]
|
|
if n["type"] != "vmess" {
|
|
t.Errorf("expected type vmess, got %v", n["type"])
|
|
}
|
|
if n["server"] != "1.2.3.4" {
|
|
t.Errorf("expected server 1.2.3.4, got %v", n["server"])
|
|
}
|
|
if n["uuid"] != "uuid-1234" {
|
|
t.Errorf("expected uuid uuid-1234, got %v", n["uuid"])
|
|
}
|
|
if n["network"] != "ws" {
|
|
t.Errorf("expected network ws, got %v", n["network"])
|
|
}
|
|
ws, ok := n["ws-opts"].(map[string]any)
|
|
if !ok {
|
|
t.Fatalf("expected ws-opts map, got %T", n["ws-opts"])
|
|
}
|
|
if ws["path"] != "/path" {
|
|
t.Errorf("expected ws path /path, got %v", ws["path"])
|
|
}
|
|
if n["tls"] != true {
|
|
t.Errorf("expected tls true, got %v", n["tls"])
|
|
}
|
|
}
|
|
|
|
func TestParseVLESS(t *testing.T) {
|
|
uri := "vless://uuid-1234@1.2.3.4:443?encryption=none&security=tls&type=ws&host=example.com&path=%2Fpath&sni=example.com#TestVLESS"
|
|
nodes := ParseProxies(uri)
|
|
if len(nodes) != 1 {
|
|
t.Fatalf("expected 1 node, got %d", len(nodes))
|
|
}
|
|
n := nodes[0]
|
|
if n["type"] != "vless" {
|
|
t.Errorf("expected type vless, got %v", n["type"])
|
|
}
|
|
if n["uuid"] != "uuid-1234" {
|
|
t.Errorf("expected uuid uuid-1234, got %v", n["uuid"])
|
|
}
|
|
if n["network"] != "ws" {
|
|
t.Errorf("expected network ws, got %v", n["network"])
|
|
}
|
|
ws, ok := n["ws-opts"].(map[string]any)
|
|
if !ok {
|
|
t.Fatalf("expected ws-opts map, got %T", n["ws-opts"])
|
|
}
|
|
if ws["path"] != "/path" {
|
|
t.Errorf("expected ws path /path, got %v", ws["path"])
|
|
}
|
|
if n["tls"] != true {
|
|
t.Errorf("expected tls true, got %v", n["tls"])
|
|
}
|
|
}
|
|
|
|
func TestParseTrojan(t *testing.T) {
|
|
uri := "trojan://password123@1.2.3.4:443?sni=example.com&type=ws&host=example.com&path=%2Fpath#TestTrojan"
|
|
nodes := ParseProxies(uri)
|
|
if len(nodes) != 1 {
|
|
t.Fatalf("expected 1 node, got %d", len(nodes))
|
|
}
|
|
n := nodes[0]
|
|
if n["type"] != "trojan" {
|
|
t.Errorf("expected type trojan, got %v", n["type"])
|
|
}
|
|
if n["password"] != "password123" {
|
|
t.Errorf("expected password password123, got %v", n["password"])
|
|
}
|
|
if n["sni"] != "example.com" {
|
|
t.Errorf("expected sni example.com, got %v", n["sni"])
|
|
}
|
|
}
|
|
|
|
func TestParseHysteria2(t *testing.T) {
|
|
uri := "hysteria2://password123@1.2.3.4:443?sni=example.com&insecure=1#TestHys2"
|
|
nodes := ParseProxies(uri)
|
|
if len(nodes) != 1 {
|
|
t.Fatalf("expected 1 node, got %d", len(nodes))
|
|
}
|
|
n := nodes[0]
|
|
if n["type"] != "hysteria2" {
|
|
t.Errorf("expected type hysteria2, got %v", n["type"])
|
|
}
|
|
if n["password"] != "password123" {
|
|
t.Errorf("expected password password123, got %v", n["password"])
|
|
}
|
|
}
|
|
|
|
func TestParseMultipleLines(t *testing.T) {
|
|
lines := []string{
|
|
"ss://aes-256-gcm:pass@1.2.3.4:8388#Node1",
|
|
"ss://aes-256-gcm:pass@5.6.7.8:8388#Node2",
|
|
"ss://aes-256-gcm:pass@9.10.11.12:8388#Node3",
|
|
}
|
|
nodes := ParseProxies(strings.Join(lines, "\n"))
|
|
if len(nodes) != 3 {
|
|
t.Fatalf("expected 3 nodes, got %d", len(nodes))
|
|
}
|
|
if nodes[0]["name"] != "Node1" {
|
|
t.Errorf("expected name Node1, got %v", nodes[0]["name"])
|
|
}
|
|
if nodes[2]["name"] != "Node3" {
|
|
t.Errorf("expected name Node3, got %v", nodes[2]["name"])
|
|
}
|
|
}
|
|
|
|
func TestParseBase64Content(t *testing.T) {
|
|
lines := []string{
|
|
"ss://aes-256-gcm:pass@1.2.3.4:8388#Node1",
|
|
"ss://aes-256-gcm:pass@5.6.7.8:8388#Node2",
|
|
}
|
|
plain := strings.Join(lines, "\n")
|
|
encoded := base64.StdEncoding.EncodeToString([]byte(plain))
|
|
// DecodeMaybeBase64 should detect and decode base64 content
|
|
decoded := DecodeMaybeBase64(encoded)
|
|
nodes := ParseProxies(decoded)
|
|
if len(nodes) != 2 {
|
|
t.Fatalf("expected 2 nodes from base64 content, got %d", len(nodes))
|
|
}
|
|
}
|
|
|
|
func TestParseEmptyContent(t *testing.T) {
|
|
nodes := ParseProxies("")
|
|
if len(nodes) != 0 {
|
|
t.Errorf("expected 0 nodes for empty content, got %d", len(nodes))
|
|
}
|
|
}
|
|
|
|
func TestParseInvalidContent(t *testing.T) {
|
|
nodes := ParseProxies("this is not a valid proxy URI")
|
|
if len(nodes) != 0 {
|
|
t.Errorf("expected 0 nodes for invalid content, got %d", len(nodes))
|
|
}
|
|
}
|
|
|
|
func TestParseJSONContent(t *testing.T) {
|
|
jsonContent := `[{"type":"ss","server":"1.2.3.4","port":8388,"cipher":"aes-256-gcm","password":"pass","name":"TestJSON"}]`
|
|
nodes := ParseProxies(jsonContent)
|
|
if len(nodes) != 1 {
|
|
t.Fatalf("expected 1 node from JSON, got %d", len(nodes))
|
|
}
|
|
if nodes[0]["type"] != "ss" {
|
|
t.Errorf("expected type ss, got %v", nodes[0]["type"])
|
|
}
|
|
}
|
|
|
|
func TestEnsureUniqueProxyNames(t *testing.T) {
|
|
nodes := []model.ProxyNode{
|
|
{"name": "Same", "server": "1.1.1.1"},
|
|
{"name": "Same", "server": "2.2.2.2"},
|
|
{"name": "Same", "server": "3.3.3.3"},
|
|
{"name": "Unique", "server": "4.4.4.4"},
|
|
}
|
|
result := EnsureUniqueProxyNames(nodes)
|
|
if result[0]["name"] != "Same" {
|
|
t.Errorf("first name should be unchanged, got %v", result[0]["name"])
|
|
}
|
|
if result[1]["name"] == "Same" {
|
|
t.Error("second duplicate should be renamed")
|
|
}
|
|
if result[3]["name"] != "Unique" {
|
|
t.Errorf("unique name should be unchanged, got %v", result[3]["name"])
|
|
}
|
|
}
|
|
|
|
func TestAddPreviewIds(t *testing.T) {
|
|
nodes := []model.ProxyNode{
|
|
{"name": "A", "server": "1.1.1.1"},
|
|
{"name": "B", "server": "2.2.2.2"},
|
|
}
|
|
result := AddPreviewIds(nodes)
|
|
if _, ok := result[0]["id"]; !ok {
|
|
t.Error("expected id field on first node")
|
|
}
|
|
if _, ok := result[1]["id"]; !ok {
|
|
t.Error("expected id field on second node")
|
|
}
|
|
}
|
|
|
|
func TestDecodeMaybeBase64(t *testing.T) {
|
|
plain := "ss://pass@host:port#name"
|
|
if DecodeMaybeBase64(plain) != plain {
|
|
t.Error("plain text should pass through")
|
|
}
|
|
encoded := base64.StdEncoding.EncodeToString([]byte(plain))
|
|
decoded := DecodeMaybeBase64(encoded)
|
|
if !strings.Contains(decoded, "ss://") {
|
|
t.Errorf("base64 should be decoded, got: %s", decoded)
|
|
}
|
|
}
|
|
|
|
func TestNodeJSONSerialization(t *testing.T) {
|
|
uri := "ss://aes-256-gcm:pass@1.2.3.4:8388#Test"
|
|
nodes := ParseProxies(uri)
|
|
if len(nodes) == 0 {
|
|
t.Fatal("no nodes parsed")
|
|
}
|
|
data, err := json.Marshal(nodes[0])
|
|
if err != nil {
|
|
t.Fatalf("failed to marshal node: %v", err)
|
|
}
|
|
var back map[string]any
|
|
if err := json.Unmarshal(data, &back); err != nil {
|
|
t.Fatalf("failed to unmarshal node: %v", err)
|
|
}
|
|
if back["type"] != "ss" {
|
|
t.Errorf("expected type ss after round-trip, got %v", back["type"])
|
|
}
|
|
}
|