package render import ( "encoding/json" "strings" "testing" "gopkg.in/yaml.v3" "github.com/peterqiu0516/sub-store/internal/model" ) func makeTestNodes() []model.ProxyNode { return []model.ProxyNode{ { "type": "ss", "name": "TestSS", "server": "1.2.3.4", "port": 8388, "cipher": "aes-256-gcm", "password": "pass123", }, { "type": "vmess", "name": "TestVMess", "server": "5.6.7.8", "port": 443, "uuid": "test-uuid", "alterId": 0, "cipher": "auto", "network": "ws", "tls": true, "ws-opts": map[string]any{ "path": "/path", "headers": map[string]any{"Host": "example.com"}, }, }, { "type": "trojan", "name": "TestTrojan", "server": "9.10.11.12", "port": 443, "password": "trojan-pass", "sni": "example.com", }, } } func TestRenderMihomo(t *testing.T) { nodes := makeTestNodes() output, err := RenderTarget(nodes, "mihomo", nil) if err != nil { t.Fatalf("render mihomo failed: %v", err) } // Should be valid YAML var parsed map[string]any if err := yaml.Unmarshal([]byte(output), &parsed); err != nil { t.Fatalf("output is not valid YAML: %v", err) } proxies, ok := parsed["proxies"].([]any) if !ok { t.Fatal("expected proxies array in YAML") } if len(proxies) != 3 { t.Errorf("expected 3 proxies, got %d", len(proxies)) } } func TestRenderJSON(t *testing.T) { nodes := makeTestNodes() output, err := RenderTarget(nodes, "json", nil) if err != nil { t.Fatalf("render json failed: %v", err) } var parsed map[string]any if err := json.Unmarshal([]byte(output), &parsed); err != nil { t.Fatalf("output is not valid JSON: %v", err) } proxies, ok := parsed["proxies"].([]any) if !ok { t.Fatal("expected proxies array in JSON output") } if len(proxies) != 3 { t.Errorf("expected 3 nodes, got %d", len(proxies)) } } func TestRenderURI(t *testing.T) { nodes := makeTestNodes() output, err := RenderTarget(nodes, "uri", nil) if err != nil { t.Fatalf("render uri failed: %v", err) } lines := strings.Split(strings.TrimSpace(output), "\n") if len(lines) != 3 { t.Errorf("expected 3 lines, got %d", len(lines)) } // Each line should start with a protocol for _, line := range lines { line = strings.TrimSpace(line) if !strings.Contains(line, "://") { t.Errorf("expected URI format, got: %s", line) } } } func TestRenderSurge(t *testing.T) { nodes := makeTestNodes()[:2] // ss + vmess output, err := RenderTarget(nodes, "surge", nil) if err != nil { t.Fatalf("render surge failed: %v", err) } if !strings.Contains(output, "TestSS") { t.Error("expected TestSS in surge output") } } func TestRenderSingBox(t *testing.T) { nodes := makeTestNodes()[:1] output, err := RenderTarget(nodes, "sing-box", nil) if err != nil { t.Fatalf("render sing-box failed: %v", err) } var parsed map[string]any if err := json.Unmarshal([]byte(output), &parsed); err != nil { t.Fatalf("sing-box output is not valid JSON: %v", err) } // Per review-resolution #42: should have log, inbounds, outbounds, route if _, ok := parsed["outbounds"]; !ok { t.Error("expected outbounds in sing-box output") } } func TestRenderEmpty(t *testing.T) { nodes := []model.ProxyNode{} _, err := RenderTarget(nodes, "json", nil) if err == nil { // Some renderers may handle empty gracefully } } func TestRenderUnsupportedTarget(t *testing.T) { nodes := makeTestNodes() // Unsupported targets fall back to mihomo per dispatch default output, err := RenderTarget(nodes, "unsupported-target", nil) if err != nil { // Error is acceptable for unsupported targets return } // If no error, output should be valid YAML if output == "" { t.Error("expected non-empty output") } } func TestRenderMihomoWithTemplate(t *testing.T) { nodes := makeTestNodes() tmplConfig := map[string]any{ "proxy-groups": []any{ map[string]any{ "name": "PROXY", "type": "select", "proxies": []any{"TestSS", "TestVMess", "TestTrojan"}, }, }, } output, err := RenderBuildTarget(nodes, "mihomo", "", tmplConfig) if err != nil { t.Fatalf("render mihomo with template failed: %v", err) } var parsed map[string]any if err := yaml.Unmarshal([]byte(output), &parsed); err != nil { t.Fatalf("output is not valid YAML: %v", err) } groups, ok := parsed["proxy-groups"].([]any) if !ok { t.Fatal("expected proxy-groups in YAML") } if len(groups) != 1 { t.Errorf("expected 1 proxy group, got %d", len(groups)) } }