summaryrefslogtreecommitdiff
path: root/lib/entry_test.go
diff options
context:
space:
mode:
authoropenai-code-agent[bot] <[email protected]>2026-03-06 20:57:16 +0000
committeropenai-code-agent[bot] <[email protected]>2026-03-06 20:57:16 +0000
commitc2ca6487add435663073d714b0d5f2792f977e55 (patch)
treeec3ae30539110bb57fe8f9cbed926639256d4482 /lib/entry_test.go
parent23a7bbf963aca608ce1344f2f1986034020ad8c3 (diff)
test: add comprehensive lib package coveragecodex/add-unit-tests-lib-package
Diffstat (limited to 'lib/entry_test.go')
-rw-r--r--lib/entry_test.go414
1 files changed, 414 insertions, 0 deletions
diff --git a/lib/entry_test.go b/lib/entry_test.go
new file mode 100644
index 00000000..f3a11989
--- /dev/null
+++ b/lib/entry_test.go
@@ -0,0 +1,414 @@
+package lib
+
+import (
+ "net"
+ "net/netip"
+ "testing"
+
+ "go4.org/netipx"
+)
+
+func TestProcessPrefixVariants(t *testing.T) {
+ e := NewEntry("proc")
+
+ ipv4 := net.ParseIP("1.1.1.1")
+ p, ipType, err := e.processPrefix(ipv4)
+ if err != nil || ipType != IPv4 || p.String() != "1.1.1.1/32" {
+ t.Fatalf("processPrefix(net.IPv4) = %v %v %v", p, ipType, err)
+ }
+
+ if _, _, err := e.processPrefix(net.IP{}); err != ErrInvalidIP {
+ t.Fatalf("expected ErrInvalidIP for empty net.IP, got %v", err)
+ }
+
+ ipv6 := net.ParseIP("2001:db8::1")
+ p, ipType, err = e.processPrefix(ipv6)
+ if err != nil || ipType != IPv6 || p.String() != "2001:db8::1/128" {
+ t.Fatalf("processPrefix(net.IPv6) = %v %v %v", p, ipType, err)
+ }
+
+ _, n, _ := net.ParseCIDR("10.0.0.0/24")
+ p, ipType, err = e.processPrefix(n)
+ if err != nil || ipType != IPv4 || p.String() != "10.0.0.0/24" {
+ t.Fatalf("processPrefix(*net.IPNet) = %v %v %v", p, ipType, err)
+ }
+
+ _, n6, _ := net.ParseCIDR("2001:db8:ffff::/48")
+ p, ipType, err = e.processPrefix(n6)
+ if err != nil || ipType != IPv6 || p.String() != "2001:db8:ffff::/48" {
+ t.Fatalf("processPrefix(*net.IPNet ipv6) = %v %v %v", p, ipType, err)
+ }
+
+ badNet := &net.IPNet{IP: net.IPv4(1, 2, 3, 4), Mask: net.IPMask{1}}
+ if _, _, err := e.processPrefix(badNet); err != ErrInvalidIPNet {
+ t.Fatalf("expected ErrInvalidIPNet, got %v", err)
+ }
+
+ addr := netip.MustParseAddr("192.0.2.1")
+ p, ipType, err = e.processPrefix(addr)
+ if err != nil || ipType != IPv4 || p.String() != "192.0.2.1/32" {
+ t.Fatalf("processPrefix(netip.Addr) = %v %v %v", p, ipType, err)
+ }
+
+ ipv6Addr := netip.MustParseAddr("2001:db8::3")
+ p, ipType, err = e.processPrefix(ipv6Addr)
+ if err != nil || ipType != IPv6 || p.String() != "2001:db8::3/128" {
+ t.Fatalf("processPrefix(netip.Addr ipv6) = %v %v %v", p, ipType, err)
+ }
+
+ addrPtr := netip.MustParseAddr("2001:db8::2")
+ p, ipType, err = e.processPrefix(&addrPtr)
+ if err != nil || ipType != IPv6 || p.String() != "2001:db8::2/128" {
+ t.Fatalf("processPrefix(*netip.Addr) = %v %v %v", p, ipType, err)
+ }
+
+ addrPtr4 := netip.MustParseAddr("198.18.0.1")
+ p, ipType, err = e.processPrefix(&addrPtr4)
+ if err != nil || ipType != IPv4 || p.String() != "198.18.0.1/32" {
+ t.Fatalf("processPrefix(*netip.Addr ipv4) = %v %v %v", p, ipType, err)
+ }
+
+ prefix := netip.MustParsePrefix("198.51.100.0/24")
+ p, ipType, err = e.processPrefix(prefix)
+ if err != nil || ipType != IPv4 || p.String() != "198.51.100.0/24" {
+ t.Fatalf("processPrefix(netip.Prefix) = %v %v %v", p, ipType, err)
+ }
+
+ ipv6PrefixVal := netip.MustParsePrefix("2001:db8:abcd::/48")
+ if p, ipType, err := e.processPrefix(ipv6PrefixVal); err != nil || ipType != IPv6 || p.String() != "2001:db8:abcd::/48" {
+ t.Fatalf("processPrefix(netip.Prefix ipv6) = %v %v %v", p, ipType, err)
+ }
+
+ prefixPtr := netip.MustParsePrefix("2001:db8:ffff::/48")
+ p, ipType, err = e.processPrefix(&prefixPtr)
+ if err != nil || ipType != IPv6 || p.String() != "2001:db8:ffff::/48" {
+ t.Fatalf("processPrefix(*netip.Prefix) = %v %v %v", p, ipType, err)
+ }
+
+ prefixPtr4 := netip.MustParsePrefix("198.51.100.0/24")
+ p, ipType, err = e.processPrefix(&prefixPtr4)
+ if err != nil || ipType != IPv4 || p.String() != "198.51.100.0/24" {
+ t.Fatalf("processPrefix(*netip.Prefix ipv4) = %v %v %v", p, ipType, err)
+ }
+
+ // IPv4-mapped IPv6 with insufficient bits should be rejected
+ badPrefix := netip.MustParsePrefix("::ffff:192.0.2.1/95")
+ if _, _, err := e.processPrefix(badPrefix); err != ErrInvalidPrefix {
+ t.Fatalf("expected ErrInvalidPrefix, got %v", err)
+ }
+
+ mappedPrefix := netip.MustParsePrefix("::ffff:192.0.2.0/120")
+ if p, ipType, err := e.processPrefix(mappedPrefix); err != nil || ipType != IPv4 || p.String() != "192.0.2.0/24" {
+ t.Fatalf("processPrefix(mappedPrefix) = %v %v %v", p, ipType, err)
+ }
+
+ invalidPrefix4 := netip.PrefixFrom(netip.MustParseAddr("1.1.1.1"), 40)
+ if _, _, err := e.processPrefix(invalidPrefix4); err != ErrInvalidPrefix {
+ t.Fatalf("expected ErrInvalidPrefix for invalid ipv4 prefix, got %v", err)
+ }
+
+ invalidPrefix6 := netip.PrefixFrom(netip.MustParseAddr("2001:db8::1"), 200)
+ if _, _, err := e.processPrefix(invalidPrefix6); err != ErrInvalidPrefix {
+ t.Fatalf("expected ErrInvalidPrefix for invalid ipv6 prefix, got %v", err)
+ }
+
+ invalidPrefix4Ptr := invalidPrefix4
+ if _, _, err := e.processPrefix(&invalidPrefix4Ptr); err != ErrInvalidPrefix {
+ t.Fatalf("expected ErrInvalidPrefix for invalid ipv4 prefix pointer, got %v", err)
+ }
+
+ invalidPrefix6Ptr := invalidPrefix6
+ if _, _, err := e.processPrefix(&invalidPrefix6Ptr); err != ErrInvalidPrefix {
+ t.Fatalf("expected ErrInvalidPrefix for invalid ipv6 prefix pointer, got %v", err)
+ }
+
+ zeroPrefix := netip.Prefix{}
+ if _, _, err := e.processPrefix(zeroPrefix); err != ErrInvalidIPLength {
+ t.Fatalf("expected ErrInvalidIPLength for zero prefix, got %v", err)
+ }
+
+ badPrefixPtr := badPrefix
+ if _, _, err := e.processPrefix(&badPrefixPtr); err != ErrInvalidPrefix {
+ t.Fatalf("expected ErrInvalidPrefix for pointer bad prefix, got %v", err)
+ }
+
+ zeroPrefixPtr := zeroPrefix
+ if _, _, err := e.processPrefix(&zeroPrefixPtr); err != ErrInvalidIPLength {
+ t.Fatalf("expected ErrInvalidIPLength for zero prefix pointer, got %v", err)
+ }
+
+ addrZero := netip.Addr{}
+ if _, _, err := e.processPrefix(&addrZero); err != ErrInvalidIPLength {
+ t.Fatalf("expected ErrInvalidIPLength for zero addr pointer, got %v", err)
+ }
+
+ mappedPrefixPtr := mappedPrefix
+ if p, ipType, err := e.processPrefix(&mappedPrefixPtr); err != nil || ipType != IPv4 || p.String() != "192.0.2.0/24" {
+ t.Fatalf("processPrefix(mappedPrefixPtr) = %v %v %v", p, ipType, err)
+ }
+
+ if _, _, err := e.processPrefix(netip.Addr{}); err != ErrInvalidIPLength {
+ t.Fatalf("expected ErrInvalidIPLength, got %v", err)
+ }
+
+ if _, _, err := e.processPrefix("1.2.3.4"); err != nil {
+ t.Fatalf("processPrefix(string ip) error = %v", err)
+ }
+
+ if _, _, err := e.processPrefix("2001:db8::1"); err != nil {
+ t.Fatalf("processPrefix(string ipv6) error = %v", err)
+ }
+
+ if _, _, err := e.processPrefix("10.0.0.0/8"); err != nil {
+ t.Fatalf("processPrefix(string cidr) error = %v", err)
+ }
+
+ if _, _, err := e.processPrefix("2001:db8::/32"); err != nil {
+ t.Fatalf("processPrefix(string cidr ipv6) error = %v", err)
+ }
+
+ if _, _, err := e.processPrefix("invalid/24"); err != ErrInvalidCIDR {
+ t.Fatalf("expected ErrInvalidCIDR, got %v", err)
+ }
+
+ if _, _, err := e.processPrefix(" //comment"); err != ErrCommentLine {
+ t.Fatalf("expected ErrCommentLine, got %v", err)
+ }
+
+ if _, _, err := e.processPrefix(123); err != ErrInvalidPrefixType {
+ t.Fatalf("expected ErrInvalidPrefixType, got %v", err)
+ }
+}
+
+func TestEntryAddAndRemovePrefix(t *testing.T) {
+ e := NewEntry("demo")
+
+ if err := e.AddPrefix("10.0.0.0/24"); err != nil {
+ t.Fatalf("AddPrefix() error = %v", err)
+ }
+ if err := e.AddPrefix("2001:db8::/32"); err != nil {
+ t.Fatalf("AddPrefix() error = %v", err)
+ }
+
+ ipv4set, err := e.GetIPv4Set()
+ if err != nil || !ipv4set.Contains(netip.MustParseAddr("10.0.0.1")) {
+ t.Fatalf("IPv4 set missing data: %v %v", ipv4set, err)
+ }
+
+ ipv6set, err := e.GetIPv6Set()
+ if err != nil || !ipv6set.Contains(netip.MustParseAddr("2001:db8::1")) {
+ t.Fatalf("IPv6 set missing data: %v %v", ipv6set, err)
+ }
+
+ if err := e.RemovePrefix("10.0.0.0/24"); err != nil {
+ t.Fatalf("RemovePrefix() error = %v", err)
+ }
+ e.ipv4Set = nil
+ ipv4set, _ = e.GetIPv4Set()
+ if ipv4set.Contains(netip.MustParseAddr("10.0.0.1")) {
+ t.Fatalf("prefix should be removed")
+ }
+
+ if err := e.RemovePrefix("2001:db8::/32"); err != nil {
+ t.Fatalf("RemovePrefix() error = %v", err)
+ }
+ e.ipv6Set = nil
+ ipv6set, _ = e.GetIPv6Set()
+ if ipv6set.Contains(netip.MustParseAddr("2001:db8::1")) {
+ t.Fatalf("ipv6 prefix should be removed")
+ }
+
+ if err := e.RemovePrefix("invalid"); err == nil {
+ t.Fatalf("expected error for invalid prefix")
+ }
+}
+
+func TestEntryAddRemoveInvalidIPType(t *testing.T) {
+ e := NewEntry("invalid")
+ if err := e.add(nil, IPType("unknown")); err != ErrInvalidIPType {
+ t.Fatalf("expected ErrInvalidIPType, got %v", err)
+ }
+ if err := e.remove(nil, IPType("unknown")); err != ErrInvalidIPType {
+ t.Fatalf("expected ErrInvalidIPType, got %v", err)
+ }
+}
+
+func TestEntryMarshalFunctions(t *testing.T) {
+ e := NewEntry("marshal")
+ _ = e.AddPrefix("203.0.113.0/24")
+ _ = e.AddPrefix("2001:db8:abcd::/48")
+
+ prefixes, err := e.MarshalPrefix()
+ if err != nil || len(prefixes) != 2 {
+ t.Fatalf("MarshalPrefix() = %v, %v", prefixes, err)
+ }
+
+ prefixes, err = e.MarshalPrefix(IgnoreIPv6)
+ if err != nil || len(prefixes) != 1 {
+ t.Fatalf("MarshalPrefix(IgnoreIPv6) = %v, %v", prefixes, err)
+ }
+
+ ranges, err := e.MarshalIPRange()
+ if err != nil || len(ranges) != 2 {
+ t.Fatalf("MarshalIPRange() = %v, %v", ranges, err)
+ }
+
+ ranges, err = e.MarshalIPRange(IgnoreIPv4)
+ if err != nil || len(ranges) != 1 {
+ t.Fatalf("MarshalIPRange(IgnoreIPv4) = %v, %v", ranges, err)
+ }
+
+ text, err := e.MarshalText()
+ if err != nil || len(text) != 2 {
+ t.Fatalf("MarshalText() = %v, %v", text, err)
+ }
+
+ // Ignore IPv4 results
+ prefixes, err = e.MarshalPrefix(IgnoreIPv4)
+ if err != nil || len(prefixes) != 1 {
+ t.Fatalf("MarshalPrefix(IgnoreIPv4) = %v, %v", prefixes, err)
+ }
+
+ text, err = e.MarshalText(IgnoreIPv4)
+ if err != nil || len(text) != 1 {
+ t.Fatalf("MarshalText(IgnoreIPv4) = %v, %v", text, err)
+ }
+
+ text, err = e.MarshalText(IgnoreIPv6)
+ if err != nil || len(text) != 1 {
+ t.Fatalf("MarshalText(IgnoreIPv6) = %v, %v", text, err)
+ }
+}
+
+func TestEntryMarshalErrors(t *testing.T) {
+ e := NewEntry("empty")
+
+ if _, err := e.MarshalPrefix(); err == nil {
+ t.Fatalf("expected error for empty entry")
+ }
+ if _, err := e.MarshalIPRange(); err == nil {
+ t.Fatalf("expected error for empty entry")
+ }
+ if _, err := e.MarshalText(); err == nil {
+ t.Fatalf("expected error for empty entry")
+ }
+}
+
+func TestEntryBuildIPSetError(t *testing.T) {
+ e := NewEntry("errbuild")
+ builder := &netipx.IPSetBuilder{}
+ builder.AddPrefix(netip.Prefix{}) // invalid prefix triggers builder error
+ e.ipv4Builder = builder
+
+ if _, err := e.GetIPv4Set(); err == nil {
+ t.Fatalf("expected buildIPSet error")
+ }
+
+ builder6 := &netipx.IPSetBuilder{}
+ builder6.AddPrefix(netip.Prefix{})
+ e.ipv6Builder = builder6
+ if _, err := e.GetIPv6Set(); err == nil {
+ t.Fatalf("expected buildIPSet error for ipv6")
+ }
+
+ if _, err := e.MarshalPrefix(); err == nil {
+ t.Fatalf("expected MarshalPrefix error from builder")
+ }
+
+ if _, err := e.MarshalIPRange(); err == nil {
+ t.Fatalf("expected MarshalIPRange error from builder")
+ }
+
+ if _, err := e.MarshalText(); err == nil {
+ t.Fatalf("expected MarshalText error from builder")
+ }
+
+ // Use fresh entries to ensure builder errors are preserved
+ e2 := NewEntry("errbuild2")
+ b2 := &netipx.IPSetBuilder{}
+ b2.AddPrefix(netip.Prefix{})
+ e2.ipv4Builder = b2
+ if _, err := e2.MarshalPrefix(); err == nil {
+ t.Fatalf("expected MarshalPrefix error from builder")
+ }
+
+ e3 := NewEntry("errbuild3")
+ b3 := &netipx.IPSetBuilder{}
+ b3.AddPrefix(netip.Prefix{})
+ e3.ipv4Builder = b3
+ if _, err := e3.MarshalIPRange(); err == nil {
+ t.Fatalf("expected MarshalIPRange error from builder")
+ }
+
+ e4 := NewEntry("errbuild4")
+ b4 := &netipx.IPSetBuilder{}
+ b4.AddPrefix(netip.Prefix{})
+ e4.ipv4Builder = b4
+ if _, err := e4.MarshalText(); err == nil {
+ t.Fatalf("expected MarshalText error from builder")
+ }
+}
+
+func TestEntryGetSetErrors(t *testing.T) {
+ e := NewEntry("sets")
+ _ = e.AddPrefix("2001:db8::/32")
+
+ if _, err := e.GetIPv4Set(); err == nil {
+ t.Fatalf("expected error for missing IPv4 set")
+ }
+
+ e2 := NewEntry("sets2")
+ _ = e2.AddPrefix("192.0.2.0/24")
+ if _, err := e2.GetIPv6Set(); err == nil {
+ t.Fatalf("expected error for missing IPv6 set")
+ }
+}
+
+func TestAddPrefixErrorPath(t *testing.T) {
+ e := NewEntry("err")
+ if err := e.AddPrefix("bad-prefix"); err == nil {
+ t.Fatalf("expected error for bad prefix")
+ }
+ if err := e.RemovePrefix("bad-prefix"); err == nil {
+ t.Fatalf("expected error for bad prefix removal")
+ }
+}
+
+func TestEntryCommentLineHandling(t *testing.T) {
+ e := NewEntry("comment")
+ if err := e.AddPrefix("# this is comment"); err != ErrInvalidIPType {
+ t.Fatalf("expected ErrInvalidIPType for comment line, got %v", err)
+ }
+ if err := e.RemovePrefix("// another comment"); err != ErrInvalidIPType {
+ t.Fatalf("expected ErrInvalidIPType for comment line removal, got %v", err)
+ }
+}
+
+func TestEntryMarshalIgnoreIPv6(t *testing.T) {
+ e := NewEntry("ignore6")
+ _ = e.AddPrefix("2001:db8::/32")
+ _ = e.AddPrefix("198.51.100.0/24")
+
+ ranges, err := e.MarshalIPRange(IgnoreIPv6)
+ if err != nil || len(ranges) != 1 {
+ t.Fatalf("MarshalIPRange(IgnoreIPv6) = %v, %v", ranges, err)
+ }
+}
+
+func TestEntryBuildIPSetReuse(t *testing.T) {
+ e := NewEntry("reuse")
+ builder := netipx.IPSetBuilder{}
+ builder.AddPrefix(netip.MustParsePrefix("10.10.0.0/16"))
+ e.ipv4Builder = &builder
+
+ if err := e.buildIPSet(); err != nil {
+ t.Fatalf("buildIPSet() error = %v", err)
+ }
+ if !e.hasIPv4Set() {
+ t.Fatalf("expected ipv4 set to be built")
+ }
+ // Second call should be a no-op but still succeed
+ if err := e.buildIPSet(); err != nil {
+ t.Fatalf("buildIPSet() second call error = %v", err)
+ }
+}