watcher tests: far fewer live queries, longer live attempts (closes #214)
check / check (push) Successful in 1m34s
check / check (push) Successful in 1m34s
A domain check looked up each nameserver's addresses by asking every nameserver of that name's zone for all eight record types; it now asks only for A, AAAA and CNAME, the ones it reads. The watcher tests now check example.org and desec.io, whose nameservers are in zones with two nameservers, not cloudflare.com and example.com, whose nameserver addresses are looked up at cloudflare.com's five. The record change and NS failure tests start from saved state built on one NS lookup instead of a first full check, and the port change test runs only the port checks again. A live test attempt may take 18 seconds, not 8. A new live test checks that a nameserver's addresses include IPv4 and IPv6. Model: opus-5-5
This commit is contained in:
@@ -19,6 +19,8 @@ trial run of the finished image: https://git.eeqj.de/sneak/dnswatcher/issues/149
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-10-02: watcher tests send far fewer queries and a live attempt may take
|
||||||
|
18s; nameserver addresses are asked only for A, AAAA, CNAME (closes #214).
|
||||||
- 2026-10-02: the resolver tries root servers, and every other server list it
|
- 2026-10-02: the resolver tries root servers, and every other server list it
|
||||||
walks, in a random order each time, not always from the top (closes #138).
|
walks, in a random order each time, not always from the top (closes #138).
|
||||||
- 2026-10-02: a name listed more than once in `DNSWATCHER_TARGETS`, in any
|
- 2026-10-02: a name listed more than once in `DNSWATCHER_TARGETS`, in any
|
||||||
|
|||||||
@@ -36,11 +36,18 @@ const (
|
|||||||
// before the test fails.
|
// before the test fails.
|
||||||
attempts = 3
|
attempts = 3
|
||||||
|
|
||||||
// AttemptTimeout bounds one attempt. Worst case for an operation
|
// AttemptTimeout bounds one attempt. It must fit the longest
|
||||||
// is attempts * AttemptTimeout plus the backoff — about 26
|
// operation, a watcher check, which sends over a hundred queries one
|
||||||
// seconds, well inside the 90-second `go test -timeout` backstop
|
// after another and on a slow build host takes several times as long
|
||||||
// even when several operations exhaust their attempts.
|
// as the few seconds it takes on a fast one. An operation whose
|
||||||
AttemptTimeout = 8 * time.Second
|
// every attempt fails takes attempts * AttemptTimeout plus the
|
||||||
|
// backoff, about 56 seconds, after it waits for one of the
|
||||||
|
// Concurrency slots that every live operation in the test binary
|
||||||
|
// shares. So when live DNS does not answer at all, a test binary
|
||||||
|
// with more live operations than slots runs into the 90-second
|
||||||
|
// `go test -timeout` backstop instead of each test failing on its
|
||||||
|
// own.
|
||||||
|
AttemptTimeout = 18 * time.Second
|
||||||
|
|
||||||
// backoffBase is the delay after the first failed attempt; it is
|
// backoffBase is the delay after the first failed attempt; it is
|
||||||
// multiplied by backoffFactor each time.
|
// multiplied by backoffFactor each time.
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func (r *Resolver) QueryEachNS(
|
|||||||
nameservers []string,
|
nameservers []string,
|
||||||
hostname string,
|
hostname string,
|
||||||
) (map[string]*NameserverResponse, error) {
|
) (map[string]*NameserverResponse, error) {
|
||||||
return r.queryEachNS(ctx, nameservers, hostname)
|
return r.queryEachNS(ctx, nameservers, hostname, recordTypes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveNSIPs exports resolveNSIPs for testing.
|
// ResolveNSIPs exports resolveNSIPs for testing.
|
||||||
|
|||||||
@@ -533,12 +533,39 @@ func (r *Resolver) FindAuthoritativeNameservers(
|
|||||||
return nil, ErrNoNameservers
|
return nil, ErrNoNameservers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// recordTypes returns the record types a nameserver is asked for when a
|
||||||
|
// name is checked.
|
||||||
|
func recordTypes() []uint16 {
|
||||||
|
return []uint16{
|
||||||
|
dns.TypeA, dns.TypeAAAA, dns.TypeCNAME,
|
||||||
|
dns.TypeMX, dns.TypeTXT, dns.TypeSRV,
|
||||||
|
dns.TypeCAA, dns.TypeNS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addressTypes returns the record types ResolveIPAddresses asks for,
|
||||||
|
// the only ones it reads.
|
||||||
|
func addressTypes() []uint16 {
|
||||||
|
return []uint16{dns.TypeA, dns.TypeAAAA, dns.TypeCNAME}
|
||||||
|
}
|
||||||
|
|
||||||
// QueryNameserver queries a specific nameserver for all record
|
// QueryNameserver queries a specific nameserver for all record
|
||||||
// types and builds a NameserverResponse.
|
// types and builds a NameserverResponse.
|
||||||
func (r *Resolver) QueryNameserver(
|
func (r *Resolver) QueryNameserver(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
nsHostname string,
|
nsHostname string,
|
||||||
hostname string,
|
hostname string,
|
||||||
|
) (*NameserverResponse, error) {
|
||||||
|
return r.queryNameserver(ctx, nsHostname, hostname, recordTypes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryNameserver queries a specific nameserver for the record types
|
||||||
|
// in qtypes and builds a NameserverResponse.
|
||||||
|
func (r *Resolver) queryNameserver(
|
||||||
|
ctx context.Context,
|
||||||
|
nsHostname string,
|
||||||
|
hostname string,
|
||||||
|
qtypes []uint16,
|
||||||
) (*NameserverResponse, error) {
|
) (*NameserverResponse, error) {
|
||||||
if checkCtx(ctx) != nil {
|
if checkCtx(ctx) != nil {
|
||||||
return nil, ErrContextCanceled
|
return nil, ErrContextCanceled
|
||||||
@@ -551,7 +578,7 @@ func (r *Resolver) QueryNameserver(
|
|||||||
|
|
||||||
hostname = dns.Fqdn(hostname)
|
hostname = dns.Fqdn(hostname)
|
||||||
|
|
||||||
return r.queryAllTypes(ctx, nsHostname, nsIPs[0], hostname)
|
return r.queryTypes(ctx, nsHostname, nsIPs[0], hostname, qtypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryNameserverIP queries a nameserver by its IP address directly,
|
// QueryNameserverIP queries a nameserver by its IP address directly,
|
||||||
@@ -568,14 +595,15 @@ func (r *Resolver) QueryNameserverIP(
|
|||||||
|
|
||||||
hostname = dns.Fqdn(hostname)
|
hostname = dns.Fqdn(hostname)
|
||||||
|
|
||||||
return r.queryAllTypes(ctx, nsHostname, nsIP, hostname)
|
return r.queryTypes(ctx, nsHostname, nsIP, hostname, recordTypes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) queryAllTypes(
|
func (r *Resolver) queryTypes(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
nsHostname string,
|
nsHostname string,
|
||||||
nsIP string,
|
nsIP string,
|
||||||
hostname string,
|
hostname string,
|
||||||
|
qtypes []uint16,
|
||||||
) (*NameserverResponse, error) {
|
) (*NameserverResponse, error) {
|
||||||
resp := &NameserverResponse{
|
resp := &NameserverResponse{
|
||||||
Nameserver: nsHostname,
|
Nameserver: nsHostname,
|
||||||
@@ -583,12 +611,6 @@ func (r *Resolver) queryAllTypes(
|
|||||||
Status: StatusOK,
|
Status: StatusOK,
|
||||||
}
|
}
|
||||||
|
|
||||||
qtypes := []uint16{
|
|
||||||
dns.TypeA, dns.TypeAAAA, dns.TypeCNAME,
|
|
||||||
dns.TypeMX, dns.TypeTXT, dns.TypeSRV,
|
|
||||||
dns.TypeCAA, dns.TypeNS,
|
|
||||||
}
|
|
||||||
|
|
||||||
state := r.queryEachType(ctx, nsIP, hostname, qtypes, resp)
|
state := r.queryEachType(ctx, nsIP, hostname, qtypes, resp)
|
||||||
classifyResponse(resp, state)
|
classifyResponse(resp, state)
|
||||||
|
|
||||||
@@ -779,18 +801,19 @@ func (r *Resolver) QueryAllNameservers(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.queryEachNS(ctx, nameservers, hostname)
|
return r.queryEachNS(ctx, nameservers, hostname, recordTypes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) queryEachNS(
|
func (r *Resolver) queryEachNS(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
nameservers []string,
|
nameservers []string,
|
||||||
hostname string,
|
hostname string,
|
||||||
|
qtypes []uint16,
|
||||||
) (map[string]*NameserverResponse, error) {
|
) (map[string]*NameserverResponse, error) {
|
||||||
results := make(map[string]*NameserverResponse)
|
results := make(map[string]*NameserverResponse)
|
||||||
|
|
||||||
for _, ns := range nameservers {
|
for _, ns := range nameservers {
|
||||||
resp, err := r.QueryNameserver(ctx, ns, hostname)
|
resp, err := r.queryNameserver(ctx, ns, hostname, qtypes)
|
||||||
|
|
||||||
// A query the context cut short says nothing about the
|
// A query the context cut short says nothing about the
|
||||||
// nameserver, so it must not be returned as its failure.
|
// nameserver, so it must not be returned as its failure.
|
||||||
@@ -835,9 +858,10 @@ func (r *Resolver) LookupAllRecords(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPAddresses resolves a hostname to all IPv4 and IPv6
|
// ResolveIPAddresses resolves a hostname to all IPv4 and IPv6
|
||||||
// addresses, following CNAME chains up to MaxCNAMEDepth. When no
|
// addresses, following CNAME chains up to MaxCNAMEDepth. It asks each
|
||||||
// nameserver of the name's zone answered, it returns an error rather
|
// nameserver of the name's zone for its A, AAAA and CNAME records only.
|
||||||
// than no addresses.
|
// When no nameserver of the name's zone answered, it returns an error
|
||||||
|
// rather than no addresses.
|
||||||
func (r *Resolver) ResolveIPAddresses(
|
func (r *Resolver) ResolveIPAddresses(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
hostname string,
|
hostname string,
|
||||||
@@ -858,7 +882,12 @@ func (r *Resolver) resolveIPWithCNAME(
|
|||||||
return nil, ErrCNAMEDepthExceeded
|
return nil, ErrCNAMEDepthExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
results, err := r.QueryAllNameservers(ctx, hostname)
|
nameservers, err := r.FindAuthoritativeNameservers(ctx, hostname)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
results, err := r.queryEachNS(ctx, nameservers, hostname, addressTypes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -562,6 +562,34 @@ func TestResolveIPAddresses_CloudflareDomain(t *testing.T) {
|
|||||||
assert.NotEmpty(t, ips)
|
assert.NotEmpty(t, ips)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestResolveIPAddresses_NameserverIPv4AndIPv6 looks up the addresses of
|
||||||
|
// one of cloudflare.com's nameservers, as a domain check does for each
|
||||||
|
// nameserver. That name has A and AAAA records, so both kinds of address
|
||||||
|
// come back.
|
||||||
|
func TestResolveIPAddresses_NameserverIPv4AndIPv6(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
r := newTestResolver(t)
|
||||||
|
ns := findOneNSForDomain(t, r, "cloudflare.com")
|
||||||
|
ips := liveResolveIPs(t, r, ns)
|
||||||
|
|
||||||
|
var ipv4, ipv6 int
|
||||||
|
|
||||||
|
for _, ip := range ips {
|
||||||
|
parsed := net.ParseIP(ip)
|
||||||
|
require.NotNil(t, parsed, "should be valid IP: %s", ip)
|
||||||
|
|
||||||
|
if parsed.To4() != nil {
|
||||||
|
ipv4++
|
||||||
|
} else {
|
||||||
|
ipv6++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Positive(t, ipv4, "no IPv4 address for %s: %v", ns, ips)
|
||||||
|
assert.Positive(t, ipv6, "no IPv6 address for %s: %v", ns, ips)
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// Context cancellation tests
|
// Context cancellation tests
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
|
|||||||
@@ -26,18 +26,22 @@ import (
|
|||||||
|
|
||||||
// The watcher looks these names up in live DNS with the real resolver,
|
// The watcher looks these names up in live DNS with the real resolver,
|
||||||
// so tests assert on what the watcher does with the answers, never on
|
// so tests assert on what the watcher does with the answers, never on
|
||||||
// the records these zones publish. testHost's nameservers and addresses
|
// the records these zones publish. The nameservers of testHost and
|
||||||
// stay the same from one check to the next, which the tests that check
|
// testSmallDomain stay the same between a test looking them up and its
|
||||||
// it twice rely on, and testSmallDomain's nameservers stay the same
|
// check. Every query a check sends is one more that can be lost, so the
|
||||||
// between a test looking them up and its check. A domain check looks up
|
// tests keep them few. A check asks each of a name's nameservers about
|
||||||
// each nameserver's addresses, about a second per nameserver, so the
|
// every record type, and both names have two. A domain check also looks
|
||||||
// tests that check a domain use testSmallDomain, which has two
|
// up each nameserver's addresses at every nameserver of the zone that
|
||||||
// nameservers, and check it once. The tests that query testDomain's
|
// nameserver is in: testSmallDomain's nameservers are in zones with two
|
||||||
// nameservers directly do no domain check.
|
// nameservers, while a domain whose nameservers are in, say,
|
||||||
|
// cloudflare.com, which has five, makes each domain check much longer.
|
||||||
|
// A test checks a domain only when it is about domains, and checks once,
|
||||||
|
// from saved state it builds, rather than twice. The tests that query
|
||||||
|
// testDomain's nameservers directly do no domain check.
|
||||||
const (
|
const (
|
||||||
testDomain = "google.com"
|
testDomain = "google.com"
|
||||||
testSmallDomain = "example.com"
|
testSmallDomain = "desec.io"
|
||||||
testHost = "cloudflare.com"
|
testHost = "example.org"
|
||||||
testIssuer = "DigiCert"
|
testIssuer = "DigiCert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -259,55 +263,48 @@ func checkOnce(
|
|||||||
|
|
||||||
// runChecks builds a watcher, lets prepare set up the saved state and
|
// runChecks builds a watcher, lets prepare set up the saved state and
|
||||||
// stand-ins it starts from, and runs its checks once against live DNS.
|
// stand-ins it starts from, and runs its checks once against live DNS.
|
||||||
// If change is not nil, change then alters the saved state or stand-ins
|
// When the check finds no fresh address for a name (see checkOnce), the
|
||||||
// and the checks run a second time. When either check finds no fresh
|
// watcher is thrown away and all of this runs again on a new one, so a
|
||||||
// address for a name (see checkOnce), the watcher is thrown away and
|
// failed attempt leaves nothing behind in the saved state, the
|
||||||
// all of this runs again on a new one, so a failed attempt leaves
|
// stand-ins or the notifications.
|
||||||
// nothing behind in the saved state, the stand-ins or the notifications.
|
|
||||||
func runChecks(
|
func runChecks(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
cfg *config.Config,
|
cfg *config.Config,
|
||||||
prepare, change func(deps *testDeps),
|
prepare func(deps *testDeps),
|
||||||
) *testDeps {
|
) (*watcher.Watcher, *testDeps) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
var deps *testDeps
|
var (
|
||||||
|
w *watcher.Watcher
|
||||||
|
deps *testDeps
|
||||||
|
)
|
||||||
|
|
||||||
livednstest.Retry(t, "watcher checks", func(ctx context.Context) error {
|
livednstest.Retry(t, "watcher checks", func(ctx context.Context) error {
|
||||||
var w *watcher.Watcher
|
|
||||||
|
|
||||||
w, deps = newTestWatcher(t, cfg)
|
w, deps = newTestWatcher(t, cfg)
|
||||||
|
|
||||||
if prepare != nil {
|
if prepare != nil {
|
||||||
prepare(deps)
|
prepare(deps)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := checkOnce(ctx, w, deps)
|
|
||||||
if err != nil || change == nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
change(deps)
|
|
||||||
|
|
||||||
return checkOnce(ctx, w, deps)
|
return checkOnce(ctx, w, deps)
|
||||||
})
|
})
|
||||||
|
|
||||||
return deps
|
return w, deps
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookupNameservers returns the nameservers live DNS lists for domain,
|
// lookupNameservers returns the nameservers live DNS lists for name,
|
||||||
// for a test to save in the state its check starts from.
|
// for a test to save in the state its check starts from.
|
||||||
func lookupNameservers(t *testing.T, domain string) []string {
|
func lookupNameservers(t *testing.T, name string) []string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
res := resolver.NewFromLogger(slog.Default())
|
res := resolver.NewFromLogger(slog.Default())
|
||||||
|
|
||||||
var nameservers []string
|
var nameservers []string
|
||||||
|
|
||||||
livednstest.Retry(t, "LookupNS("+domain+")", func(ctx context.Context) error {
|
livednstest.Retry(t, "LookupNS("+name+")", func(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
nameservers, err = res.LookupNS(ctx, domain)
|
nameservers, err = res.LookupNS(ctx, name)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
@@ -370,7 +367,7 @@ func TestFirstRunBaseline(t *testing.T) {
|
|||||||
cfg.Domains = []string{testSmallDomain}
|
cfg.Domains = []string{testSmallDomain}
|
||||||
cfg.Hostnames = []string{testHost}
|
cfg.Hostnames = []string{testHost}
|
||||||
|
|
||||||
deps := runChecks(t, cfg, nil, nil)
|
_, deps := runChecks(t, cfg, nil)
|
||||||
|
|
||||||
assertNoNotifications(t, deps)
|
assertNoNotifications(t, deps)
|
||||||
assertStatePopulated(t, deps)
|
assertStatePopulated(t, deps)
|
||||||
@@ -422,7 +419,7 @@ func TestDomainPortAndTLSChecks(t *testing.T) {
|
|||||||
cfg := defaultTestConfig(t)
|
cfg := defaultTestConfig(t)
|
||||||
cfg.Domains = []string{testSmallDomain}
|
cfg.Domains = []string{testSmallDomain}
|
||||||
|
|
||||||
deps := runChecks(t, cfg, nil, nil)
|
_, deps := runChecks(t, cfg, nil)
|
||||||
|
|
||||||
snap := deps.state.GetSnapshot()
|
snap := deps.state.GetSnapshot()
|
||||||
|
|
||||||
@@ -462,11 +459,11 @@ func TestNSChangeDetection(t *testing.T) {
|
|||||||
cfg.Domains = []string{testSmallDomain}
|
cfg.Domains = []string{testSmallDomain}
|
||||||
|
|
||||||
// The saved state lists nameservers that live DNS does not.
|
// The saved state lists nameservers that live DNS does not.
|
||||||
deps := runChecks(t, cfg, func(deps *testDeps) {
|
_, deps := runChecks(t, cfg, func(deps *testDeps) {
|
||||||
deps.state.SetDomainState(testSmallDomain, &state.DomainState{
|
deps.state.SetDomainState(testSmallDomain, &state.DomainState{
|
||||||
Nameservers: []string{oldNS1, oldNS2},
|
Nameservers: []string{oldNS1, oldNS2},
|
||||||
})
|
})
|
||||||
}, nil)
|
})
|
||||||
|
|
||||||
assertNotified(t, deps, "NS Change: "+testSmallDomain, "warning")
|
assertNotified(t, deps, "NS Change: "+testSmallDomain, "warning")
|
||||||
|
|
||||||
@@ -486,7 +483,7 @@ func TestNSAddressChangeDetection(t *testing.T) {
|
|||||||
|
|
||||||
// The saved state lists the nameservers live DNS lists, each at an
|
// The saved state lists the nameservers live DNS lists, each at an
|
||||||
// address live DNS never returns.
|
// address live DNS never returns.
|
||||||
deps := runChecks(t, cfg, func(deps *testDeps) {
|
_, deps := runChecks(t, cfg, func(deps *testDeps) {
|
||||||
nsAddresses := make(map[string][]string, len(nameservers))
|
nsAddresses := make(map[string][]string, len(nameservers))
|
||||||
for _, ns := range nameservers {
|
for _, ns := range nameservers {
|
||||||
nsAddresses[ns] = []string{oldIP}
|
nsAddresses[ns] = []string{oldIP}
|
||||||
@@ -496,7 +493,7 @@ func TestNSAddressChangeDetection(t *testing.T) {
|
|||||||
Nameservers: nameservers,
|
Nameservers: nameservers,
|
||||||
NameserverAddresses: nsAddresses,
|
NameserverAddresses: nsAddresses,
|
||||||
})
|
})
|
||||||
}, nil)
|
})
|
||||||
|
|
||||||
title := "NS Address Change: " + testSmallDomain
|
title := "NS Address Change: " + testSmallDomain
|
||||||
ds, _ := deps.state.GetDomainState(testSmallDomain)
|
ds, _ := deps.state.GetDomainState(testSmallDomain)
|
||||||
@@ -542,12 +539,12 @@ func TestNSAddedAndRemovedIsNoAddressChange(t *testing.T) {
|
|||||||
// The saved state lists oldNS1, which live DNS does not, in place of
|
// The saved state lists oldNS1, which live DNS does not, in place of
|
||||||
// the first nameserver live DNS lists, so that the check finds that
|
// the first nameserver live DNS lists, so that the check finds that
|
||||||
// one added and oldNS1 removed. Only oldNS1 has addresses saved.
|
// one added and oldNS1 removed. Only oldNS1 has addresses saved.
|
||||||
deps := runChecks(t, cfg, func(deps *testDeps) {
|
_, deps := runChecks(t, cfg, func(deps *testDeps) {
|
||||||
deps.state.SetDomainState(testSmallDomain, &state.DomainState{
|
deps.state.SetDomainState(testSmallDomain, &state.DomainState{
|
||||||
Nameservers: append([]string{oldNS1}, nameservers[1:]...),
|
Nameservers: append([]string{oldNS1}, nameservers[1:]...),
|
||||||
NameserverAddresses: map[string][]string{oldNS1: {oldIP}},
|
NameserverAddresses: map[string][]string{oldNS1: {oldIP}},
|
||||||
})
|
})
|
||||||
}, nil)
|
})
|
||||||
|
|
||||||
if n := countNotifications(deps, "NS Change: "+testSmallDomain); n != 1 {
|
if n := countNotifications(deps, "NS Change: "+testSmallDomain); n != 1 {
|
||||||
t.Errorf("sent %d NS changes, want 1", n)
|
t.Errorf("sent %d NS changes, want 1", n)
|
||||||
@@ -565,15 +562,17 @@ func TestRecordChangeDetection(t *testing.T) {
|
|||||||
cfg := defaultTestConfig(t)
|
cfg := defaultTestConfig(t)
|
||||||
cfg.Hostnames = []string{testHost}
|
cfg.Hostnames = []string{testHost}
|
||||||
|
|
||||||
// Between the checks, save for every nameserver an address live DNS
|
nameservers := lookupNameservers(t, testHost)
|
||||||
// never returns.
|
|
||||||
deps := runChecks(t, cfg, nil, func(deps *testDeps) {
|
// The saved state has every nameserver live DNS lists answering
|
||||||
hs, _ := deps.state.GetHostnameState(testHost)
|
// with an address live DNS never returns.
|
||||||
for _, nsState := range hs.RecordsByNameserver {
|
_, deps := runChecks(t, cfg, func(deps *testDeps) {
|
||||||
nsState.Records = map[string][]string{"A": {oldIP}}
|
byNameserver := make(map[string]*state.NameserverRecordState)
|
||||||
|
for _, ns := range nameservers {
|
||||||
|
byNameserver[ns] = answered(map[string][]string{"A": {oldIP}})
|
||||||
}
|
}
|
||||||
|
|
||||||
deps.state.SetHostnameState(testHost, hs)
|
deps.state.SetHostnameState(testHost, saved(byNameserver))
|
||||||
})
|
})
|
||||||
|
|
||||||
assertNotified(t, deps, "Record Change: "+testHost, "warning")
|
assertNotified(t, deps, "Record Change: "+testHost, "warning")
|
||||||
@@ -585,12 +584,15 @@ func TestPortStateChange(t *testing.T) {
|
|||||||
cfg := defaultTestConfig(t)
|
cfg := defaultTestConfig(t)
|
||||||
cfg.Hostnames = []string{testHost}
|
cfg.Hostnames = []string{testHost}
|
||||||
|
|
||||||
// Between the checks, every port closes.
|
w, deps := runChecks(t, cfg, nil)
|
||||||
deps := runChecks(t, cfg, nil, func(deps *testDeps) {
|
|
||||||
deps.portChecker.mu.Lock()
|
// Every port closes, and the port checks run again. They look
|
||||||
deps.portChecker.closed = true
|
// nothing up.
|
||||||
deps.portChecker.mu.Unlock()
|
deps.portChecker.mu.Lock()
|
||||||
})
|
deps.portChecker.closed = true
|
||||||
|
deps.portChecker.mu.Unlock()
|
||||||
|
|
||||||
|
w.CheckAllPorts(t.Context())
|
||||||
|
|
||||||
hs, _ := deps.state.GetHostnameState(testHost)
|
hs, _ := deps.state.GetHostnameState(testHost)
|
||||||
assertNotified(
|
assertNotified(
|
||||||
@@ -610,7 +612,7 @@ func TestTLSExpiryWarning(t *testing.T) {
|
|||||||
cfg := defaultTestConfig(t)
|
cfg := defaultTestConfig(t)
|
||||||
cfg.Hostnames = []string{testHost}
|
cfg.Hostnames = []string{testHost}
|
||||||
|
|
||||||
deps := runChecks(t, cfg, expiresInThreeDays, nil)
|
_, deps := runChecks(t, cfg, expiresInThreeDays)
|
||||||
|
|
||||||
assertNotified(t, deps, "TLS Expiry Warning: "+testHost, "warning")
|
assertNotified(t, deps, "TLS Expiry Warning: "+testHost, "warning")
|
||||||
}
|
}
|
||||||
@@ -782,7 +784,7 @@ func TestDNSRunsBeforePortAndTLSChecks(t *testing.T) {
|
|||||||
cfg.Hostnames = []string{testHost}
|
cfg.Hostnames = []string{testHost}
|
||||||
|
|
||||||
// The saved state says the last check found testHost at oldIP.
|
// The saved state says the last check found testHost at oldIP.
|
||||||
deps := runChecks(t, cfg, func(deps *testDeps) {
|
_, deps := runChecks(t, cfg, func(deps *testDeps) {
|
||||||
deps.state.SetHostnameState(testHost, &state.HostnameState{
|
deps.state.SetHostnameState(testHost, &state.HostnameState{
|
||||||
RecordsByNameserver: map[string]*state.NameserverRecordState{
|
RecordsByNameserver: map[string]*state.NameserverRecordState{
|
||||||
oldNS1: {
|
oldNS1: {
|
||||||
@@ -791,7 +793,7 @@ func TestDNSRunsBeforePortAndTLSChecks(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}, nil)
|
})
|
||||||
|
|
||||||
snap := deps.state.GetSnapshot()
|
snap := deps.state.GetSnapshot()
|
||||||
|
|
||||||
@@ -922,21 +924,20 @@ func TestNSFailureAndRecovery(t *testing.T) {
|
|||||||
cfg := defaultTestConfig(t)
|
cfg := defaultTestConfig(t)
|
||||||
cfg.Hostnames = []string{testHost}
|
cfg.Hostnames = []string{testHost}
|
||||||
|
|
||||||
// Between the checks, save every nameserver the first check found
|
nameservers := lookupNameservers(t, testHost)
|
||||||
// as one that did not answer, and add, as answering, one that live
|
|
||||||
// DNS does not list, which then disappears.
|
// The saved state has every nameserver live DNS lists as one that
|
||||||
deps := runChecks(t, cfg, nil, func(deps *testDeps) {
|
// did not answer, and, as answering, one that live DNS does not
|
||||||
hs, _ := deps.state.GetHostnameState(testHost)
|
// list, which then disappears.
|
||||||
for ns := range hs.RecordsByNameserver {
|
_, deps := runChecks(t, cfg, func(deps *testDeps) {
|
||||||
hs.RecordsByNameserver[ns] = failed()
|
byNameserver := map[string]*state.NameserverRecordState{
|
||||||
|
oldNS1: answered(map[string][]string{"A": {oldIP}}),
|
||||||
|
}
|
||||||
|
for _, ns := range nameservers {
|
||||||
|
byNameserver[ns] = failed()
|
||||||
}
|
}
|
||||||
|
|
||||||
hs.RecordsByNameserver[oldNS1] = &state.NameserverRecordState{
|
deps.state.SetHostnameState(testHost, saved(byNameserver))
|
||||||
Records: map[string][]string{"A": {oldIP}},
|
|
||||||
Status: "ok",
|
|
||||||
}
|
|
||||||
|
|
||||||
deps.state.SetHostnameState(testHost, hs)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
assertNotified(t, deps, "NS Failure: "+testHost, "error")
|
assertNotified(t, deps, "NS Failure: "+testHost, "error")
|
||||||
|
|||||||
Reference in New Issue
Block a user