82 lines
2.8 KiB
Go
82 lines
2.8 KiB
Go
package zonefile
|
|
|
|
import (
|
|
"path/filepath"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// nameTypeRdata renders every record as "name TYPE rdata", skipping the line
|
|
// number and the TTL. The shipped examples are edited by hand, so pinning the
|
|
// exact record set is enough; pinning line numbers would make a comment change
|
|
// look like a parser bug.
|
|
func nameTypeRdata(t *testing.T, z *Zone) []string {
|
|
t.Helper()
|
|
out := make([]string, 0, len(z.Records))
|
|
for _, rec := range z.Records {
|
|
parts := strings.SplitN(rec.RR.String(), "\t", 5)
|
|
if len(parts) != 5 {
|
|
t.Fatalf("record on line %d does not render as 5 tab-separated fields: %q",
|
|
rec.Line, rec.Text())
|
|
}
|
|
out = append(out, parts[0]+" "+parts[3]+" "+parts[4])
|
|
}
|
|
return out
|
|
}
|
|
|
|
// TestExampleLocalZoneIsOriginIndependent parses deploy/local.example.zone the
|
|
// way the product parses it — with the origin taken from the file name, which
|
|
// for local.example.zone is "local." — and pins that every name in the example
|
|
// is fully qualified anyway.
|
|
//
|
|
// This is not a style test. The origin a master file is read with is its base
|
|
// name, so a relative owner name silently becomes <name>.local. and the zone
|
|
// answers for names nobody asked about. The example is what an operator copies,
|
|
// and it used to mix relative owners with absolute ones: a file that looked
|
|
// right and matched nothing.
|
|
func TestExampleLocalZoneIsOriginIndependent(t *testing.T) {
|
|
path := filepath.Join("..", "..", "deploy", "local.example.zone")
|
|
if got, want := OriginFromPath(path), "local."; got != want {
|
|
t.Fatalf("OriginFromPath(%q) = %q, want %q — the example depends on this origin",
|
|
path, got, want)
|
|
}
|
|
|
|
z, err := ParseFile(path, OriginFromPath(path))
|
|
if err != nil {
|
|
t.Fatalf("ParseFile(%s): %v", path, err)
|
|
}
|
|
got := nameTypeRdata(t, z)
|
|
want := []string{
|
|
"lan. SOA ns1.lan. hostmaster.lan. 2024010101 3600 600 604800 300",
|
|
"lan. NS ns1.lan.",
|
|
"lan. NS ns2.lan.",
|
|
"ns1.lan. A 192.168.1.2",
|
|
"ns2.lan. A 192.168.1.3",
|
|
"router.lan. A 192.168.1.1",
|
|
"nas.lan. A 192.168.1.10",
|
|
"nas.lan. AAAA fd00::10",
|
|
"*.lan. A 192.168.1.50",
|
|
"*.internal.lan. A 10.0.0.50",
|
|
"_ntp._udp.lan. SRV 0 0 123 ns1.lan.",
|
|
"_ldap._tcp.lan. SRV 10 0 389 nas.lan.",
|
|
`_dmarc._domain.lan. TXT "v=DMARC1; p=reject; rua=mailto:dmarc.lan"`,
|
|
`_lan. TXT "internal network, do not use on the open internet"`,
|
|
}
|
|
if !slices.Equal(got, want) {
|
|
t.Errorf("records mismatch\n got: %q\nwant: %q", got, want)
|
|
}
|
|
if z.DefaultTTL != 300 {
|
|
t.Errorf("DefaultTTL = %d, want the $TTL 300", z.DefaultTTL)
|
|
}
|
|
for _, rec := range z.Records {
|
|
if !strings.HasSuffix(rec.Name, ".") {
|
|
t.Errorf("record %q is not fully qualified; the example is read with the "+
|
|
"origin %q derived from the file name", rec.Name, z.Origin)
|
|
}
|
|
}
|
|
if z.SOA == nil || z.SOA.Serial != 2024010101 {
|
|
t.Errorf("SOA = %v, want the serial 2024010101", z.SOA)
|
|
}
|
|
}
|