package app_test import ( "path/filepath" "strings" "testing" "proxyctl/internal/app" "proxyctl/internal/profile" ) func TestProfileValidationAcceptsSupportedURLsAndNormalizesEveryBypassKind(t *testing.T) { application, err := app.New(profile.NewFileStore(filepath.Join(t.TempDir(), "profiles.json"))) if err != nil { t.Fatal(err) } result := application.Dispatch(app.Intent{Kind: app.CreateProfile, Profile: profile.Profile{ Name: "mixed", Mode: profile.AdvancedMode, HTTPURL: "http://proxy.example:80", HTTPSURL: "https://user:secret@proxy.example:443", AllURL: "socks5h://[2001:db8::5]:1080", Bypass: []string{ "LOCALHOST, Example.COM.", ".Internal.Example", "192.0.2.1", "2001:0db8::1", "10.2.3.4/8", "2001:db8:abcd::5/48", "localhost", }, }}) if result.Err != nil { t.Fatalf("create validated Profile: %v", result.Err) } want := []string{"localhost", "example.com", ".internal.example", "192.0.2.1", "2001:db8::1", "10.0.0.0/8", "2001:db8:abcd::/48"} if got := result.Snapshot.Profiles[0].Bypass; !equalStrings(got, want) { t.Fatalf("normalized Bypass = %#v, want %#v", got, want) } } func TestProfileValidationRejectsMalformedURLsAndBypassWithActionableErrors(t *testing.T) { tests := []struct { name string profile profile.Profile want string }{ {"unsupported scheme", profile.Profile{Name: "bad", Mode: profile.SingleMode, SingleURL: "ftp://user:secret@proxy.example:21"}, "http, https, socks5, or socks5h"}, {"missing host", profile.Profile{Name: "bad", Mode: profile.SingleMode, SingleURL: "http://:8080"}, "host"}, {"missing port", profile.Profile{Name: "bad", Mode: profile.SingleMode, SingleURL: "http://proxy.example"}, "port"}, {"invalid port", profile.Profile{Name: "bad", Mode: profile.SingleMode, SingleURL: "http://proxy.example:70000"}, "between 1 and 65535"}, {"path", profile.Profile{Name: "bad", Mode: profile.SingleMode, SingleURL: "http://proxy.example:8080/path"}, "path"}, {"invalid IPv4 host", profile.Profile{Name: "bad", Mode: profile.SingleMode, SingleURL: "http://999.1.2.3:8080"}, "invalid host"}, {"invalid bypass", profile.Profile{Name: "bad", Mode: profile.SingleMode, SingleURL: "http://proxy.example:8080", Bypass: []string{"*"}}, "hostname, domain suffix, IP address, or CIDR"}, {"invalid IPv4 bypass", profile.Profile{Name: "bad", Mode: profile.SingleMode, SingleURL: "http://proxy.example:8080", Bypass: []string{"999.1.2.3"}}, "hostname, domain suffix, IP address, or CIDR"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { application, err := app.New(profile.NewFileStore(filepath.Join(t.TempDir(), "profiles.json"))) if err != nil { t.Fatal(err) } result := application.Dispatch(app.Intent{Kind: app.CreateProfile, Profile: tt.profile}) if result.Err == nil || !strings.Contains(result.Err.Error(), tt.want) { t.Fatalf("error = %v, want actionable text containing %q", result.Err, tt.want) } if strings.Contains(result.Err.Error(), "secret") { t.Fatalf("validation error exposed proxy password: %v", result.Err) } }) } }