package control import ( "os" "path/filepath" "strings" "testing" ) func TestRenderRuntimeBundle(t *testing.T) { t.Parallel() dir := t.TempDir() node := Node{ ID: "nl-01", Name: "NL 01", Region: "nl", Host: "203.0.113.10", Domain: "nl-01.example.com", ACMEEmail: "admin@example.com", Enabled: true, SSH: SSHConfig{ User: "root", Port: 22, Auth: "key", }, Protocols: []ProtocolProfile{ { Type: "vless", Enabled: true, Port: 443, TLS: &TLSProfile{ Enabled: true, ServerName: "nl-01.example.com", }, Auth: &AuthProfile{ UUID: "11111111-1111-1111-1111-111111111111", }, Extra: map[string]any{ "path": "/ws", }, }, }, } if err := RenderRuntimeBundle(dir, node, "20260401-123000"); err != nil { t.Fatalf("RenderRuntimeBundle error = %v", err) } data, err := os.ReadFile(filepath.Join(dir, "docker-compose.yml")) if err != nil { t.Fatalf("ReadFile docker-compose.yml error = %v", err) } if !strings.Contains(string(data), "sing-box:") { t.Fatal("expected sing-box service in runtime compose") } if !strings.Contains(string(data), "caddy:") { t.Fatal("expected caddy service in runtime compose") } caddyfile, err := os.ReadFile(filepath.Join(dir, "Caddyfile")) if err != nil { t.Fatalf("ReadFile Caddyfile error = %v", err) } if !strings.Contains(string(caddyfile), "nl-01.example.com") { t.Fatal("expected domain in Caddyfile") } serverConfig, err := os.ReadFile(filepath.Join(dir, "sing-box.server.json")) if err != nil { t.Fatalf("ReadFile sing-box.server.json error = %v", err) } if !strings.Contains(string(serverConfig), "\"type\": \"vless\"") { t.Fatal("expected vless inbound in sing-box config") } } func TestRenderRuntimeBundleReality(t *testing.T) { t.Parallel() dir := t.TempDir() node := Node{ ID: "nl-reality", Name: "NL Reality", Region: "nl", Host: "203.0.113.20", Enabled: true, SSH: SSHConfig{ User: "root", Port: 22, Auth: "key", }, Protocols: []ProtocolProfile{ { Type: "vless-reality", Enabled: true, Port: 443, Auth: &AuthProfile{ UUID: "33333333-3333-3333-3333-333333333333", }, Reality: &VLESSRealityProfile{ ServerName: "login.microsoftonline.com", ServerPort: 443, PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc", PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0", ShortID: "0123456789abcdef", Fingerprint: "chrome", }, }, }, } if err := RenderRuntimeBundle(dir, node, "20260408-180000"); err != nil { t.Fatalf("RenderRuntimeBundle error = %v", err) } data, err := os.ReadFile(filepath.Join(dir, "docker-compose.yml")) if err != nil { t.Fatalf("ReadFile docker-compose.yml error = %v", err) } if !strings.Contains(string(data), "sing-box:") { t.Fatal("expected sing-box service in runtime compose") } if strings.Contains(string(data), "caddy:") { t.Fatal("did not expect caddy service for reality runtime") } if _, err := os.Stat(filepath.Join(dir, "Caddyfile")); !os.IsNotExist(err) { t.Fatal("did not expect Caddyfile for reality runtime") } serverConfig, err := os.ReadFile(filepath.Join(dir, "sing-box.server.json")) if err != nil { t.Fatalf("ReadFile sing-box.server.json error = %v", err) } s := string(serverConfig) if !strings.Contains(s, "\"private_key\": \"UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc\"") { t.Fatal("expected reality private key in sing-box config") } if !strings.Contains(s, "\"short_id\": [") || !strings.Contains(s, "0123456789abcdef") { t.Fatal("expected reality short id in sing-box config") } if !strings.Contains(s, "login.microsoftonline.com") { t.Fatal("expected reality handshake destination in sing-box config") } } func TestHysteria2Bundle(t *testing.T) { t.Parallel() dir := t.TempDir() node := Node{ ID: "nl-hy2", Name: "NL Hysteria2", Region: "nl", Host: "203.0.113.30", Enabled: true, SSH: SSHConfig{ User: "root", Port: 22, Auth: "key", }, Protocols: []ProtocolProfile{ { Type: "hysteria2", Enabled: true, Port: 443, Auth: &AuthProfile{ Password: "user-password", }, Hysteria2: &Hysteria2Profile{ Port: 443, UpMbps: 100, DownMbps: 100, ObfsPassword: "obfs-password", UserPassword: "user-password", CertPath: "/etc/sing-box/cert.pem", KeyPath: "/etc/sing-box/key.pem", }, }, }, } if err := RenderRuntimeBundle(dir, node, "20260408-220000"); err != nil { t.Fatalf("RenderRuntimeBundle error = %v", err) } data, err := os.ReadFile(filepath.Join(dir, "docker-compose.yml")) if err != nil { t.Fatalf("ReadFile docker-compose.yml error = %v", err) } compose := string(data) if !strings.Contains(compose, "443:443/udp") { t.Fatal("expected udp port mapping for hysteria2 runtime") } if !strings.Contains(compose, "127.0.0.1:1080:1080/tcp") { t.Fatal("expected local tcp health port mapping for hysteria2 runtime") } if strings.Contains(compose, "caddy:") { t.Fatal("did not expect caddy service for hysteria2 runtime") } serverConfig, err := os.ReadFile(filepath.Join(dir, "sing-box.server.json")) if err != nil { t.Fatalf("ReadFile sing-box.server.json error = %v", err) } config := string(serverConfig) if !strings.Contains(config, "\"type\": \"hysteria2\"") { t.Fatal("expected hysteria2 inbound in sing-box config") } if !strings.Contains(config, "\"salamander\"") { t.Fatal("expected salamander obfuscation in sing-box config") } if !strings.Contains(config, "\"listen_port\": 1080") { t.Fatal("expected mixed health inbound in sing-box config") } if !strings.Contains(config, "\"certificate_path\": \"/etc/sing-box/cert.pem\"") { t.Fatal("expected embedded certificate path in sing-box config") } if _, err := os.Stat(filepath.Join(dir, "cert.pem")); err != nil { t.Fatalf("expected generated cert.pem: %v", err) } if _, err := os.Stat(filepath.Join(dir, "key.pem")); err != nil { t.Fatalf("expected generated key.pem: %v", err) } } func TestRenderRuntimeBundleMultiProtocol(t *testing.T) { t.Parallel() dir := t.TempDir() node := Node{ ID: "nl-multi", Name: "NL Multi", Region: "nl", Host: "203.0.113.40", Enabled: true, SSH: SSHConfig{ User: "root", Port: 22, Auth: "key", }, Protocols: []ProtocolProfile{ { Type: "vless-reality", Enabled: true, Port: 443, Auth: &AuthProfile{ UUID: "33333333-3333-3333-3333-333333333333", }, Reality: &VLESSRealityProfile{ ServerName: "www.microsoft.com", ServerPort: 443, PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc", PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0", ShortID: "0123456789abcdef", Fingerprint: "chrome", }, }, { Type: "hysteria2", Enabled: true, Port: 443, Auth: &AuthProfile{ Password: "user-password", }, Hysteria2: &Hysteria2Profile{ Port: 443, UpMbps: 100, DownMbps: 100, ObfsPassword: "obfs-password", UserPassword: "user-password", CertPath: "/etc/sing-box/cert.pem", KeyPath: "/etc/sing-box/key.pem", }, }, }, } if err := RenderRuntimeBundle(dir, node, "20260409-120000"); err != nil { t.Fatalf("RenderRuntimeBundle error = %v", err) } data, err := os.ReadFile(filepath.Join(dir, "docker-compose.yml")) if err != nil { t.Fatalf("ReadFile docker-compose.yml error = %v", err) } compose := string(data) if !strings.Contains(compose, "network_mode: host") { t.Fatal("expected host networking for multi protocol runtime") } serverConfig, err := os.ReadFile(filepath.Join(dir, "sing-box.server.json")) if err != nil { t.Fatalf("ReadFile sing-box.server.json error = %v", err) } config := string(serverConfig) if !strings.Contains(config, "\"tag\": \"vless-reality-in\"") { t.Fatal("expected reality inbound in sing-box config") } if !strings.Contains(config, "\"tag\": \"hysteria2-in\"") { t.Fatal("expected hysteria2 inbound in sing-box config") } if !strings.Contains(config, "\"tag\": \"hy2-health-in\"") { t.Fatal("expected hysteria2 health inbound for multi runtime") } }