diff options
Diffstat (limited to 'lib/container_test.go')
| -rw-r--r-- | lib/container_test.go | 717 |
1 files changed, 717 insertions, 0 deletions
diff --git a/lib/container_test.go b/lib/container_test.go new file mode 100644 index 00000000..fbc20a8f --- /dev/null +++ b/lib/container_test.go @@ -0,0 +1,717 @@ +package lib + +import ( + "testing" +) + +func TestNewContainer(t *testing.T) { + c := NewContainer() + if c == nil { + t.Fatal("NewContainer() returned nil") + } + if c.Len() != 0 { + t.Errorf("NewContainer().Len() = %d, want 0", c.Len()) + } +} + +func TestContainer_GetEntry(t *testing.T) { + c := NewContainer() + entry := NewEntry("test") + entry.AddPrefix("192.168.1.0/24") + c.Add(entry) + + tests := []struct { + name string + entryName string + wantFound bool + }{ + { + name: "existing entry", + entryName: "test", + wantFound: true, + }, + { + name: "existing entry uppercase", + entryName: "TEST", + wantFound: true, + }, + { + name: "existing entry with spaces", + entryName: " test ", + wantFound: true, + }, + { + name: "non-existing entry", + entryName: "nonexistent", + wantFound: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, found := c.GetEntry(tt.entryName) + if found != tt.wantFound { + t.Errorf("Container.GetEntry() found = %v, wantFound %v", found, tt.wantFound) + } + if tt.wantFound && got == nil { + t.Error("Container.GetEntry() returned nil entry") + } + }) + } +} + +func TestContainer_Add(t *testing.T) { + tests := []struct { + name string + entries []*Entry + opts []IgnoreIPOption + wantLen int + }{ + { + name: "add single entry", + entries: []*Entry{ + func() *Entry { + e := NewEntry("test1") + e.AddPrefix("192.168.1.0/24") + return e + }(), + }, + opts: nil, + wantLen: 1, + }, + { + name: "add multiple entries", + entries: []*Entry{ + func() *Entry { + e := NewEntry("test1") + e.AddPrefix("192.168.1.0/24") + return e + }(), + func() *Entry { + e := NewEntry("test2") + e.AddPrefix("10.0.0.0/8") + return e + }(), + }, + opts: nil, + wantLen: 2, + }, + { + name: "add duplicate entry", + entries: []*Entry{ + func() *Entry { + e := NewEntry("test1") + e.AddPrefix("192.168.1.0/24") + return e + }(), + func() *Entry { + e := NewEntry("test1") + e.AddPrefix("10.0.0.0/8") + return e + }(), + }, + opts: nil, + wantLen: 1, // Should merge into one + }, + { + name: "add with ignore IPv4", + entries: []*Entry{ + func() *Entry { + e := NewEntry("test1") + e.AddPrefix("192.168.1.0/24") + e.AddPrefix("2001:db8::/32") + return e + }(), + }, + opts: []IgnoreIPOption{IgnoreIPv4}, + wantLen: 1, + }, + { + name: "add with ignore IPv6", + entries: []*Entry{ + func() *Entry { + e := NewEntry("test1") + e.AddPrefix("192.168.1.0/24") + e.AddPrefix("2001:db8::/32") + return e + }(), + }, + opts: []IgnoreIPOption{IgnoreIPv6}, + wantLen: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := NewContainer() + for _, entry := range tt.entries { + if err := c.Add(entry, tt.opts...); err != nil { + t.Errorf("Container.Add() error = %v", err) + } + } + if c.Len() != tt.wantLen { + t.Errorf("Container.Len() = %d, want %d", c.Len(), tt.wantLen) + } + }) + } +} + +func TestContainer_Remove(t *testing.T) { + tests := []struct { + name string + setupFn func(Container) + removeName string + removeCase CaseRemove + opts []IgnoreIPOption + wantErr bool + checkFn func(*testing.T, Container) + }{ + { + name: "remove non-existent entry", + setupFn: func(c Container) { + e := NewEntry("test1") + e.AddPrefix("192.168.1.0/24") + c.Add(e) + }, + removeName: "nonexistent", + removeCase: CaseRemoveEntry, + wantErr: true, + }, + { + name: "remove entry completely", + setupFn: func(c Container) { + e := NewEntry("test1") + e.AddPrefix("192.168.1.0/24") + c.Add(e) + }, + removeName: "test1", + removeCase: CaseRemoveEntry, + wantErr: false, + checkFn: func(t *testing.T, c Container) { + if c.Len() != 0 { + t.Errorf("Container.Len() = %d, want 0", c.Len()) + } + }, + }, + { + name: "remove prefix", + setupFn: func(c Container) { + e := NewEntry("test1") + e.AddPrefix("192.168.1.0/24") + e.AddPrefix("10.0.0.0/8") + c.Add(e) + }, + removeName: "test1", + removeCase: CaseRemovePrefix, + wantErr: false, + checkFn: func(t *testing.T, c Container) { + if c.Len() != 1 { + t.Errorf("Container.Len() = %d, want 1", c.Len()) + } + }, + }, + { + name: "remove with ignore IPv4", + setupFn: func(c Container) { + e := NewEntry("test1") + e.AddPrefix("192.168.1.0/24") + e.AddPrefix("2001:db8::/32") + c.Add(e) + }, + removeName: "test1", + removeCase: CaseRemoveEntry, + opts: []IgnoreIPOption{IgnoreIPv4}, + wantErr: false, + checkFn: func(t *testing.T, c Container) { + if c.Len() != 1 { + t.Errorf("Container.Len() = %d, want 1 (IPv4 should be removed)", c.Len()) + } + }, + }, + { + name: "remove with ignore IPv6", + setupFn: func(c Container) { + e := NewEntry("test1") + e.AddPrefix("192.168.1.0/24") + e.AddPrefix("2001:db8::/32") + c.Add(e) + }, + removeName: "test1", + removeCase: CaseRemoveEntry, + opts: []IgnoreIPOption{IgnoreIPv6}, + wantErr: false, + checkFn: func(t *testing.T, c Container) { + if c.Len() != 1 { + t.Errorf("Container.Len() = %d, want 1 (IPv6 should be removed)", c.Len()) + } + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := NewContainer() + if tt.setupFn != nil { + tt.setupFn(c) + } + + removeEntry := NewEntry(tt.removeName) + removeEntry.AddPrefix("192.168.1.0/24") + + err := c.Remove(removeEntry, tt.removeCase, tt.opts...) + if (err != nil) != tt.wantErr { + t.Errorf("Container.Remove() error = %v, wantErr %v", err, tt.wantErr) + } + + if tt.checkFn != nil { + tt.checkFn(t, c) + } + }) + } +} + +func TestContainer_Loop(t *testing.T) { + c := NewContainer() + + // Add some entries + for i := 1; i <= 3; i++ { + e := NewEntry("test" + string(rune('0'+i))) + e.AddPrefix("192.168.1.0/24") + c.Add(e) + } + + count := 0 + for entry := range c.Loop() { + if entry == nil { + t.Error("Container.Loop() returned nil entry") + } + count++ + } + + if count != 3 { + t.Errorf("Container.Loop() iterated %d times, want 3", count) + } +} + +func TestContainer_Lookup(t *testing.T) { + c := NewContainer() + + // Setup test data + e1 := NewEntry("CN") + e1.AddPrefix("192.168.1.0/24") + e1.AddPrefix("10.0.0.0/8") + e1.AddPrefix("2001:db8:1::/48") // Add IPv6 for CN + c.Add(e1) + + e2 := NewEntry("US") + e2.AddPrefix("172.16.0.0/12") + e2.AddPrefix("2001:db8::/32") + c.Add(e2) + + tests := []struct { + name string + ipOrCidr string + searchList []string + wantFound bool + wantErr bool + checkFn func(*testing.T, []string) + }{ + { + name: "lookup IPv4 address", + ipOrCidr: "192.168.1.100", + searchList: nil, + wantFound: true, + wantErr: false, + checkFn: func(t *testing.T, results []string) { + if len(results) == 0 { + t.Error("Expected at least one result") + } + }, + }, + { + name: "lookup IPv4 CIDR", + ipOrCidr: "192.168.1.0/24", + searchList: nil, + wantFound: true, + wantErr: false, + }, + { + name: "lookup IPv6 address", + ipOrCidr: "2001:db8::1", + searchList: nil, + wantFound: true, + wantErr: false, + }, + { + name: "lookup IPv6 CIDR", + ipOrCidr: "2001:db8::/32", + searchList: nil, + wantFound: true, + wantErr: false, + }, + { + name: "lookup with search list", + ipOrCidr: "192.168.1.100", + searchList: []string{"CN"}, + wantFound: true, + wantErr: false, + }, + { + name: "lookup not in search list", + ipOrCidr: "172.16.1.1", + searchList: []string{"CN"}, + wantFound: false, + wantErr: false, + }, + { + name: "lookup non-existent IP", + ipOrCidr: "1.1.1.1", + searchList: nil, + wantFound: false, + wantErr: false, + }, + { + name: "invalid IP", + ipOrCidr: "invalid", + searchList: nil, + wantFound: false, + wantErr: true, + }, + { + name: "invalid CIDR", + ipOrCidr: "invalid/24", + searchList: nil, + wantFound: false, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + results, found, err := c.Lookup(tt.ipOrCidr, tt.searchList...) + if (err != nil) != tt.wantErr { + t.Errorf("Container.Lookup() error = %v, wantErr %v", err, tt.wantErr) + return + } + if found != tt.wantFound { + t.Errorf("Container.Lookup() found = %v, wantFound %v", found, tt.wantFound) + } + if tt.checkFn != nil { + tt.checkFn(t, results) + } + }) + } +} + +func TestContainer_RemoveWithPrefixCase(t *testing.T) { + c := NewContainer() + + e1 := NewEntry("test") + e1.AddPrefix("192.168.1.0/24") + e1.AddPrefix("10.0.0.0/8") + e1.AddPrefix("2001:db8::/32") + c.Add(e1) + + // Create entry with prefixes to remove + removeEntry := NewEntry("test") + removeEntry.AddPrefix("192.168.1.0/24") + + // Remove with CaseRemovePrefix and ignore IPv4 + err := c.Remove(removeEntry, CaseRemovePrefix, IgnoreIPv4) + if err != nil { + t.Errorf("Container.Remove() error = %v", err) + } +} + +func TestContainer_InvalidRemoveCase(t *testing.T) { + c := NewContainer() + + e := NewEntry("test") + e.AddPrefix("192.168.1.0/24") + c.Add(e) + + // Try to remove with invalid case + err := c.Remove(e, CaseRemove(99)) + if err == nil { + t.Error("Container.Remove() should return error for invalid case") + } +} + +func TestContainer_AddWithMerging(t *testing.T) { + c := NewContainer() + + // Add first entry + e1 := NewEntry("test") + e1.AddPrefix("192.168.1.0/24") + e1.AddPrefix("2001:db8::/32") + c.Add(e1) + + // Add second entry with same name - should merge + e2 := NewEntry("test") + e2.AddPrefix("10.0.0.0/8") + e2.AddPrefix("2001:db9::/32") + c.Add(e2) + + if c.Len() != 1 { + t.Errorf("Container.Len() = %d, want 1 (entries should merge)", c.Len()) + } + + // Verify merged entry has prefixes from both + entry, found := c.GetEntry("test") + if !found { + t.Fatal("Entry not found after merge") + } + + prefixes, err := entry.MarshalPrefix() + if err != nil { + t.Errorf("MarshalPrefix() error = %v", err) + } + // After merging, we should have at least some prefixes + if len(prefixes) == 0 { + t.Error("Merged entry has no prefixes") + } +} + +func TestContainer_AddWithIgnoreOptions(t *testing.T) { + tests := []struct { + name string + opts []IgnoreIPOption + checkFn func(*testing.T, Container) + }{ + { + name: "add new entry with ignore IPv4", + opts: []IgnoreIPOption{IgnoreIPv4}, + checkFn: func(t *testing.T, c Container) { + entry, _ := c.GetEntry("test2") + // When adding a new entry with ignore IPv4, IPv4 should not be added + _, err := entry.GetIPv4Set() + if err == nil { + t.Error("Expected no IPv4 set for new entry when ignored") + } + }, + }, + { + name: "add new entry with ignore IPv6", + opts: []IgnoreIPOption{IgnoreIPv6}, + checkFn: func(t *testing.T, c Container) { + entry, _ := c.GetEntry("test2") + // When adding a new entry with ignore IPv6, IPv6 should not be added + _, err := entry.GetIPv6Set() + if err == nil { + t.Error("Expected no IPv6 set for new entry when ignored") + } + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := NewContainer() + + e := NewEntry("test2") + e.AddPrefix("192.168.1.0/24") + e.AddPrefix("2001:db8::/32") + c.Add(e, tt.opts...) + + if tt.checkFn != nil { + tt.checkFn(t, c) + } + }) + } +} + +func TestContainer_RemoveWithPrefixAndIgnoreOptions(t *testing.T) { + tests := []struct { + name string + opts []IgnoreIPOption + checkFn func(*testing.T, Container) + }{ + { + name: "remove prefix with ignore IPv4", + opts: []IgnoreIPOption{IgnoreIPv4}, + checkFn: func(t *testing.T, c Container) { + entry, _ := c.GetEntry("test") + // IPv6 should be removed, IPv4 should remain + _, err := entry.GetIPv4Set() + if err != nil { + t.Error("IPv4 should still exist") + } + }, + }, + { + name: "remove prefix with ignore IPv6", + opts: []IgnoreIPOption{IgnoreIPv6}, + checkFn: func(t *testing.T, c Container) { + entry, _ := c.GetEntry("test") + // IPv4 should be removed, IPv6 should remain + _, err := entry.GetIPv6Set() + if err != nil { + t.Error("IPv6 should still exist") + } + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := NewContainer() + + e1 := NewEntry("test") + e1.AddPrefix("192.168.1.0/24") + e1.AddPrefix("2001:db8::/32") + c.Add(e1) + + removeEntry := NewEntry("test") + removeEntry.AddPrefix("192.168.1.0/24") + removeEntry.AddPrefix("2001:db8::/32") + + err := c.Remove(removeEntry, CaseRemovePrefix, tt.opts...) + if err != nil { + t.Errorf("Container.Remove() error = %v", err) + } + + if tt.checkFn != nil { + tt.checkFn(t, c) + } + }) + } +} + +func TestContainer_GetEntryInvalidContainer(t *testing.T) { + // Create an invalid container (nil map) + c := &container{entries: nil} + + _, found := c.GetEntry("test") + if found { + t.Error("GetEntry() should return false for invalid container") + } +} + +func TestContainer_LenInvalidContainer(t *testing.T) { + // Create an invalid container (nil map) + c := &container{entries: nil} + + if c.Len() != 0 { + t.Errorf("Len() = %d, want 0 for invalid container", c.Len()) + } +} + +func TestContainer_AddMergingEdgeCases(t *testing.T) { + c := NewContainer() + + // Add entry with both IPv4 and IPv6 + e1 := NewEntry("test") + e1.AddPrefix("192.168.1.0/24") + e1.AddPrefix("2001:db8::/32") + c.Add(e1) + + // Merge with entry that has only IPv4 - should merge both + e2 := NewEntry("test") + e2.AddPrefix("10.0.0.0/8") + c.Add(e2) + + entry, _ := c.GetEntry("test") + // Should have both IPv4 and IPv6 + _, err4 := entry.GetIPv4Set() + _, err6 := entry.GetIPv6Set() + if err4 != nil || err6 != nil { + t.Error("Both IPv4 and IPv6 should exist after merge") + } + + // Now merge with ignore options on existing entry + e3 := NewEntry("test") + e3.AddPrefix("172.16.0.0/12") + e3.AddPrefix("2001:db9::/32") + c.Add(e3, IgnoreIPv4) + + // IPv4 should still exist, IPv6 should be updated + entry, _ = c.GetEntry("test") + _, err := entry.GetIPv4Set() + if err != nil { + t.Error("IPv4 should still exist when merging with IgnoreIPv4") + } +} + +func TestContainer_AddMergingWithExistingBuilders(t *testing.T) { + c := NewContainer() + + // Test merge when val already has builders (lines 102-109) + e1 := NewEntry("test") + e1.AddPrefix("192.168.1.0/24") + e1.AddPrefix("2001:db8::/32") + c.Add(e1) + + // Merge another entry (default case, both builders exist) + e2 := NewEntry("test") + e2.AddPrefix("10.0.0.0/8") + e2.AddPrefix("2001:db9::/32") + c.Add(e2) // This should hit lines 102-109 + + entry, _ := c.GetEntry("test") + prefixes, _ := entry.MarshalPrefix() + if len(prefixes) == 0 { + t.Error("Should have prefixes after merge") + } +} + +func TestContainer_AddMergingIgnoreIPv6WithExistingBuilder(t *testing.T) { + c := NewContainer() + + // Add entry with IPv4 and IPv6 + e1 := NewEntry("test") + e1.AddPrefix("192.168.1.0/24") + e1.AddPrefix("2001:db8::/32") + c.Add(e1) + + // Merge with IgnoreIPv6 when val already has IPv4 builder (lines 97-100) + e2 := NewEntry("test") + e2.AddPrefix("10.0.0.0/8") + e2.AddPrefix("2001:db9::/32") + c.Add(e2, IgnoreIPv6) + + entry, _ := c.GetEntry("test") + _, err := entry.GetIPv4Set() + if err != nil { + t.Error("IPv4 should exist after merge with IgnoreIPv6") + } +} + +func TestContainer_AddMergingIgnoreIPv4WithExistingBuilder(t *testing.T) { + c := NewContainer() + + // Add entry with IPv4 and IPv6 + e1 := NewEntry("test") + e1.AddPrefix("192.168.1.0/24") + e1.AddPrefix("2001:db8::/32") + c.Add(e1) + + // Merge with IgnoreIPv4 when val already has IPv6 builder (lines 92-95) + e2 := NewEntry("test") + e2.AddPrefix("10.0.0.0/8") + e2.AddPrefix("2001:db9::/32") + c.Add(e2, IgnoreIPv4) + + entry, _ := c.GetEntry("test") + _, err := entry.GetIPv6Set() + if err != nil { + t.Error("IPv6 should exist after merge with IgnoreIPv4") + } +} + +func TestContainer_RemoveNilBuilders(t *testing.T) { + c := NewContainer() + + // Add entry + e1 := NewEntry("test") + e1.AddPrefix("192.168.1.0/24") + c.Add(e1) + + // Try to remove prefixes when entry has no IPv6 builder + removeEntry := NewEntry("test") + removeEntry.AddPrefix("2001:db8::/32") // IPv6 + + err := c.Remove(removeEntry, CaseRemovePrefix) + if err != nil { + t.Errorf("Remove should handle missing builder gracefully, got error: %v", err) + } +} |
