Generate RFC 9432 DNS catalog zone files from a declarative input file. Parses zone-to-catalog assignments with optional group and coo properties, produces deterministic BIND-format output with automatic SOA serial management and change detection.
142 lines
3.8 KiB
Go
142 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestLoadConfig(t *testing.T) {
|
|
t.Run("valid config", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
writeTestFile(t, dir, "catz.yaml", `catalogs:
|
|
cat1:
|
|
zone: catalog1.example.com.
|
|
soa:
|
|
mname: ns1.example.com.
|
|
rname: hostmaster.example.com.
|
|
`)
|
|
cfg, err := loadConfig(filepath.Join(dir, "catz.yaml"))
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg.Catalogs["cat1"].Zone != "catalog1.example.com." {
|
|
t.Errorf("zone = %q, want %q", cfg.Catalogs["cat1"].Zone, "catalog1.example.com.")
|
|
}
|
|
})
|
|
|
|
t.Run("normalizes FQDNs", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
writeTestFile(t, dir, "catz.yaml", `catalogs:
|
|
cat1:
|
|
zone: Catalog1.Example.COM
|
|
soa:
|
|
mname: NS1.Example.COM
|
|
rname: Hostmaster.Example.COM
|
|
`)
|
|
cfg, err := loadConfig(filepath.Join(dir, "catz.yaml"))
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg.Catalogs["cat1"].Zone != "catalog1.example.com." {
|
|
t.Errorf("zone = %q, want normalized FQDN", cfg.Catalogs["cat1"].Zone)
|
|
}
|
|
if cfg.SOA.Mname != "ns1.example.com." {
|
|
t.Errorf("mname = %q, want normalized FQDN", cfg.SOA.Mname)
|
|
}
|
|
})
|
|
|
|
t.Run("missing file", func(t *testing.T) {
|
|
_, err := loadConfig("/nonexistent/catz.yaml")
|
|
if err == nil {
|
|
t.Fatal("expected error for missing file")
|
|
}
|
|
})
|
|
|
|
t.Run("invalid YAML", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
writeTestFile(t, dir, "catz.yaml", "{{invalid yaml")
|
|
_, err := loadConfig(filepath.Join(dir, "catz.yaml"))
|
|
if err == nil {
|
|
t.Fatal("expected error for invalid YAML")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestValidateConfig(t *testing.T) {
|
|
t.Run("no catalogs", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Catalogs: map[string]CatalogConfig{},
|
|
SOA: SOAConfig{Mname: "ns1.example.com.", Rname: "hostmaster.example.com."},
|
|
}
|
|
if err := validateConfig(cfg); err == nil {
|
|
t.Fatal("expected error for no catalogs")
|
|
}
|
|
})
|
|
|
|
t.Run("nil catalogs", func(t *testing.T) {
|
|
cfg := &Config{
|
|
SOA: SOAConfig{Mname: "ns1.example.com.", Rname: "hostmaster.example.com."},
|
|
}
|
|
if err := validateConfig(cfg); err == nil {
|
|
t.Fatal("expected error for nil catalogs")
|
|
}
|
|
})
|
|
|
|
t.Run("empty zone", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Catalogs: map[string]CatalogConfig{"cat1": {Zone: ""}},
|
|
SOA: SOAConfig{Mname: "ns1.example.com.", Rname: "hostmaster.example.com."},
|
|
}
|
|
if err := validateConfig(cfg); err == nil {
|
|
t.Fatal("expected error for empty zone")
|
|
}
|
|
})
|
|
|
|
t.Run("missing mname", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Catalogs: map[string]CatalogConfig{"cat1": {Zone: "cat.example.com."}},
|
|
SOA: SOAConfig{Rname: "hostmaster.example.com."},
|
|
}
|
|
if err := validateConfig(cfg); err == nil {
|
|
t.Fatal("expected error for missing mname")
|
|
}
|
|
})
|
|
|
|
t.Run("missing rname", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Catalogs: map[string]CatalogConfig{"cat1": {Zone: "cat.example.com."}},
|
|
SOA: SOAConfig{Mname: "ns1.example.com."},
|
|
}
|
|
if err := validateConfig(cfg); err == nil {
|
|
t.Fatal("expected error for missing rname")
|
|
}
|
|
})
|
|
|
|
t.Run("valid config", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Catalogs: map[string]CatalogConfig{"cat1": {Zone: "cat.example.com."}},
|
|
SOA: SOAConfig{Mname: "ns1.example.com.", Rname: "hostmaster.example.com."},
|
|
}
|
|
if err := validateConfig(cfg); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestLoadConfigWritePermission(t *testing.T) {
|
|
// Test that loadConfig returns error for unreadable file
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "catz.yaml")
|
|
writeTestFile(t, dir, "catz.yaml", "catalogs: {}")
|
|
if err := os.Chmod(path, 0o000); err != nil {
|
|
t.Skip("cannot change file permissions")
|
|
}
|
|
defer os.Chmod(path, 0o644)
|
|
|
|
_, err := loadConfig(path)
|
|
if err == nil {
|
|
t.Fatal("expected error for unreadable file")
|
|
}
|
|
}
|