check / check (push) Successful in 1m37s
Rebasing this branch onto next surfaced two failures that the branch
did not have in isolation. Both come from the change this PR makes:
the watcher package now queries live DNS, and it is the second package
to do so.
Burst fan-out in the watcher package. All 13 watcher tests are
parallel and each runs a full iterative resolution, so they hit the
same root servers in the same instant and get rate-limited. This is
exactly the pathology internal/resolver/livedns_test.go was written to
prevent (9cb2c2b); that gate is package-scoped and cannot reach into
this package's test binary, so liveWatcherGate is its counterpart
here, acquired in newTestWatcher and released when the test ends.
Cross-package oversubscription. Go runs package binaries in parallel,
so with both live-DNS packages in flight their gates sum rather than
hold. The excess is rate-limited and the resolver's 8s per-attempt
deadlines expire, failing ResolveIPAddresses tests this branch never
touched. script/test now passes -p 1 so each gate is authoritative
while its package runs. Serialising is also net faster here, because
the retries it removes cost more than the lost parallelism: resolver
16-22s (was 30-36s under contention), watcher 12-13s (was 27-37s),
whole suite 39-46s against the 60s cap and the 90s -timeout backstop.
TestQueryNameserverIP_UnreachableServer is dropped rather than fixed.
It asserted that a query to an RFC 5737 documentation address comes
back non-OK with no records, which does not hold in the build
environment: that network transparently intercepts all UDP/53 traffic
regardless of destination and answers it locally, so the query returns
StatusOK with 9 real records for example.com. Verified directly with
dig @192.0.2.1 inside the build network. The mock DNSClient that used
to force this classification is what this PR removes, and a live
substitute would only be testing the sandbox's network behaviour, so
the coverage gap is recorded in a comment where the test was.
Verified: three consecutive `docker build .` runs green, after three
consecutive failures without these changes.
56 lines
2.5 KiB
Markdown
56 lines
2.5 KiB
Markdown
# Testing Policy
|
||
|
||
## DNS Resolution Tests
|
||
|
||
All tests that involve DNS resolution — in every package, including
|
||
consumers of the resolver such as the watcher — **MUST** use live
|
||
queries against real DNS servers. No mocking, faking, or stubbing of
|
||
DNS at any layer is permitted.
|
||
|
||
### Rationale
|
||
|
||
The resolver performs iterative resolution from root nameservers through
|
||
the full delegation chain. Mocked responses cannot faithfully represent
|
||
the variety of real-world DNS behavior (truncation, referrals, glue
|
||
records, DNSSEC, varied response times, EDNS, etc.). Testing against
|
||
real servers ensures the resolver works correctly in production.
|
||
Robustness comes from handling real-world DNS behavior with tolerant
|
||
assertions and sensible timeouts, not from mocks.
|
||
|
||
### Constraints
|
||
|
||
- Tests hit real DNS infrastructure and require network access
|
||
- Test duration depends on network conditions; timeout tuning keeps
|
||
the suite within the 60-second target
|
||
- Query timeout is calibrated to 3× maximum antipodal RTT (~300ms)
|
||
plus processing margin
|
||
- Root server fan-out is limited to reduce parallel query load
|
||
- Flaky failures from transient network issues are acceptable and
|
||
should be investigated as potential resolver bugs, not papered over
|
||
with mocks or skip flags
|
||
- Watcher change-detection tests seed a synthetic *previous state*
|
||
and compare it against fresh live lookups; the DNS side is never
|
||
faked
|
||
- Live query concurrency is bounded per package (`liveGate` in
|
||
`internal/resolver`, `liveWatcherGate` in `internal/watcher`) so
|
||
parallel tests do not burst at the root servers
|
||
- Those gates are package-scoped and therefore per test binary, so
|
||
`script/test` also passes `-p 1`: with both live-DNS packages
|
||
running at once the gates sum instead of holding, and the resolver's
|
||
per-attempt deadlines start expiring
|
||
|
||
### What NOT to do
|
||
|
||
- **Do not mock `DNSClient`**, the watcher's `DNSResolver` interface,
|
||
or any other DNS abstraction — in any package, for any reason
|
||
- **Do not add `-short` flags** to skip slow tests
|
||
- **Do not increase `-timeout`** to hide hanging queries
|
||
- **Do not remove `-count=1` from `script/test`** — Go's test cache
|
||
replays a previous run's output without querying anything, so a
|
||
cached pass is not evidence that live resolution works
|
||
- **Do not remove `-p 1` from `script/test`** — running the live-DNS
|
||
packages in parallel oversubscribes live DNS past what their gates
|
||
bound, which surfaces as unrelated resolver tests failing on
|
||
expired deadlines
|
||
- **Do not modify linter configuration** to suppress findings
|