package ip import ( "net" "testing" "github.com/stretchr/testify/require" ) func TestNormalizeIP(t *testing.T) { tests := []struct { name string ip string want string }{ {"plain_ip", "192.168.1.1", "192.168.1.1"}, {"with_port", "192.168.1.1:8080", "192.168.1.1"}, {"with_spaces", " 192.168.1.1 ", "192.168.1.1"}, {"ipv6", "::1", "::1"}, {"ipv6_with_port", "[::1]:8080", "::1"}, {"empty", "", ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := normalizeIP(tt.ip) require.Equal(t, tt.want, got) }) } } func TestIsPrivateIP(t *testing.T) { tests := []struct { name string ip string want bool }{ {"private_10.x", "10.0.0.1", true}, {"private_172.16.x", "172.16.0.1", true}, {"private_172.31.x", "172.31.0.1", true}, {"private_192.168.x", "192.168.1.1", true}, {"private_loopback", "127.0.0.1", true}, {"private_ipv6_loopback", "::1", true}, {"public_ip", "8.8.8.8", false}, {"public_ip2", "1.1.1.1", false}, {"invalid_ip", "invalid", false}, {"empty_ip", "", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := isPrivateIP(tt.ip) require.Equal(t, tt.want, got) }) } } func TestCompileIPRules(t *testing.T) { tests := []struct { name string patterns []string wantCIDRs int wantIPs int wantPatterns int }{ { name: "empty", patterns: []string{}, wantCIDRs: 0, wantIPs: 0, wantPatterns: 0, }, { name: "single_ip", patterns: []string{"192.168.1.1"}, wantCIDRs: 0, wantIPs: 1, wantPatterns: 1, }, { name: "single_cidr", patterns: []string{"192.168.0.0/24"}, wantCIDRs: 1, wantIPs: 0, wantPatterns: 1, }, { name: "mixed", patterns: []string{"192.168.1.1", "10.0.0.0/8"}, wantCIDRs: 1, wantIPs: 1, wantPatterns: 2, }, { name: "with_invalid", patterns: []string{"192.168.1.1", "invalid", "10.0.0.0/8"}, wantCIDRs: 1, wantIPs: 1, wantPatterns: 3, }, { name: "with_empty_and_spaces", patterns: []string{"", " ", "192.168.1.1"}, wantCIDRs: 0, wantIPs: 1, wantPatterns: 3, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { rules := CompileIPRules(tt.patterns) require.Equal(t, tt.wantCIDRs, len(rules.CIDRs)) require.Equal(t, tt.wantIPs, len(rules.IPs)) require.Equal(t, tt.wantPatterns, rules.PatternCount) }) } } func TestMatchesCompiledRules(t *testing.T) { rules := CompileIPRules([]string{"192.168.1.1", "10.0.0.0/8"}) tests := []struct { name string ip string want bool }{ {"match_ip", "192.168.1.1", true}, {"match_cidr", "10.0.1.1", true}, {"no_match", "8.8.8.8", false}, {"invalid", "invalid", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ip := net.ParseIP(tt.ip) got := matchesCompiledRules(ip, rules) require.Equal(t, tt.want, got) }) } } func TestMatchesCompiledRules_NilCases(t *testing.T) { rules := CompileIPRules([]string{"192.168.1.1"}) // nil IP require.False(t, matchesCompiledRules(nil, rules)) // nil rules validIP := net.ParseIP("192.168.1.1") require.False(t, matchesCompiledRules(validIP, nil)) } func TestMatchesPattern(t *testing.T) { tests := []struct { name string client string pattern string want bool }{ {"ip_match", "192.168.1.1", "192.168.1.1", true}, {"ip_no_match", "192.168.1.1", "192.168.1.2", false}, {"cidr_match", "192.168.1.50", "192.168.1.0/24", true}, {"cidr_no_match", "192.168.2.1", "192.168.1.0/24", false}, {"invalid_client", "invalid", "192.168.1.0/24", false}, {"invalid_pattern", "192.168.1.1", "invalid", false}, {"invalid_cidr", "192.168.1.1", "192.168.1/24", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := MatchesPattern(tt.client, tt.pattern) require.Equal(t, tt.want, got) }) } } func TestMatchesAnyPattern(t *testing.T) { patterns := []string{"192.168.1.1", "10.0.0.0/8"} require.True(t, MatchesAnyPattern("192.168.1.1", patterns)) require.True(t, MatchesAnyPattern("10.0.1.1", patterns)) require.False(t, MatchesAnyPattern("8.8.8.8", patterns)) require.False(t, MatchesAnyPattern("8.8.8.8", []string{})) } func TestCheckIPRestriction(t *testing.T) { tests := []struct { name string clientIP string whitelist []string blacklist []string wantAllow bool }{ {"no_restrictions", "192.168.1.1", nil, nil, true}, {"whitelist_match", "192.168.1.1", []string{"192.168.1.0/24"}, nil, true}, {"whitelist_no_match", "192.168.1.1", []string{"10.0.0.0/8"}, nil, false}, {"blacklist_match", "192.168.1.1", nil, []string{"192.168.1.0/24"}, false}, {"blacklist_no_match", "192.168.1.1", nil, []string{"10.0.0.0/8"}, true}, {"blacklist_priority", "192.168.1.1", []string{"0.0.0.0/0"}, []string{"192.168.1.0/24"}, false}, {"empty_ip", "", []string{"192.168.1.0/24"}, nil, false}, {"invalid_ip", "invalid", []string{"192.168.1.0/24"}, nil, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { allow, _ := CheckIPRestriction(tt.clientIP, tt.whitelist, tt.blacklist) require.Equal(t, tt.wantAllow, allow) }) } } func TestValidateIPPattern(t *testing.T) { tests := []struct { name string pattern string want bool }{ {"valid_ip", "192.168.1.1", true}, {"valid_ipv6", "::1", true}, {"valid_cidr", "192.168.0.0/24", true}, {"invalid", "not-an-ip", false}, {"empty", "", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ValidateIPPattern(tt.pattern) require.Equal(t, tt.want, got) }) } } func TestValidateIPPatterns(t *testing.T) { patterns := []string{"192.168.1.1", "invalid", "192.168.0.0/24", "not-an-ip"} invalid := ValidateIPPatterns(patterns) require.Equal(t, []string{"invalid", "not-an-ip"}, invalid) // all valid validPatterns := []string{"192.168.1.1", "192.168.0.0/24"} invalid = ValidateIPPatterns(validPatterns) require.Empty(t, invalid) }