package lib import ( "net" "net/netip" "testing" ) func TestNewEntry(t *testing.T) { tests := []struct { name string want string }{ {"test", "TEST"}, {" Test ", "TEST"}, {"UPPER", "UPPER"}, {"lower", "LOWER"}, {" ", ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { entry := NewEntry(tt.name) if entry.GetName() != tt.want { t.Errorf("NewEntry(%q).GetName() = %q, want %q", tt.name, entry.GetName(), tt.want) } }) } } func TestEntryAddPrefix_IPv4String(t *testing.T) { entry := NewEntry("test") // Test adding IPv4 address if err := entry.AddPrefix("192.168.1.1"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } if !entry.hasIPv4Builder() { t.Error("Expected IPv4 builder to be set") } // Test adding IPv4 CIDR if err := entry.AddPrefix("10.0.0.0/8"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } } func TestEntryAddPrefix_IPv6String(t *testing.T) { entry := NewEntry("test") // Test adding IPv6 address if err := entry.AddPrefix("2001:db8::1"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } if !entry.hasIPv6Builder() { t.Error("Expected IPv6 builder to be set") } // Test adding IPv6 CIDR if err := entry.AddPrefix("2001:db8::/32"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } } func TestEntryAddPrefix_NetIP(t *testing.T) { entry := NewEntry("test") // Test adding net.IP IPv4 ipv4 := net.ParseIP("192.168.1.1") if err := entry.AddPrefix(ipv4); err != nil { t.Fatalf("AddPrefix net.IP IPv4 failed: %v", err) } // Test adding net.IP IPv6 ipv6 := net.ParseIP("2001:db8::1") if err := entry.AddPrefix(ipv6); err != nil { t.Fatalf("AddPrefix net.IP IPv6 failed: %v", err) } } func TestEntryAddPrefix_NetIPNet(t *testing.T) { entry := NewEntry("test") // Test adding *net.IPNet IPv4 _, ipnet4, _ := net.ParseCIDR("10.0.0.0/8") if err := entry.AddPrefix(ipnet4); err != nil { t.Fatalf("AddPrefix *net.IPNet IPv4 failed: %v", err) } // Test adding *net.IPNet IPv6 _, ipnet6, _ := net.ParseCIDR("2001:db8::/32") if err := entry.AddPrefix(ipnet6); err != nil { t.Fatalf("AddPrefix *net.IPNet IPv6 failed: %v", err) } } func TestEntryAddPrefix_NetipAddr(t *testing.T) { entry := NewEntry("test") // Test adding netip.Addr IPv4 addr4 := netip.MustParseAddr("192.168.1.1") if err := entry.AddPrefix(addr4); err != nil { t.Fatalf("AddPrefix netip.Addr IPv4 failed: %v", err) } // Test adding netip.Addr IPv6 addr6 := netip.MustParseAddr("2001:db8::1") if err := entry.AddPrefix(addr6); err != nil { t.Fatalf("AddPrefix netip.Addr IPv6 failed: %v", err) } } func TestEntryAddPrefix_NetipAddrPointer(t *testing.T) { entry := NewEntry("test") // Test adding *netip.Addr IPv4 addr4 := netip.MustParseAddr("192.168.1.1") if err := entry.AddPrefix(&addr4); err != nil { t.Fatalf("AddPrefix *netip.Addr IPv4 failed: %v", err) } // Test adding *netip.Addr IPv6 addr6 := netip.MustParseAddr("2001:db8::1") if err := entry.AddPrefix(&addr6); err != nil { t.Fatalf("AddPrefix *netip.Addr IPv6 failed: %v", err) } } func TestEntryAddPrefix_NetipPrefix(t *testing.T) { entry := NewEntry("test") // Test adding netip.Prefix IPv4 prefix4 := netip.MustParsePrefix("10.0.0.0/8") if err := entry.AddPrefix(prefix4); err != nil { t.Fatalf("AddPrefix netip.Prefix IPv4 failed: %v", err) } // Test adding netip.Prefix IPv6 prefix6 := netip.MustParsePrefix("2001:db8::/32") if err := entry.AddPrefix(prefix6); err != nil { t.Fatalf("AddPrefix netip.Prefix IPv6 failed: %v", err) } } func TestEntryAddPrefix_NetipPrefixPointer(t *testing.T) { entry := NewEntry("test") // Test adding *netip.Prefix IPv4 prefix4 := netip.MustParsePrefix("10.0.0.0/8") if err := entry.AddPrefix(&prefix4); err != nil { t.Fatalf("AddPrefix *netip.Prefix IPv4 failed: %v", err) } // Test adding *netip.Prefix IPv6 prefix6 := netip.MustParsePrefix("2001:db8::/32") if err := entry.AddPrefix(&prefix6); err != nil { t.Fatalf("AddPrefix *netip.Prefix IPv6 failed: %v", err) } } func TestEntryAddPrefix_CommentLine(t *testing.T) { entry := NewEntry("test") // Test comment lines - these should either return ErrCommentLine or ErrInvalidIPType // because the processPrefix function returns ErrCommentLine for empty strings after // stripping comments, and AddPrefix then passes nil to add() which returns ErrInvalidIPType comments := []string{ "# comment", "// comment", "/* comment */", " # comment with leading spaces", } for _, comment := range comments { err := entry.AddPrefix(comment) // After stripping comments, the string is empty, and processPrefix returns ErrCommentLine // AddPrefix checks for ErrCommentLine and skips it, but then calls add with nil which // returns ErrInvalidIPType. This is expected behavior. if err != nil && err != ErrCommentLine && err != ErrInvalidIPType { t.Errorf("AddPrefix(%q) unexpected error: %v", comment, err) } } } func TestEntryAddPrefix_InvalidInput(t *testing.T) { entry := NewEntry("test") // Test invalid inputs invalidInputs := []string{ "invalid", "192.168.1.256", // Invalid IP "10.0.0.0/33", // Invalid prefix length "2001:db8::gggg", } for _, input := range invalidInputs { err := entry.AddPrefix(input) if err == nil { t.Errorf("AddPrefix(%q) expected error, got nil", input) } } } func TestEntryAddPrefix_UnsupportedType(t *testing.T) { entry := NewEntry("test") err := entry.AddPrefix(12345) // int is not supported if err != ErrInvalidPrefixType { t.Errorf("AddPrefix(int) = %v, want %v", err, ErrInvalidPrefixType) } } func TestEntryRemovePrefix(t *testing.T) { entry := NewEntry("test") // Add some prefixes first if err := entry.AddPrefix("192.168.1.0/24"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } if err := entry.AddPrefix("2001:db8::/32"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } // Remove a prefix if err := entry.RemovePrefix("192.168.1.0/24"); err != nil { t.Fatalf("RemovePrefix failed: %v", err) } if err := entry.RemovePrefix("2001:db8::/32"); err != nil { t.Fatalf("RemovePrefix failed: %v", err) } } func TestEntryRemovePrefix_CommentLine(t *testing.T) { entry := NewEntry("test") // Test comment lines - similar to AddPrefix, after stripping comments, // the string is empty, and processPrefix returns ErrCommentLine. // RemovePrefix checks for ErrCommentLine and skips it, but then calls // remove with nil which returns ErrInvalidIPType. err := entry.RemovePrefix("# comment") if err != nil && err != ErrCommentLine && err != ErrInvalidIPType { t.Errorf("RemovePrefix with comment unexpected error: %v", err) } } func TestEntryRemovePrefix_NoBuilder(t *testing.T) { entry := NewEntry("test") // Try to remove from empty entry - should not error if err := entry.RemovePrefix("192.168.1.0/24"); err != nil { // Error is expected if no builder exists t.Logf("RemovePrefix from empty entry: %v", err) } } func TestEntryMarshalPrefix(t *testing.T) { entry := NewEntry("test") // Add prefixes if err := entry.AddPrefix("192.168.1.0/24"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } if err := entry.AddPrefix("2001:db8::/32"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } // Marshal all prefixes prefixes, err := entry.MarshalPrefix() if err != nil { t.Fatalf("MarshalPrefix failed: %v", err) } if len(prefixes) != 2 { t.Errorf("MarshalPrefix returned %d prefixes, want 2", len(prefixes)) } } func TestEntryMarshalPrefix_IgnoreIPv4(t *testing.T) { entry := NewEntry("test") if err := entry.AddPrefix("192.168.1.0/24"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } if err := entry.AddPrefix("2001:db8::/32"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } // Marshal with IgnoreIPv4 prefixes, err := entry.MarshalPrefix(IgnoreIPv4) if err != nil { t.Fatalf("MarshalPrefix failed: %v", err) } // Should only have IPv6 for _, p := range prefixes { if p.Addr().Is4() { t.Error("Expected no IPv4 prefixes when IgnoreIPv4 is set") } } } func TestEntryMarshalPrefix_IgnoreIPv6(t *testing.T) { entry := NewEntry("test") if err := entry.AddPrefix("192.168.1.0/24"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } if err := entry.AddPrefix("2001:db8::/32"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } // Marshal with IgnoreIPv6 prefixes, err := entry.MarshalPrefix(IgnoreIPv6) if err != nil { t.Fatalf("MarshalPrefix failed: %v", err) } // Should only have IPv4 for _, p := range prefixes { if p.Addr().Is6() { t.Error("Expected no IPv6 prefixes when IgnoreIPv6 is set") } } } func TestEntryMarshalPrefix_Empty(t *testing.T) { entry := NewEntry("test") // Marshal from empty entry _, err := entry.MarshalPrefix() if err == nil { t.Error("MarshalPrefix from empty entry should return error") } } func TestEntryMarshalIPRange(t *testing.T) { entry := NewEntry("test") if err := entry.AddPrefix("192.168.1.0/24"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } if err := entry.AddPrefix("2001:db8::/32"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } // Marshal IP ranges ranges, err := entry.MarshalIPRange() if err != nil { t.Fatalf("MarshalIPRange failed: %v", err) } if len(ranges) != 2 { t.Errorf("MarshalIPRange returned %d ranges, want 2", len(ranges)) } } func TestEntryMarshalIPRange_IgnoreIPv4(t *testing.T) { entry := NewEntry("test") if err := entry.AddPrefix("192.168.1.0/24"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } if err := entry.AddPrefix("2001:db8::/32"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } // Marshal with IgnoreIPv4 ranges, err := entry.MarshalIPRange(IgnoreIPv4) if err != nil { t.Fatalf("MarshalIPRange failed: %v", err) } // Should only have IPv6 for _, r := range ranges { if r.From().Is4() { t.Error("Expected no IPv4 ranges when IgnoreIPv4 is set") } } } func TestEntryMarshalIPRange_IgnoreIPv6(t *testing.T) { entry := NewEntry("test") if err := entry.AddPrefix("192.168.1.0/24"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } if err := entry.AddPrefix("2001:db8::/32"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } // Marshal with IgnoreIPv6 ranges, err := entry.MarshalIPRange(IgnoreIPv6) if err != nil { t.Fatalf("MarshalIPRange failed: %v", err) } // Should only have IPv4 for _, r := range ranges { if r.From().Is6() { t.Error("Expected no IPv6 ranges when IgnoreIPv6 is set") } } } func TestEntryMarshalIPRange_Empty(t *testing.T) { entry := NewEntry("test") // Marshal from empty entry _, err := entry.MarshalIPRange() if err == nil { t.Error("MarshalIPRange from empty entry should return error") } } func TestEntryMarshalText(t *testing.T) { entry := NewEntry("test") if err := entry.AddPrefix("192.168.1.0/24"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } if err := entry.AddPrefix("2001:db8::/32"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } // Marshal text cidrList, err := entry.MarshalText() if err != nil { t.Fatalf("MarshalText failed: %v", err) } if len(cidrList) != 2 { t.Errorf("MarshalText returned %d items, want 2", len(cidrList)) } } func TestEntryMarshalText_IgnoreIPv4(t *testing.T) { entry := NewEntry("test") if err := entry.AddPrefix("192.168.1.0/24"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } if err := entry.AddPrefix("2001:db8::/32"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } // Marshal with IgnoreIPv4 cidrList, err := entry.MarshalText(IgnoreIPv4) if err != nil { t.Fatalf("MarshalText failed: %v", err) } if len(cidrList) != 1 { t.Errorf("MarshalText returned %d items, want 1", len(cidrList)) } } func TestEntryMarshalText_IgnoreIPv6(t *testing.T) { entry := NewEntry("test") if err := entry.AddPrefix("192.168.1.0/24"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } if err := entry.AddPrefix("2001:db8::/32"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } // Marshal with IgnoreIPv6 cidrList, err := entry.MarshalText(IgnoreIPv6) if err != nil { t.Fatalf("MarshalText failed: %v", err) } if len(cidrList) != 1 { t.Errorf("MarshalText returned %d items, want 1", len(cidrList)) } } func TestEntryMarshalText_Empty(t *testing.T) { entry := NewEntry("test") // Marshal from empty entry _, err := entry.MarshalText() if err == nil { t.Error("MarshalText from empty entry should return error") } } func TestEntryGetIPv4Set(t *testing.T) { entry := NewEntry("test") if err := entry.AddPrefix("192.168.1.0/24"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } ipset, err := entry.GetIPv4Set() if err != nil { t.Fatalf("GetIPv4Set failed: %v", err) } if ipset == nil { t.Error("GetIPv4Set returned nil") } } func TestEntryGetIPv4Set_NoIPv4(t *testing.T) { entry := NewEntry("test") if err := entry.AddPrefix("2001:db8::/32"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } _, err := entry.GetIPv4Set() if err == nil { t.Error("GetIPv4Set should return error when no IPv4 data") } } func TestEntryGetIPv6Set(t *testing.T) { entry := NewEntry("test") if err := entry.AddPrefix("2001:db8::/32"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } ipset, err := entry.GetIPv6Set() if err != nil { t.Fatalf("GetIPv6Set failed: %v", err) } if ipset == nil { t.Error("GetIPv6Set returned nil") } } func TestEntryGetIPv6Set_NoIPv6(t *testing.T) { entry := NewEntry("test") if err := entry.AddPrefix("192.168.1.0/24"); err != nil { t.Fatalf("AddPrefix failed: %v", err) } _, err := entry.GetIPv6Set() if err == nil { t.Error("GetIPv6Set should return error when no IPv6 data") } } func TestEntryAddPrefix_IPv4MappedIPv6(t *testing.T) { entry := NewEntry("test") // Test IPv4-mapped IPv6 prefix prefix := netip.MustParsePrefix("::ffff:192.168.1.0/120") if err := entry.AddPrefix(prefix); err != nil { t.Fatalf("AddPrefix IPv4-mapped IPv6 prefix failed: %v", err) } // Should be stored as IPv4 if !entry.hasIPv4Builder() { t.Error("IPv4-mapped IPv6 should be stored as IPv4") } } func TestEntryAddPrefix_IPv4MappedIPv6InvalidBits(t *testing.T) { entry := NewEntry("test") // Test IPv4-mapped IPv6 prefix with invalid bits (<96) prefix := netip.MustParsePrefix("::ffff:192.168.1.0/64") err := entry.AddPrefix(prefix) if err != ErrInvalidPrefix { t.Errorf("AddPrefix with invalid IPv4-mapped bits = %v, want %v", err, ErrInvalidPrefix) } } func TestEntryAddPrefix_InvalidCIDRWithIPv4MappedIPv6(t *testing.T) { entry := NewEntry("test") // This tests the edge case where network.String() contains "::" // but the address unmaps to IPv4 err := entry.AddPrefix("::ffff:192.168.1.1/128") // This should be handled as invalid based on the code logic if err != nil && err != ErrInvalidCIDR { t.Logf("AddPrefix with IPv4-mapped IPv6 CIDR: %v", err) } } func TestEntryAddPrefix_IPv4MappedIPv6PrefixPointer(t *testing.T) { entry := NewEntry("test") // Test *netip.Prefix with IPv4-mapped IPv6 prefix := netip.MustParsePrefix("::ffff:192.168.1.0/120") if err := entry.AddPrefix(&prefix); err != nil { t.Fatalf("AddPrefix *netip.Prefix IPv4-mapped failed: %v", err) } // Should be stored as IPv4 if !entry.hasIPv4Builder() { t.Error("IPv4-mapped IPv6 should be stored as IPv4") } } func TestEntryAddPrefix_IPv4MappedIPv6PrefixPointerInvalidBits(t *testing.T) { entry := NewEntry("test") // Test *netip.Prefix with IPv4-mapped IPv6 invalid bits (<96) prefix := netip.MustParsePrefix("::ffff:192.168.1.0/64") err := entry.AddPrefix(&prefix) if err != ErrInvalidPrefix { t.Errorf("AddPrefix with invalid IPv4-mapped bits = %v, want %v", err, ErrInvalidPrefix) } }