package validate import ( "strings" "testing" ) func TestValidateHostPort(t *testing.T) { tests := []struct { name string hostPort string wantErr bool errSubstr string }{ {"valid localhost:8080", "localhost:8080", false, ""}, {"valid 0.0.0.0:443", "0.0.0.0:443", false, ""}, {"valid 192.168.1.1:9090", "192.168.1.1:9090", false, ""}, {"valid max port", "host:65535", false, ""}, {"valid port 1", "host:1", false, ""}, {"missing port", "localhost", true, "expected format host:port"}, {"missing host", ":8080", true, "host must not be empty"}, {"non-numeric port", "host:abc", true, "port must be a number"}, {"port too large", "host:99999", true, "port must be a number"}, {"port zero", "host:0", true, "port must be a number"}, {"empty string", "", true, "expected format host:port"}, {"negative port", "host:-1", true, "port must be a number"}, {"multiple colons", "host:80:90", true, "expected format host:port"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ValidateHostPort(tt.hostPort) if tt.wantErr { if err == nil { t.Errorf("ValidateHostPort(%q) = nil, want error containing %q", tt.hostPort, tt.errSubstr) } else if tt.errSubstr != "" && !strings.Contains(err.Error(), tt.errSubstr) { t.Errorf("ValidateHostPort(%q) error = %q, want error containing %q", tt.hostPort, err.Error(), tt.errSubstr) } } else { if err != nil { t.Errorf("ValidateHostPort(%q) = %v, want nil", tt.hostPort, err) } } }) } } func TestValidatePort(t *testing.T) { tests := []struct { name string port int wantErr bool }{ {"valid port 1", 1, false}, {"valid port 80", 80, false}, {"valid port 443", 443, false}, {"valid port 8080", 8080, false}, {"valid port 65535", 65535, false}, {"invalid port 0", 0, true}, {"invalid port -1", -1, true}, {"invalid port 65536", 65536, true}, {"invalid large port", 100000, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ValidatePort(tt.port) if tt.wantErr { if err == nil { t.Errorf("ValidatePort(%d) = nil, want error", tt.port) } } else { if err != nil { t.Errorf("ValidatePort(%d) = %v, want nil", tt.port, err) } } }) } } func TestValidateHostOrHostPort(t *testing.T) { tests := []struct { name string addr string wantErr bool errSubstr string }{ {"valid host only", "localhost", false, ""}, {"valid hostname", "myserver.example.com", false, ""}, {"valid IP", "192.168.1.1", false, ""}, {"valid host:port", "localhost:8080", false, ""}, {"valid IP:port", "0.0.0.0:443", false, ""}, {"empty string", "", true, "address must not be empty"}, {"invalid port in host:port", "host:abc", true, "port must be a number"}, {"missing host in host:port", ":8080", true, "host must not be empty"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ValidateHostOrHostPort(tt.addr) if tt.wantErr { if err == nil { t.Errorf("ValidateHostOrHostPort(%q) = nil, want error containing %q", tt.addr, tt.errSubstr) } else if tt.errSubstr != "" && !strings.Contains(err.Error(), tt.errSubstr) { t.Errorf("ValidateHostOrHostPort(%q) error = %q, want error containing %q", tt.addr, err.Error(), tt.errSubstr) } } else { if err != nil { t.Errorf("ValidateHostOrHostPort(%q) = %v, want nil", tt.addr, err) } } }) } } func TestExtractSwarmKeyHex(t *testing.T) { validHex := "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2" tests := []struct { name string input string want string }{ { "full swarm key format", "/key/swarm/psk/1.0.0/\n/base16/\n" + validHex + "\n", validHex, }, { "full swarm key format no trailing newline", "/key/swarm/psk/1.0.0/\n/base16/\n" + validHex, validHex, }, { "raw hex string", validHex, validHex, }, { "with leading and trailing whitespace", " " + validHex + " ", validHex, }, { "empty string", "", "", }, { "only header lines no hex", "/key/swarm/psk/1.0.0/\n/base16/\n", "/key/swarm/psk/1.0.0/\n/base16/", }, { "base16 marker only", "/base16/\n" + validHex, validHex, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ExtractSwarmKeyHex(tt.input) if got != tt.want { t.Errorf("ExtractSwarmKeyHex(%q) = %q, want %q", tt.input, got, tt.want) } }) } } func TestValidateSwarmKey(t *testing.T) { validHex := "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2" tests := []struct { name string key string wantErr bool errSubstr string }{ { "valid 64-char hex", validHex, false, "", }, { "valid full swarm key format", "/key/swarm/psk/1.0.0/\n/base16/\n" + validHex, false, "", }, { "too short", "a1b2c3d4", true, "must be 64 hex characters", }, { "too long", validHex + "ffff", true, "must be 64 hex characters", }, { "non-hex characters", "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", true, "must be valid hexadecimal", }, { "empty string", "", true, "must be 64 hex characters", }, { "63 chars (one short)", validHex[:63], true, "must be 64 hex characters", }, { "65 chars (one over)", validHex + "a", true, "must be 64 hex characters", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ValidateSwarmKey(tt.key) if tt.wantErr { if err == nil { t.Errorf("ValidateSwarmKey(%q) = nil, want error containing %q", tt.key, tt.errSubstr) } else if tt.errSubstr != "" && !strings.Contains(err.Error(), tt.errSubstr) { t.Errorf("ValidateSwarmKey(%q) error = %q, want error containing %q", tt.key, err.Error(), tt.errSubstr) } } else { if err != nil { t.Errorf("ValidateSwarmKey(%q) = %v, want nil", tt.key, err) } } }) } } func TestExtractTCPPort(t *testing.T) { tests := []struct { name string multiaddr string want string }{ { "valid multiaddr with tcp port", "/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWExample", "4001", }, { "valid multiaddr no p2p", "/ip4/0.0.0.0/tcp/8080", "8080", }, { "ipv6 with tcp port", "/ip6/::/tcp/9090/p2p/12D3KooWExample", "9090", }, { "no tcp component", "/ip4/127.0.0.1/udp/4001", "", }, { "empty string", "", "", }, { "tcp at end without port value", "/ip4/127.0.0.1/tcp", "", }, { "only tcp with port", "/tcp/443", "443", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ExtractTCPPort(tt.multiaddr) if got != tt.want { t.Errorf("ExtractTCPPort(%q) = %q, want %q", tt.multiaddr, got, tt.want) } }) } } func TestValidationError_Error(t *testing.T) { tests := []struct { name string err ValidationError want string }{ { "with hint", ValidationError{ Path: "discovery.bootstrap_peers[0]", Message: "invalid multiaddr", Hint: "expected /ip{4,6}/.../tcp//p2p/", }, "discovery.bootstrap_peers[0]: invalid multiaddr; expected /ip{4,6}/.../tcp//p2p/", }, { "without hint", ValidationError{ Path: "node.listen_addr", Message: "must not be empty", }, "node.listen_addr: must not be empty", }, { "empty hint", ValidationError{ Path: "config.port", Message: "invalid", Hint: "", }, "config.port: invalid", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.err.Error() if got != tt.want { t.Errorf("ValidationError.Error() = %q, want %q", got, tt.want) } }) } }