package main import ( "path/filepath" "testing" ) func TestParseLine(t *testing.T) { tests := []struct { name string line string wantZone string wantCats []string wantGrp string wantCOO string wantErr bool }{ { name: "simple single catalog", line: "zone.example.org catalog1", wantZone: "zone.example.org.", wantCats: []string{"catalog1"}, }, { name: "multiple catalogs comma separated", line: "zone.example.org catalog1, catalog2", wantZone: "zone.example.org.", wantCats: []string{"catalog1", "catalog2"}, }, { name: "with group", line: "test.example.net catalog1, group=internal", wantZone: "test.example.net.", wantCats: []string{"catalog1"}, wantGrp: "internal", }, { name: "with coo", line: "zone.example.com catalog2, coo=old-catalog.example.com.", wantZone: "zone.example.com.", wantCats: []string{"catalog2"}, wantCOO: "old-catalog.example.com.", }, { name: "with group and coo", line: "app.example.org catalog1, group=external, coo=migrated.example.com.", wantZone: "app.example.org.", wantCats: []string{"catalog1"}, wantGrp: "external", wantCOO: "migrated.example.com.", }, { name: "trailing dot on zone", line: "zone.example.org. catalog1", wantZone: "zone.example.org.", wantCats: []string{"catalog1"}, }, { name: "no catalog", line: "zone.example.org", wantErr: true, }, { name: "only properties no catalog", line: "zone.example.org group=foo", wantErr: true, }, { name: "unknown property", line: "zone.example.org catalog1, foo=bar", wantErr: true, }, { name: "empty group value", line: "zone.example.org catalog1, group=", wantErr: true, }, { name: "empty coo value", line: "zone.example.org catalog1, coo=", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { entry, err := parseLine(tt.line, "test.txt", 1) if tt.wantErr { if err == nil { t.Fatal("expected error, got nil") } return } if err != nil { t.Fatalf("unexpected error: %v", err) } if entry.Zone != tt.wantZone { t.Errorf("zone = %q, want %q", entry.Zone, tt.wantZone) } if len(entry.Catalogs) != len(tt.wantCats) { t.Fatalf("catalogs = %v, want %v", entry.Catalogs, tt.wantCats) } for i, cat := range entry.Catalogs { if cat != tt.wantCats[i] { t.Errorf("catalog[%d] = %q, want %q", i, cat, tt.wantCats[i]) } } if entry.Group != tt.wantGrp { t.Errorf("group = %q, want %q", entry.Group, tt.wantGrp) } if entry.COO != tt.wantCOO { t.Errorf("coo = %q, want %q", entry.COO, tt.wantCOO) } }) } } func TestTokenize(t *testing.T) { tests := []struct { line string want []string }{ {"zone.example.org catalog1, catalog2", []string{"zone.example.org", "catalog1", "catalog2"}}, {"zone.example.org catalog1,catalog2", []string{"zone.example.org", "catalog1", "catalog2"}}, {"zone.example.org catalog1 , catalog2", []string{"zone.example.org", "catalog1", "catalog2"}}, {"zone.example.org\tcatalog1", []string{"zone.example.org", "catalog1"}}, } for _, tt := range tests { got := tokenize(tt.line) if len(got) != len(tt.want) { t.Errorf("tokenize(%q) = %v, want %v", tt.line, got, tt.want) continue } for i := range got { if got[i] != tt.want[i] { t.Errorf("tokenize(%q)[%d] = %q, want %q", tt.line, i, got[i], tt.want[i]) } } } } func TestBuildCatalogMembers(t *testing.T) { cfg := &Config{ Catalogs: map[string]CatalogConfig{ "catalog1": {Zone: "catalog1.example.com."}, "catalog2": {Zone: "catalog2.example.com."}, }, } t.Run("valid input", func(t *testing.T) { entries := []ZoneEntry{ {Zone: "a.example.com.", Catalogs: []string{"catalog1"}, File: "test", Line: 1}, {Zone: "b.example.com.", Catalogs: []string{"catalog1", "catalog2"}, File: "test", Line: 2}, } members, err := buildCatalogMembers(entries, cfg) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(members["catalog1"]) != 2 { t.Errorf("catalog1 members = %d, want 2", len(members["catalog1"])) } if len(members["catalog2"]) != 1 { t.Errorf("catalog2 members = %d, want 1", len(members["catalog2"])) } }) t.Run("unknown catalog", func(t *testing.T) { entries := []ZoneEntry{ {Zone: "a.example.com.", Catalogs: []string{"unknown"}, File: "test", Line: 1}, } _, err := buildCatalogMembers(entries, cfg) if err == nil { t.Fatal("expected error for unknown catalog") } }) t.Run("duplicate zone in same catalog", func(t *testing.T) { entries := []ZoneEntry{ {Zone: "a.example.com.", Catalogs: []string{"catalog1"}, File: "test", Line: 1}, {Zone: "a.example.com.", Catalogs: []string{"catalog1"}, File: "test", Line: 2}, } _, err := buildCatalogMembers(entries, cfg) if err == nil { t.Fatal("expected error for duplicate zone") } }) } func TestParseInput(t *testing.T) { cfg := &Config{ Catalogs: map[string]CatalogConfig{ "catalog1": {Zone: "catalog1.example.com."}, "catalog2": {Zone: "catalog2.example.com."}, }, } dir := t.TempDir() inputPath := filepath.Join(dir, "zones.txt") content := `# Comment zone.example.org catalog1, catalog2 test.example.net catalog1, group=internal ` writeTestFile(t, dir, "zones.txt", content) members, err := parseInput(inputPath, cfg) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(members["catalog1"]) != 2 { t.Errorf("catalog1 members = %d, want 2", len(members["catalog1"])) } if len(members["catalog2"]) != 1 { t.Errorf("catalog2 members = %d, want 1", len(members["catalog2"])) } } func TestParseInputErrors(t *testing.T) { cfg := &Config{ Catalogs: map[string]CatalogConfig{ "catalog1": {Zone: "catalog1.example.com."}, }, } t.Run("missing file", func(t *testing.T) { _, err := parseInput("/nonexistent/zones.txt", cfg) if err == nil { t.Fatal("expected error for missing file") } }) t.Run("invalid line in input", func(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "zones.txt") writeTestFile(t, dir, "zones.txt", "zone-with-no-catalog\n") _, err := parseInput(path, cfg) if err == nil { t.Fatal("expected error for invalid line") } }) } func TestBuildCatalogMembersSameZoneDifferentCatalogs(t *testing.T) { cfg := &Config{ Catalogs: map[string]CatalogConfig{ "catalog1": {Zone: "catalog1.example.com."}, "catalog2": {Zone: "catalog2.example.com."}, }, } // Same zone in different catalogs is OK entries := []ZoneEntry{ {Zone: "a.example.com.", Catalogs: []string{"catalog1", "catalog2"}, File: "test", Line: 1}, } members, err := buildCatalogMembers(entries, cfg) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(members["catalog1"]) != 1 { t.Errorf("catalog1 members = %d, want 1", len(members["catalog1"])) } if len(members["catalog2"]) != 1 { t.Errorf("catalog2 members = %d, want 1", len(members["catalog2"])) } }