Generate a BIND-format domains.conf file alongside catalog zones. New input properties: file= (zone data path) and dnssec (bare flag). When --bind-conf is set, every zone must have file= or it errors. Renames ZoneEntry.File to ZonesFile (input path for error messages) and adds ZoneFile (BIND file path) and DNSSEC (bool) fields.
313 lines
8.1 KiB
Go
313 lines
8.1 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
|
|
wantFile string
|
|
wantDNS bool
|
|
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: "with file property",
|
|
line: "zone.example.org catalog1, file=data/zones/example.org",
|
|
wantZone: "zone.example.org.",
|
|
wantCats: []string{"catalog1"},
|
|
wantFile: "data/zones/example.org",
|
|
},
|
|
{
|
|
name: "with dnssec flag",
|
|
line: "zone.example.org catalog1, dnssec",
|
|
wantZone: "zone.example.org.",
|
|
wantCats: []string{"catalog1"},
|
|
wantDNS: true,
|
|
},
|
|
{
|
|
name: "with file and dnssec",
|
|
line: "zone.example.org catalog1, file=data/zones/example.org, dnssec",
|
|
wantZone: "zone.example.org.",
|
|
wantCats: []string{"catalog1"},
|
|
wantFile: "data/zones/example.org",
|
|
wantDNS: true,
|
|
},
|
|
{
|
|
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,
|
|
},
|
|
{
|
|
name: "empty file value",
|
|
line: "zone.example.org catalog1, file=",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "dnssec with equals",
|
|
line: "zone.example.org catalog1, dnssec=yes",
|
|
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)
|
|
}
|
|
if entry.ZoneFile != tt.wantFile {
|
|
t.Errorf("zonefile = %q, want %q", entry.ZoneFile, tt.wantFile)
|
|
}
|
|
if entry.DNSSEC != tt.wantDNS {
|
|
t.Errorf("dnssec = %v, want %v", entry.DNSSEC, tt.wantDNS)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
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"}, ZonesFile: "test", Line: 1},
|
|
{Zone: "b.example.com.", Catalogs: []string{"catalog1", "catalog2"}, ZonesFile: "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"}, ZonesFile: "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"}, ZonesFile: "test", Line: 1},
|
|
{Zone: "a.example.com.", Catalogs: []string{"catalog1"}, ZonesFile: "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"}, ZonesFile: "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"]))
|
|
}
|
|
}
|