Files
dnswatcher/internal/watcher/export_test.go
T
sneak dd052ec7f7
check / check (push) Waiting to run
watcher: a hostname whose zone's nameservers all stop answering gets NS Failure (closes #284)
When none of the nameservers of a hostname's zone answered, the walk to
them failed and the check was only logged. The walk now returns a
NoAnswerError naming the nameservers of the zone it reached through a
referral when none of them answers. When those are the nameservers the
previous check saved for the name, each is saved as failed with the
reason, so it gets an NS Failure and later an NS Recovery. A domain with
no delegation of its own, whose NS lookup is that same walk, gets the
same. A walk that fails before that zone, as when the host loses its
network, is still only logged.

Model: opus-5-5
2026-10-13 21:57:47 +00:00

161 lines
3.9 KiB
Go

package watcher
import (
"context"
"log/slog"
"time"
"sneak.berlin/go/dnswatcher/internal/config"
"sneak.berlin/go/dnswatcher/internal/resolver"
"sneak.berlin/go/dnswatcher/internal/state"
)
// NewForTest creates a Watcher without fx for unit testing. A nil cfg
// is an empty configuration.
func NewForTest(
cfg *config.Config,
st *state.State,
res DNSResolver,
pc PortChecker,
tc TLSChecker,
n Notifier,
) *Watcher {
if cfg == nil {
cfg = &config.Config{}
}
return &Watcher{
log: slog.Default(),
config: cfg,
state: st,
resolver: res,
portCheck: pc,
tlsCheck: tc,
notify: n,
firstRun: true,
}
}
// SetLogger replaces the watcher's logger, so a test can read what it
// logs.
func (w *Watcher) SetLogger(log *slog.Logger) {
w.log = log
}
// NewlyDisagreeingPairs exports newlyDisagreeingPairs for testing.
func NewlyDisagreeingPairs(
prev, current *state.HostnameState,
) [][2]string {
return newlyDisagreeingPairs(prev, current)
}
// SetFirstRun sets whether the watcher is on its first check, in which
// nothing is compared with the previous check. NewForTest's watcher is.
func (w *Watcher) SetFirstRun(firstRun bool) {
w.firstRun = firstRun
}
// UpdateHostnameState exports updateHostnameState for testing.
func (w *Watcher) UpdateHostnameState(
ctx context.Context,
hostname string,
newState *state.HostnameState,
) {
w.updateHostnameState(ctx, hostname, newState)
}
// DetectHostnameChanges exports detectHostnameChanges for testing.
func (w *Watcher) DetectHostnameChanges(
ctx context.Context,
hostname string,
prev, current *state.HostnameState,
) {
w.detectHostnameChanges(ctx, hostname, prev, current)
}
// ResolveNameserverAddresses exports resolveNameserverAddresses for
// testing.
func (w *Watcher) ResolveNameserverAddresses(
ctx context.Context,
nameservers []string,
prev map[string][]string,
) map[string][]string {
return w.resolveNameserverAddresses(ctx, nameservers, prev)
}
// ResolveCNAMEAddresses exports resolveCNAMEAddresses for testing.
func (w *Watcher) ResolveCNAMEAddresses(
ctx context.Context,
hostname string,
current, prev *state.HostnameState,
) {
w.resolveCNAMEAddresses(ctx, hostname, current, prev)
}
// DetectNSAddressChanges exports detectNSAddressChanges for testing.
func (w *Watcher) DetectNSAddressChanges(
ctx context.Context,
domain string,
prev, current map[string][]string,
) {
w.detectNSAddressChanges(ctx, domain, prev, current)
}
// MaybeSendTestNotification exports maybeSendTestNotification for
// testing.
func (w *Watcher) MaybeSendTestNotification(ctx context.Context) {
w.maybeSendTestNotification(ctx)
}
// CleanupRemovedTargets exports cleanupRemovedTargets for testing.
func (w *Watcher) CleanupRemovedTargets() {
w.cleanupRemovedTargets()
}
// CheckAllPorts exports checkAllPorts for testing.
func (w *Watcher) CheckAllPorts(ctx context.Context) {
w.checkAllPorts(ctx)
}
// RunTLSChecks exports runTLSChecks for testing.
func (w *Watcher) RunTLSChecks(ctx context.Context) {
w.runTLSChecks(ctx)
}
// CheckHostnameResult exports checkHostnameResult for testing.
func (w *Watcher) CheckHostnameResult(
ctx context.Context,
hostname string,
results map[string]*resolver.NameserverResponse,
err error,
) bool {
return w.checkHostnameResult(ctx, hostname, results, err)
}
// CheckDomainResult exports checkDomainResult for testing.
func (w *Watcher) CheckDomainResult(
ctx context.Context,
domain string,
nameservers []string,
err error,
) {
w.checkDomainResult(ctx, domain, nameservers, err)
}
// FailedNameservers exports failedNameservers for testing.
func FailedNameservers(
err error,
prev *state.HostnameState,
) map[string]*resolver.NameserverResponse {
return failedNameservers(err, prev)
}
// BuildHostnameState exports buildHostnameState for testing.
func BuildHostnameState(
results map[string]*resolver.NameserverResponse,
prev *state.HostnameState,
now time.Time,
) *state.HostnameState {
return buildHostnameState(results, prev, now)
}