Files
catz/input_test.go
Ask Bjørn Hansen 0a460b975d Clean up code reuse, consistency, and efficiency issues
Merge readExistingSerial and readExistingContent into a single
readExisting function to eliminate duplicate file I/O. Extract dateBase
helper to deduplicate serial formula between defaultSerial and
bumpSerial. Cache hash results during collision check to avoid
recomputing per member. Normalize error prefixes (remove "error:" from
fmt.Errorf, add uniformly at print sites). Use filepath.Join instead of
manual "/" concatenation. Replace trivial containsStr wrapper with
strings.Contains. Simplify tokenize to a single return. Use writeTestFile
and fixedTime helpers consistently in tests.
2026-03-01 17:38:26 -08:00

273 lines
7.0 KiB
Go

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"]))
}
}