Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48275d598e |
+2
-70
@@ -10,20 +10,14 @@ run:
|
||||
|
||||
linters:
|
||||
default: all
|
||||
enable:
|
||||
# Successor to the deprecated gomodguard. Named explicitly, rather than
|
||||
# left to `default: all`, because it carries the module policy below.
|
||||
- gomodguard_v2
|
||||
disable:
|
||||
# Genuinely incompatible with project patterns
|
||||
- exhaustruct # Requires all struct fields
|
||||
- depguard # Dependency allow/block lists
|
||||
- godot # Requires comments to end with periods
|
||||
- wsl # Deprecated, replaced by wsl_v5
|
||||
- wrapcheck # Too verbose for internal packages
|
||||
- varnamelen # Short names like db, id are idiomatic Go
|
||||
# Deprecated: the warning is attached to the old name, so it is
|
||||
# silenced by disabling that name, not by enabling the successor.
|
||||
- wsl # Deprecated, replaced by wsl_v5
|
||||
- gomodguard # Deprecated, replaced by gomodguard_v2
|
||||
settings:
|
||||
lll:
|
||||
line-length: 88
|
||||
@@ -34,68 +28,6 @@ linters:
|
||||
max-complexity: 15
|
||||
dupl:
|
||||
threshold: 100
|
||||
depguard:
|
||||
# Test-support code must not be compiled into the shipped binary. A
|
||||
# test-support package exists to hand a test privileges the program
|
||||
# itself must never have, so a file that is not a test must not import
|
||||
# one. Test files, and the files inside a package whose directory name
|
||||
# ends in `test`, are where that code belongs, and are exempt.
|
||||
#
|
||||
# The deny list below is the one part of this file a repository is
|
||||
# expected to extend, and the only part it may. depguard matches an
|
||||
# import path against a list of prefixes, so it cannot be told "any path
|
||||
# whose last segment ends in test"; a repository's own test-support
|
||||
# packages have to be named here one at a time, by full import path,
|
||||
# under a module path that differs from repository to repository. Add
|
||||
# them; change nothing else.
|
||||
rules:
|
||||
test-support:
|
||||
list-mode: lax
|
||||
files:
|
||||
- "$all"
|
||||
- "!$test"
|
||||
- "!**/*test/**"
|
||||
deny:
|
||||
- pkg: net/http/httptest
|
||||
desc: >-
|
||||
Test-support code belongs in test files and in packages whose
|
||||
directory name ends in test, not in the shipped binary.
|
||||
- pkg: sneak.berlin/go/dnswatcher/internal/livednstest
|
||||
desc: >-
|
||||
Live-DNS test support belongs in test files and in packages
|
||||
whose directory name ends in test, not in the shipped binary.
|
||||
# Only decisions already recorded in the Go package defaults are
|
||||
# listed here. Every entry matches the module path exactly.
|
||||
gomodguard_v2:
|
||||
blocked:
|
||||
- module: github.com/rs/zerolog
|
||||
recommendations:
|
||||
- log/slog
|
||||
reason: "Structured logging is stdlib log/slog."
|
||||
# One entry per pre-fork module path, because the later releases
|
||||
# are separate paths. A prefix match would be shorter but would
|
||||
# also reach github.com/go-redis/redismock, the test double for
|
||||
# the successor these entries recommend.
|
||||
- module: github.com/go-redis/redis
|
||||
recommendations:
|
||||
- github.com/redis/go-redis/v9
|
||||
reason: "Pre-fork module; use the maintained go-redis v9."
|
||||
- module: github.com/go-redis/redis/v7
|
||||
recommendations:
|
||||
- github.com/redis/go-redis/v9
|
||||
reason: "Pre-fork module; use the maintained go-redis v9."
|
||||
- module: github.com/go-redis/redis/v8
|
||||
recommendations:
|
||||
- github.com/redis/go-redis/v9
|
||||
reason: "Pre-fork module; use the maintained go-redis v9."
|
||||
- module: github.com/sergi/go-diff
|
||||
recommendations:
|
||||
- github.com/aymanbagabas/go-udiff
|
||||
reason: "No unified diff output; use go-udiff."
|
||||
- module: github.com/hexops/gotextdiff
|
||||
recommendations:
|
||||
- github.com/aymanbagabas/go-udiff
|
||||
reason: "Unmaintained fork; use go-udiff."
|
||||
|
||||
issues:
|
||||
max-issues-per-linter: 0
|
||||
|
||||
@@ -278,8 +278,6 @@ internal/
|
||||
tlscheck/tlscheck.go TLS certificate inspector
|
||||
notify/notify.go Notification service (Slack, Mattermost, ntfy)
|
||||
watcher/watcher.go Main monitoring orchestrator and scheduler
|
||||
livednstest/livednstest.go Retry and concurrency limit for tests
|
||||
against live DNS (imported only by tests)
|
||||
```
|
||||
|
||||
### Design Principles
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ real servers ensures the resolver works correctly in production.
|
||||
- Query timeout is calibrated to 3× maximum antipodal RTT (~300ms)
|
||||
plus processing margin
|
||||
- Root server fan-out is limited to reduce parallel query load
|
||||
- Live lookups that expect an answer go through `internal/livednstest`,
|
||||
- Live lookups that expect an answer go through `internal/livedns`,
|
||||
which limits how many run at once in a test binary and retries a
|
||||
lookup that got none
|
||||
- Flaky failures from transient network issues are acceptable and
|
||||
|
||||
@@ -10,7 +10,11 @@
|
||||
|
||||
# Status
|
||||
|
||||
pre-1.0. No git tags.
|
||||
pre-1.0. No git tags. Core resolver work in flight on feature/resolver
|
||||
(dirty: internal/resolver/resolver_test.go). Local checkout has diverged
|
||||
from origin: origin/main is 8 commits ahead (watcher orchestrator,
|
||||
unified TARGETS) and origin/feature/resolver already contains the full
|
||||
iterative resolver implementation with hermetic mocked tests.
|
||||
|
||||
# Next Step
|
||||
|
||||
@@ -19,22 +23,13 @@ Rationale, Design, TODO, License, Author) if any are still missing.
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-09-29: the live-DNS test package is renamed `internal/livednstest` and
|
||||
added to the `test-support` `deny` list in `.golangci.yml`, so `make lint`
|
||||
fails when program code imports it (closes #164).
|
||||
- 2026-09-29: `.golangci.yml` re-fetched unchanged from `sneak/prompts`. It
|
||||
replaces the deprecated `gomodguard` with `gomodguard_v2`, so `make lint` no
|
||||
longer warns about it, and turns on `depguard` with the org `test-support`
|
||||
rule, which rejects `net/http/httptest` except in test files and in files
|
||||
under a directory whose name ends in `test`. This repo had no `deny` entries
|
||||
of its own to carry forward (closes #123).
|
||||
- 2026-09-29: nothing stands in for DNS any more. Watcher tests that look
|
||||
something up in DNS use the real resolver against live DNS servers and test
|
||||
record and nameserver changes by preparing the saved state a check starts
|
||||
from; the resolver timeout test queries an address that never answers, and
|
||||
`NewFromLoggerWithClient`, used only by its stand-in client, is gone. The
|
||||
live-DNS retry and concurrency limit moved to `internal/livednstest`, which
|
||||
both test packages use. `TESTING.md` states the README's rule (closes #159).
|
||||
- 2026-09-29: nothing stands in for DNS any more. The watcher tests use the
|
||||
real resolver against live DNS and test changes by preparing the saved
|
||||
state a check starts from; the resolver timeout test queries an address
|
||||
that never answers, and `NewFromLoggerWithClient`, used only by its
|
||||
stand-in client, is gone. The live-DNS retry and concurrency limit moved
|
||||
to `internal/livedns`, which both test packages use. `TESTING.md` states
|
||||
the README's rule (closes #159).
|
||||
- 2026-09-28: the inconsistency alert is sent once, on the check where two
|
||||
nameservers start to disagree or where a nameserver that disagrees first
|
||||
appears, instead of on every check while they disagree, and not again after
|
||||
@@ -273,5 +268,4 @@ Infrastructure notes (from untracked TODO.md):
|
||||
- Module path sneak.berlin/go/dnswatcher differs from the git.eeqj.de
|
||||
remote intentionally; do not "fix" it
|
||||
- Dependencies: github.com/miekg/dns, golang.org/x/net/publicsuffix
|
||||
- DNS is never mocked; tests that look something up in DNS query live DNS
|
||||
servers (README, "No DNS mocking. Ever.")
|
||||
- Tests use live DNS and never mock it (README, "No DNS mocking. Ever.")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Package livednstest runs the live DNS operations of tests. Tests that
|
||||
// look something up in DNS query live DNS servers, never a stand-in —
|
||||
// Package livedns runs the live DNS operations of tests. Every test in
|
||||
// this project that needs DNS resolves against the real, live DNS —
|
||||
// see TESTING.md. Nothing here mocks, fakes, stubs, records or replays
|
||||
// DNS, and nothing here skips a test: it only changes *how* the live
|
||||
// queries are issued, so that a single dropped UDP packet or one slow
|
||||
@@ -22,7 +22,7 @@
|
||||
// first attempt. A fault in the code under test that leaves
|
||||
// nothing to check looks the same as live DNS not answering, and
|
||||
// fails only after the last attempt.
|
||||
package livednstest
|
||||
package livedns
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -1,4 +1,4 @@
|
||||
package livednstest_test
|
||||
package livedns_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"sneak.berlin/go/dnswatcher/internal/livednstest"
|
||||
"sneak.berlin/go/dnswatcher/internal/livedns"
|
||||
)
|
||||
|
||||
// Tests for the retry and the concurrency limit themselves. They
|
||||
@@ -21,11 +21,11 @@ func TestRetryRecoversFromTransientFailure(t *testing.T) {
|
||||
|
||||
attempts := 0
|
||||
|
||||
livednstest.Retry(t, "transient", func(_ context.Context) error {
|
||||
livedns.Retry(t, "transient", func(_ context.Context) error {
|
||||
attempts++
|
||||
|
||||
if attempts < wantAttempts {
|
||||
return livednstest.ErrNoAnswer
|
||||
return livedns.ErrNoAnswer
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -37,19 +37,19 @@ func TestRetryRecoversFromTransientFailure(t *testing.T) {
|
||||
func TestRetryGivesEachAttemptADeadline(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
livednstest.Retry(t, "deadline", func(ctx context.Context) error {
|
||||
livedns.Retry(t, "deadline", func(ctx context.Context) error {
|
||||
deadline, ok := ctx.Deadline()
|
||||
assert.True(t, ok, "attempt should carry a deadline")
|
||||
|
||||
remaining := time.Until(deadline)
|
||||
|
||||
assert.LessOrEqual(t, remaining, livednstest.AttemptTimeout)
|
||||
assert.LessOrEqual(t, remaining, livedns.AttemptTimeout)
|
||||
|
||||
// Lower bound too: without one this passes for a
|
||||
// deadline far shorter than intended, which would
|
||||
// silently turn every live attempt into an instant
|
||||
// timeout.
|
||||
assert.Greater(t, remaining, livednstest.AttemptTimeout/2)
|
||||
assert.Greater(t, remaining, livedns.AttemptTimeout/2)
|
||||
|
||||
return nil
|
||||
})
|
||||
@@ -73,7 +73,7 @@ func TestRunBoundsConcurrency(t *testing.T) {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
_ = livednstest.Run(func(_ context.Context) error {
|
||||
_ = livedns.Run(func(_ context.Context) error {
|
||||
mu.Lock()
|
||||
inFlight++
|
||||
|
||||
@@ -97,7 +97,7 @@ func TestRunBoundsConcurrency(t *testing.T) {
|
||||
|
||||
assert.Positive(t, maxSeen)
|
||||
assert.LessOrEqual(
|
||||
t, maxSeen, livednstest.Concurrency,
|
||||
t, maxSeen, livedns.Concurrency,
|
||||
"live queries must stay under the package-wide gate",
|
||||
)
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sneak.berlin/go/dnswatcher/internal/livednstest"
|
||||
"sneak.berlin/go/dnswatcher/internal/livedns"
|
||||
"sneak.berlin/go/dnswatcher/internal/resolver"
|
||||
)
|
||||
|
||||
@@ -17,12 +17,11 @@ import (
|
||||
// Live DNS test support
|
||||
// ----------------------------------------------------------------
|
||||
//
|
||||
// Tests that look something up in DNS query live DNS servers, never a
|
||||
// stand-in; logic that works on record data may be tested on that
|
||||
// data with no lookup (see TESTING.md). Each live operation below goes
|
||||
// through livednstest.Retry, which bounds how many resolutions are in
|
||||
// flight at once and retries an operation that got no answer (see
|
||||
// package livednstest).
|
||||
// Every test in this package resolves against the real, live DNS —
|
||||
// see TESTING.md. Each live operation below goes through
|
||||
// livedns.Retry, which bounds how many resolutions are in flight at
|
||||
// once and retries an operation that got no answer (see package
|
||||
// livedns).
|
||||
//
|
||||
// Where an assertion spans several independent nameservers, a quorum
|
||||
// is enough: a strict majority answering as expected. A server that
|
||||
@@ -162,7 +161,7 @@ func liveFindAuthoritative(
|
||||
|
||||
var out []string
|
||||
|
||||
livednstest.Retry(
|
||||
livedns.Retry(
|
||||
t,
|
||||
"FindAuthoritativeNameservers("+domain+")",
|
||||
func(ctx context.Context) error {
|
||||
@@ -174,7 +173,7 @@ func liveFindAuthoritative(
|
||||
if len(ns) == 0 {
|
||||
return fmt.Errorf(
|
||||
"%w: %s has no nameservers",
|
||||
livednstest.ErrNoAnswer, domain,
|
||||
livedns.ErrNoAnswer, domain,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -198,7 +197,7 @@ func liveLookupNS(
|
||||
|
||||
var out []string
|
||||
|
||||
livednstest.Retry(
|
||||
livedns.Retry(
|
||||
t,
|
||||
"LookupNS("+domain+")",
|
||||
func(ctx context.Context) error {
|
||||
@@ -210,7 +209,7 @@ func liveLookupNS(
|
||||
if len(ns) == 0 {
|
||||
return fmt.Errorf(
|
||||
"%w: %s has no nameservers",
|
||||
livednstest.ErrNoAnswer, domain,
|
||||
livedns.ErrNoAnswer, domain,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -240,7 +239,7 @@ func liveQueryNameserver(
|
||||
|
||||
var out *resolver.NameserverResponse
|
||||
|
||||
livednstest.Retry(
|
||||
livedns.Retry(
|
||||
t,
|
||||
what,
|
||||
func(ctx context.Context) error {
|
||||
@@ -255,7 +254,7 @@ func liveQueryNameserver(
|
||||
resp.Status == resolver.StatusError {
|
||||
return fmt.Errorf(
|
||||
"%w: %s returned %s: %s",
|
||||
livednstest.ErrNoAnswer, nameserver,
|
||||
livedns.ErrNoAnswer, nameserver,
|
||||
resp.Status, resp.Error,
|
||||
)
|
||||
}
|
||||
@@ -282,7 +281,7 @@ func liveQueryAllNameservers(
|
||||
|
||||
var out map[string]*resolver.NameserverResponse
|
||||
|
||||
livednstest.Retry(
|
||||
livedns.Retry(
|
||||
t,
|
||||
"QueryAllNameservers("+hostname+")",
|
||||
func(ctx context.Context) error {
|
||||
@@ -294,7 +293,7 @@ func liveQueryAllNameservers(
|
||||
if len(results) == 0 {
|
||||
return fmt.Errorf(
|
||||
"%w: no nameservers queried for %s",
|
||||
livednstest.ErrNoAnswer, hostname,
|
||||
livedns.ErrNoAnswer, hostname,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -327,7 +326,7 @@ func liveResolveIPs(
|
||||
|
||||
var out []string
|
||||
|
||||
livednstest.Retry(
|
||||
livedns.Retry(
|
||||
t,
|
||||
"ResolveIPAddresses("+hostname+")",
|
||||
func(ctx context.Context) error {
|
||||
@@ -339,7 +338,7 @@ func liveResolveIPs(
|
||||
if len(ips) == 0 {
|
||||
return fmt.Errorf(
|
||||
"%w: no addresses for %s",
|
||||
livednstest.ErrNoAnswer, hostname,
|
||||
livedns.ErrNoAnswer, hostname,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -366,7 +365,7 @@ func liveResolveIPsAllowingEmpty(
|
||||
|
||||
var out []string
|
||||
|
||||
livednstest.Retry(
|
||||
livedns.Retry(
|
||||
t,
|
||||
"ResolveIPAddresses("+hostname+")",
|
||||
func(ctx context.Context) error {
|
||||
|
||||
@@ -32,8 +32,8 @@ func newTestResolver(t *testing.T) *resolver.Resolver {
|
||||
}
|
||||
|
||||
// findOneNSForDomain picks one authoritative nameserver to aim a
|
||||
// test at. Quorum handling lives in livedns_test.go, and the live-DNS
|
||||
// retry and concurrency limit in package livednstest.
|
||||
// test at. Live-DNS retry, concurrency and quorum handling live in
|
||||
// livedns_test.go.
|
||||
func findOneNSForDomain(
|
||||
t *testing.T,
|
||||
r *resolver.Resolver,
|
||||
@@ -528,10 +528,10 @@ func TestQueryNameserverIP_Timeout(t *testing.T) {
|
||||
r := newTestResolver(t)
|
||||
|
||||
// Nothing answers at 192.0.2.1, a documentation address. The
|
||||
// resolver tries each query twice, and the first try gives up
|
||||
// after two seconds. A deadline that ends during the first try
|
||||
// makes the status vary from run to run between nodata and
|
||||
// timeout, so the deadline must outlast the first try.
|
||||
// resolver tries each query twice; if the deadline has passed
|
||||
// before the second try starts, the query is reported as nodata,
|
||||
// not timeout. So the deadline must outlast the first try's
|
||||
// two-second timeout.
|
||||
ctx, cancel := context.WithTimeout(
|
||||
context.Background(), 3*time.Second,
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"sneak.berlin/go/dnswatcher/internal/config"
|
||||
"sneak.berlin/go/dnswatcher/internal/livednstest"
|
||||
"sneak.berlin/go/dnswatcher/internal/livedns"
|
||||
"sneak.berlin/go/dnswatcher/internal/portcheck"
|
||||
"sneak.berlin/go/dnswatcher/internal/resolver"
|
||||
"sneak.berlin/go/dnswatcher/internal/state"
|
||||
@@ -19,10 +19,9 @@ import (
|
||||
)
|
||||
|
||||
// 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
|
||||
// the records these zones publish. testHost's nameservers and addresses
|
||||
// stay the same from one check to the next, which the tests that check
|
||||
// it twice rely on.
|
||||
// so tests assert on notifications and saved state, never on the
|
||||
// records these zones publish. testHost's addresses stay the same from
|
||||
// one check to the next, which the tests that check it twice rely on.
|
||||
const (
|
||||
testDomain = "google.com"
|
||||
testHost = "cloudflare.com"
|
||||
@@ -39,8 +38,7 @@ const (
|
||||
|
||||
// --- Stand-ins for the port checker, TLS checker and notifier ---
|
||||
//
|
||||
// DNS has none: the watchers built here use the real resolver (see
|
||||
// TESTING.md).
|
||||
// DNS has none: the watcher uses the real resolver (see TESTING.md).
|
||||
|
||||
// mockPortChecker reports every port open until closed is set.
|
||||
type mockPortChecker struct {
|
||||
@@ -175,8 +173,9 @@ func defaultTestConfig(t *testing.T) *config.Config {
|
||||
|
||||
// checkOnce runs the watcher's checks once and returns an error when a
|
||||
// configured name has no hostname state saved by this check, or that
|
||||
// state holds no address. Either live DNS gave no answer for the name,
|
||||
// or the watcher saved no fresh result for it.
|
||||
// state holds no address. The watcher saves a name's hostname state
|
||||
// only when all of the name's lookups succeed, so this means either
|
||||
// live DNS did not answer or the watcher did not save what it got.
|
||||
func checkOnce(
|
||||
ctx context.Context,
|
||||
w *watcher.Watcher,
|
||||
@@ -195,7 +194,7 @@ func checkOnce(
|
||||
return fmt.Errorf(
|
||||
"%s: %w, or the watcher saved no fresh "+
|
||||
"result for it",
|
||||
name, livednstest.ErrNoAnswer,
|
||||
name, livedns.ErrNoAnswer,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -203,42 +202,46 @@ func checkOnce(
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
// If change is not nil, change then alters the saved state or stand-ins
|
||||
// and the checks run a second time. When either check finds no fresh
|
||||
// address for a name (see checkOnce), the watcher is thrown away and
|
||||
// all of this runs again on a new one, so a failed attempt leaves
|
||||
// nothing behind in the saved state, the stand-ins or the notifications.
|
||||
func runChecks(
|
||||
// runFirstCheck builds a watcher, lets prepare set up the saved state
|
||||
// and stand-ins it starts from, and runs its checks once against live
|
||||
// DNS. When live DNS does not answer, the watcher is thrown away and
|
||||
// built again, so a failed attempt leaves nothing behind in the state
|
||||
// or the notifications.
|
||||
func runFirstCheck(
|
||||
t *testing.T,
|
||||
cfg *config.Config,
|
||||
prepare, change func(deps *testDeps),
|
||||
) *testDeps {
|
||||
prepare func(deps *testDeps),
|
||||
) (*watcher.Watcher, *testDeps) {
|
||||
t.Helper()
|
||||
|
||||
var deps *testDeps
|
||||
|
||||
livednstest.Retry(t, "watcher checks", func(ctx context.Context) error {
|
||||
var w *watcher.Watcher
|
||||
var (
|
||||
w *watcher.Watcher
|
||||
deps *testDeps
|
||||
)
|
||||
|
||||
livedns.Retry(t, "first check", func(ctx context.Context) error {
|
||||
w, deps = newTestWatcher(t, cfg)
|
||||
|
||||
if prepare != nil {
|
||||
prepare(deps)
|
||||
}
|
||||
|
||||
err := checkOnce(ctx, w, deps)
|
||||
if err != nil || change == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
change(deps)
|
||||
|
||||
return checkOnce(ctx, w, deps)
|
||||
})
|
||||
|
||||
return deps
|
||||
return w, deps
|
||||
}
|
||||
|
||||
// runCheck runs the watcher's checks once more against live DNS,
|
||||
// repeating them while live DNS does not answer. A failed lookup keeps
|
||||
// the name's saved records, so a repeat compares against the same
|
||||
// saved state.
|
||||
func runCheck(t *testing.T, w *watcher.Watcher, deps *testDeps) {
|
||||
t.Helper()
|
||||
|
||||
livedns.Retry(t, "check", func(ctx context.Context) error {
|
||||
return checkOnce(ctx, w, deps)
|
||||
})
|
||||
}
|
||||
|
||||
// addresses returns the A and AAAA values saved for a hostname.
|
||||
@@ -296,7 +299,7 @@ func TestFirstRunBaseline(t *testing.T) {
|
||||
cfg.Domains = []string{testDomain}
|
||||
cfg.Hostnames = []string{testHost}
|
||||
|
||||
deps := runChecks(t, cfg, nil, nil)
|
||||
_, deps := runFirstCheck(t, cfg, nil)
|
||||
|
||||
assertNoNotifications(t, deps)
|
||||
assertStatePopulated(t, deps)
|
||||
@@ -348,7 +351,7 @@ func TestDomainPortAndTLSChecks(t *testing.T) {
|
||||
cfg := defaultTestConfig(t)
|
||||
cfg.Domains = []string{testDomain}
|
||||
|
||||
deps := runChecks(t, cfg, nil, nil)
|
||||
_, deps := runFirstCheck(t, cfg, nil)
|
||||
|
||||
snap := deps.state.GetSnapshot()
|
||||
|
||||
@@ -388,11 +391,11 @@ func TestNSChangeDetection(t *testing.T) {
|
||||
cfg.Domains = []string{testDomain}
|
||||
|
||||
// The saved state lists nameservers that live DNS does not.
|
||||
deps := runChecks(t, cfg, func(deps *testDeps) {
|
||||
_, deps := runFirstCheck(t, cfg, func(deps *testDeps) {
|
||||
deps.state.SetDomainState(testDomain, &state.DomainState{
|
||||
Nameservers: []string{oldNS1, oldNS2},
|
||||
})
|
||||
}, nil)
|
||||
})
|
||||
|
||||
assertNotified(t, deps, "NS Change: "+testDomain, "warning")
|
||||
|
||||
@@ -408,16 +411,17 @@ func TestRecordChangeDetection(t *testing.T) {
|
||||
cfg := defaultTestConfig(t)
|
||||
cfg.Hostnames = []string{testHost}
|
||||
|
||||
// Between the checks, save for every nameserver an address live DNS
|
||||
// never returns.
|
||||
deps := runChecks(t, cfg, nil, func(deps *testDeps) {
|
||||
w, deps := runFirstCheck(t, cfg, nil)
|
||||
|
||||
// Save, for every nameserver, an address live DNS never returns.
|
||||
hs, _ := deps.state.GetHostnameState(testHost)
|
||||
for _, nsState := range hs.RecordsByNameserver {
|
||||
nsState.Records = map[string][]string{"A": {oldIP}}
|
||||
}
|
||||
|
||||
deps.state.SetHostnameState(testHost, hs)
|
||||
})
|
||||
|
||||
runCheck(t, w, deps)
|
||||
|
||||
assertNotified(t, deps, "Record Change: "+testHost, "warning")
|
||||
}
|
||||
@@ -428,12 +432,13 @@ func TestPortStateChange(t *testing.T) {
|
||||
cfg := defaultTestConfig(t)
|
||||
cfg.Hostnames = []string{testHost}
|
||||
|
||||
// Between the checks, every port closes.
|
||||
deps := runChecks(t, cfg, nil, func(deps *testDeps) {
|
||||
w, deps := runFirstCheck(t, cfg, nil)
|
||||
|
||||
deps.portChecker.mu.Lock()
|
||||
deps.portChecker.closed = true
|
||||
deps.portChecker.mu.Unlock()
|
||||
})
|
||||
|
||||
runCheck(t, w, deps)
|
||||
|
||||
hs, _ := deps.state.GetHostnameState(testHost)
|
||||
assertNotified(
|
||||
@@ -453,7 +458,7 @@ func TestTLSExpiryWarning(t *testing.T) {
|
||||
cfg := defaultTestConfig(t)
|
||||
cfg.Hostnames = []string{testHost}
|
||||
|
||||
deps := runChecks(t, cfg, expiresInThreeDays, nil)
|
||||
_, deps := runFirstCheck(t, cfg, expiresInThreeDays)
|
||||
|
||||
assertNotified(t, deps, "TLS Expiry Warning: "+testHost, "warning")
|
||||
}
|
||||
@@ -465,20 +470,19 @@ func TestTLSExpiryWarningDedup(t *testing.T) {
|
||||
cfg.Hostnames = []string{testHost}
|
||||
cfg.TLSInterval = 24 * time.Hour
|
||||
|
||||
w, deps := runFirstCheck(t, cfg, expiresInThreeDays)
|
||||
|
||||
title := "TLS Expiry Warning: " + testHost
|
||||
|
||||
// The second check comes within the TLS interval of the first,
|
||||
// so it must not warn again.
|
||||
var warnings int
|
||||
|
||||
deps := runChecks(t, cfg, expiresInThreeDays, func(deps *testDeps) {
|
||||
warnings = countNotifications(deps, title)
|
||||
})
|
||||
|
||||
warnings := countNotifications(deps, title)
|
||||
if warnings == 0 {
|
||||
t.Fatal("expected expiry warnings from the first check")
|
||||
}
|
||||
|
||||
// The second check comes within the TLS interval of the first,
|
||||
// so it must not warn again.
|
||||
runCheck(t, w, deps)
|
||||
|
||||
got := countNotifications(deps, title)
|
||||
if got != warnings {
|
||||
t.Errorf(
|
||||
@@ -525,7 +529,7 @@ func TestDNSRunsBeforePortAndTLSChecks(t *testing.T) {
|
||||
cfg.Hostnames = []string{testHost}
|
||||
|
||||
// The saved state says the last check found testHost at oldIP.
|
||||
deps := runChecks(t, cfg, func(deps *testDeps) {
|
||||
_, deps := runFirstCheck(t, cfg, func(deps *testDeps) {
|
||||
deps.state.SetHostnameState(testHost, &state.HostnameState{
|
||||
RecordsByNameserver: map[string]*state.NameserverRecordState{
|
||||
oldNS1: {
|
||||
@@ -534,7 +538,7 @@ func TestDNSRunsBeforePortAndTLSChecks(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
}, nil)
|
||||
})
|
||||
|
||||
snap := deps.state.GetSnapshot()
|
||||
|
||||
@@ -665,9 +669,10 @@ func TestNSFailureAndRecovery(t *testing.T) {
|
||||
cfg := defaultTestConfig(t)
|
||||
cfg.Hostnames = []string{testHost}
|
||||
|
||||
// Between the checks, save every nameserver the first check found
|
||||
// as failed, and add, as answering, one that live DNS does not list.
|
||||
deps := runChecks(t, cfg, nil, func(deps *testDeps) {
|
||||
w, deps := runFirstCheck(t, cfg, nil)
|
||||
|
||||
// Save every nameserver the first check found as failed, and add
|
||||
// one that live DNS does not list as having answered.
|
||||
hs, _ := deps.state.GetHostnameState(testHost)
|
||||
for _, nsState := range hs.RecordsByNameserver {
|
||||
nsState.Status = "error"
|
||||
@@ -679,7 +684,8 @@ func TestNSFailureAndRecovery(t *testing.T) {
|
||||
}
|
||||
|
||||
deps.state.SetHostnameState(testHost, hs)
|
||||
})
|
||||
|
||||
runCheck(t, w, deps)
|
||||
|
||||
assertNotified(t, deps, "NS Failure: "+testHost, "error")
|
||||
assertNotified(t, deps, "NS Recovery: "+testHost, "success")
|
||||
|
||||
Reference in New Issue
Block a user