From a7dc4534d73a06af6c7deccde5374dfb1bc240da Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 9 Nov 2026 20:59:46 +0100 Subject: [PATCH] Hold the resolver's configuration guards in the dnsmasq module's tests (issue 1966) Moved from mesh-sdk's catalogue tests, which read a literal the file no longer has. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../cmd/dnsmasq-tools/templates_test.go | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/modules/dnsmasq/cmd/dnsmasq-tools/templates_test.go b/modules/dnsmasq/cmd/dnsmasq-tools/templates_test.go index 45edf18c2..3bc7a03e7 100644 --- a/modules/dnsmasq/cmd/dnsmasq-tools/templates_test.go +++ b/modules/dnsmasq/cmd/dnsmasq-tools/templates_test.go @@ -162,3 +162,43 @@ func TestUpstreamsAreConfigValues(t *testing.T) { t.Error("dnsmasq.conf lost no-resolv") } } + +// The resolver's configuration keeps what made every earlier resolver of the mesh go wrong (moved from mesh-sdk's +// catalogue tests, which could not follow the file when its upstreams became settings): it answers on the private +// address and loopback only, by address and never by interface (novox/hq issue 110), binds once and never +// dynamically (issue 348), reads no hosts file and no operator's files (ADR 0199), and takes none of the addresses +// systemd-resolved holds. no-resolv and the two server lines are held by TestUpstreamsAreConfigValues. +func TestTheResolverConfigurationKeepsItsGuards(t *testing.T) { + raw, err := os.ReadFile(filepath.Join(moduleDir, "files", "dnsmasq.conf")) + if err != nil { + t.Fatal(err) + } + config := "\n" + string(raw) + for _, want := range []string{ + "\nlisten-address=127.0.0.1\n", "\nlisten-address=${machine:address}\n", + "\nno-hosts\n", "\nconf-file=/etc/mesh-resolver/zones.conf\n", "\nconf-file=/etc/mesh-resolver/nodes.conf\n", + "\ndomain-needed\n", "\nbogus-priv\n", "\nbind-interfaces\n", + } { + if !strings.Contains(config, want) { + t.Errorf("the resolver's configuration lacks %q", strings.TrimSpace(want)) + } + } + for _, line := range strings.Split(config, "\n") { + for _, never := range []string{"bind-dynamic", "interface=", "conf-dir=", "addn-hosts=", "listen-address=${setting:", + "listen-address=127.0.0.53", "listen-address=127.0.0.54", "listen-address=127.0.0.55"} { + if strings.HasPrefix(line, never) { + t.Errorf("the resolver's configuration says %q", line) + } + } + } + // The zone and node files the configuration reads are the ones the manifest's facts place. + manifest, err := os.ReadFile(filepath.Join(moduleDir, "module.json")) + if err != nil { + t.Fatal(err) + } + for _, p := range []string{"/etc/mesh-resolver/zones.conf", "/etc/mesh-resolver/nodes.conf"} { + if !strings.Contains(string(manifest), `"path": "`+p+`"`) { + t.Errorf("the manifest places no file at %s, which the configuration reads", p) + } + } +}