package utils import ( "net" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestIP_Equal(t *testing.T) { tests := []struct { name string ip1 net.IP ip2 net.IP expected bool }{ { name: "IPv4 equal", ip1: net.IPv4(192, 168, 1, 1), ip2: net.IPv4(192, 168, 1, 1), expected: true, }, { name: "IPv4 not equal", ip1: net.IPv4(192, 168, 1, 1), ip2: net.IPv4(192, 168, 1, 2), expected: false, }, { name: "IPv4 mapped IPv6 equal to IPv4", ip1: net.IPv4(192, 168, 1, 1), ip2: net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1}, expected: true, }, { name: "nil IPs equal", ip1: nil, ip2: nil, expected: true, }, { name: "nil and non-nil IP not equal", ip1: nil, ip2: net.IPv4(192, 168, 1, 1), expected: false, }, { name: "IPv6 equal", ip1: net.IPv6loopback, ip2: net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, expected: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.expected, tt.ip1.Equal(tt.ip2)) }) } } func TestIP_To4(t *testing.T) { tests := []struct { name string ip net.IP expected net.IP }{ { name: "Valid IPv4", ip: net.IPv4(192, 168, 1, 1), expected: net.IP{192, 168, 1, 1}, }, { name: "IPv4-mapped IPv6", ip: net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1}, expected: net.IP{192, 168, 1, 1}, }, { name: "Pure IPv6", ip: net.IPv6loopback, expected: nil, }, { name: "nil IP", ip: nil, expected: nil, }, { name: "Invalid length IP", ip: net.IP{1, 2, 3}, expected: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.expected, tt.ip.To4()) }) } } func TestIPNet_Contains(t *testing.T) { tests := []struct { name string network *net.IPNet ip net.IP expected bool }{ { name: "IPv4 in network", network: &net.IPNet{ IP: net.IPv4(192, 168, 1, 0), Mask: net.IPv4Mask(255, 255, 255, 0), }, ip: net.IPv4(192, 168, 1, 1), expected: true, }, { name: "IPv4 not in network", network: &net.IPNet{ IP: net.IPv4(192, 168, 1, 0), Mask: net.IPv4Mask(255, 255, 255, 0), }, ip: net.IPv4(192, 168, 2, 1), expected: false, }, { name: "IPv6 in network", network: &net.IPNet{ IP: net.IPv6loopback, Mask: net.CIDRMask(128, 128), }, ip: net.IPv6loopback, expected: true, }, { name: "Mismatched IP versions", network: &net.IPNet{ IP: net.IPv4(192, 168, 1, 0), Mask: net.IPv4Mask(255, 255, 255, 0), }, ip: net.IPv6loopback, expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.expected, tt.network.Contains(tt.ip)) }) } } func TestParseCIDR(t *testing.T) { tests := []struct { name string cidr string expectError bool expectedIP net.IP expectedNet *net.IPNet }{ { name: "Valid IPv4 CIDR", cidr: "192.168.1.0/24", expectError: false, expectedIP: net.IPv4(192, 168, 1, 0), expectedNet: &net.IPNet{ IP: net.IPv4(192, 168, 1, 0), Mask: net.IPv4Mask(255, 255, 255, 0), }, }, { name: "Invalid CIDR format", cidr: "192.168.1.0", expectError: true, }, { name: "Invalid prefix length", cidr: "192.168.1.0/33", expectError: true, }, { name: "Invalid IP address", cidr: "300.168.1.0/24", expectError: true, }, { name: "Empty string", cidr: "", expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ip, ipNet, err := net.ParseCIDR(tt.cidr) if tt.expectError { assert.Error(t, err) return } require.NoError(t, err) assert.True(t, tt.expectedIP.Equal(ip)) assert.True(t, tt.expectedNet.IP.Equal(ipNet.IP)) assert.Equal(t, tt.expectedNet.Mask, ipNet.Mask) }) } } func TestIP_DefaultMask(t *testing.T) { tests := []struct { name string ip net.IP expected net.IPMask }{ { name: "Class A", ip: net.IPv4(10, 0, 0, 0), expected: net.IPv4Mask(255, 0, 0, 0), }, { name: "Class B", ip: net.IPv4(172, 16, 0, 0), expected: net.IPv4Mask(255, 255, 0, 0), }, { name: "Class C", ip: net.IPv4(192, 168, 0, 0), expected: net.IPv4Mask(255, 255, 255, 0), }, { name: "IPv6 address", ip: net.IPv6loopback, expected: nil, }, { name: "nil IP", ip: nil, expected: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.expected, tt.ip.DefaultMask()) }) } } func TestIP_IsPrivate(t *testing.T) { tests := []struct { name string ip net.IP expected bool }{ { name: "Private IPv4 10.x.x.x", ip: net.IPv4(10, 0, 0, 1), expected: true, }, { name: "Private IPv4 172.16.x.x", ip: net.IPv4(172, 16, 0, 1), expected: true, }, { name: "Private IPv4 192.168.x.x", ip: net.IPv4(192, 168, 0, 1), expected: true, }, { name: "Public IPv4", ip: net.IPv4(8, 8, 8, 8), expected: false, }, { name: "Private IPv6 fc00::/7", ip: net.IP{0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, expected: true, }, { name: "Public IPv6", ip: net.IPv6loopback, expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.expected, tt.ip.IsPrivate()) }) } } func TestGetSingleIPNet(t *testing.T) { tests := []struct { name string ip net.IP expected *net.IPNet }{ { name: "IPv4 address", ip: net.IPv4(192, 168, 1, 1), expected: &net.IPNet{ IP: net.IPv4(192, 168, 1, 1), Mask: net.CIDRMask(32, 32), }, }, { name: "IPv4-mapped IPv6 address", ip: net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1}, expected: &net.IPNet{ IP: net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1}, Mask: net.CIDRMask(32, 32), }, }, { name: "IPv6 address", ip: net.IPv6loopback, expected: &net.IPNet{ IP: net.IPv6loopback, Mask: net.CIDRMask(128, 128), }, }, { name: "Another IPv6 address", ip: net.IP{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, expected: &net.IPNet{ IP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, Mask: net.CIDRMask(128, 128), }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := GetSingleIPNet(tt.ip) require.NotNil(t, result) assert.True(t, tt.expected.IP.Equal(result.IP)) assert.Equal(t, tt.expected.Mask, result.Mask) }) } }