watcher: Port Change notifications list domains apart from hostnames (closes #248)
check / check (push) Successful in 1m17s

A Port Change notification's `Hosts:` line listed a port's apex domains
and hostnames together. It now has a `Domains:` line and a `Hostnames:`
line, and leaves out one that would name nothing. A name is a domain
when it is a configured domain, the rule record notifications already
use to start `Domain:` or `Hostname:`; that rule is now one method,
isDomain, which both use. The port entries saved in the state are
unchanged. README describes the new lines.

Model: opus-5-5
This commit is contained in:
2026-10-02 08:36:35 +00:00
parent b43402a631
commit 5388a11265
4 changed files with 91 additions and 6 deletions
+48
View File
@@ -165,3 +165,51 @@ func TestStartupNotificationCountsConfiguredNames(t *testing.T) {
t.Errorf("sent %v, want one message with %q", notifications, counts)
}
}
// A Port Change notification lists the configured apex domain and the
// hostname that resolve to the port's address on separate lines. The
// port checks read the saved hostname state and look nothing up, so the
// watcher has no resolver.
func TestPortChangeListsDomainsApartFromHostnames(t *testing.T) {
t.Parallel()
cfg := defaultTestConfig(t)
cfg.Domains = []string{domain}
cfg.Hostnames = []string{host}
deps := newTestDeps(t, cfg)
w := watcher.NewForTest(
cfg, deps.state, nil,
deps.portChecker, deps.tlsChecker, deps.notifier,
)
w.SetFirstRun(false)
// Both names resolve to ip1, whose port 443 the previous check
// found open. It is closed now.
for _, name := range []string{domain, host} {
deps.state.SetHostnameState(name, saved(
map[string]*state.NameserverRecordState{
nsA: answered(map[string][]string{"A": {ip1}}),
},
))
}
key := ip1 + ":443"
deps.state.SetPortState(key, &state.PortState{
Open: true, Hostnames: []string{domain, host},
})
deps.portChecker.closed = true
w.CheckAllPorts(t.Context())
title := "Port Change: " + key
want := `Domains: example.net
Hostnames: www.example.net
Address: 192.0.2.1:443
Port now closed`
got := deps.notifier.getNotifications()
if len(got) != 1 || got[0].Title != title || got[0].Message != want {
t.Errorf("sent %v, want one %q with message:\n%s", got, title, want)
}
}