Files
proxyctl coordinator 0f4c122eff feat: add Protocol Probe and accept bare host:port in profiles
- Ctrl+D in the Single URL field runs a handshake probe against the typed endpoint. It detects HTTP and/or SOCKS5 without forwarding traffic or contacting external hosts.

- HTTP-capable endpoints map to http://; SOCKS5-only endpoints map to socks5h://. Userinfo is preserved as typed.

- Bare host:port input is accepted and defaults to http:// at save.

- Update README privacy boundary and add .scratch/protocol-probe/spec.md.
2026-07-17 17:36:50 +08:00

70 lines
2.3 KiB
Go

package profile
import "testing"
func TestValidateAndNormalizeDefaultsSchemeLessURLsToHTTP(t *testing.T) {
tests := []struct {
name string
input Profile
want map[string]string
}{
{
name: "bare single URL",
input: Profile{Name: "office", SingleURL: "proxy.example:8080"},
want: map[string]string{"single": "http://proxy.example:8080"},
},
{
name: "bare single URL with userinfo",
input: Profile{Name: "office", SingleURL: "alice:secret@proxy.example:8080"},
want: map[string]string{"single": "http://alice:secret@proxy.example:8080"},
},
{
name: "bare IPv6 single URL",
input: Profile{Name: "office", SingleURL: "[::1]:8888"},
want: map[string]string{"single": "http://[::1]:8888"},
},
{
name: "bare advanced URLs",
input: Profile{Name: "office", Mode: AdvancedMode,
HTTPURL: "proxy.example:8080", HTTPSURL: "secure.example:8443", AllURL: "socks.example:1080"},
want: map[string]string{
"http": "http://proxy.example:8080", "https": "http://secure.example:8443", "all": "http://socks.example:1080"},
},
{
name: "explicit schemes untouched",
input: Profile{Name: "office", Mode: AdvancedMode,
HTTPURL: "https://proxy.example:8443", AllURL: "socks5h://socks.example:1080"},
want: map[string]string{"http": "https://proxy.example:8443", "all": "socks5h://socks.example:1080"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ValidateAndNormalize(tt.input)
if err != nil {
t.Fatalf("ValidateAndNormalize returned error: %v", err)
}
values := map[string]string{
"single": got.SingleURL, "http": got.HTTPURL, "https": got.HTTPSURL, "all": got.AllURL,
}
for field, want := range tt.want {
if values[field] != want {
t.Fatalf("%s = %q, want %q", field, values[field], want)
}
}
})
}
}
func TestValidateAndNormalizeSchemeLessSingleURLExpandsToAllProtocols(t *testing.T) {
got, err := ValidateAndNormalize(Profile{Name: "office", SingleURL: "localhost:8888"})
if err != nil {
t.Fatalf("ValidateAndNormalize returned error: %v", err)
}
if got.SingleURL != "http://localhost:8888" {
t.Fatalf("SingleURL = %q, want http://localhost:8888", got.SingleURL)
}
if got.HTTPURL != got.SingleURL || got.HTTPSURL != got.SingleURL || got.AllURL != got.SingleURL {
t.Fatalf("Single mode did not expand: %#v", got)
}
}