# 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 ### Transport failures: loopback nameservers, not mocks The resolver classifies a nameserver that stays silent as `StatusTimeout` and one that answers SERVFAIL as `StatusError`. The public network cannot be made to produce either on demand — a black-holed address is only black-holed on some networks, and build environments that transparently intercept UDP/53 answer it locally — so a test built on a chosen remote address asserts on the network it happens to run on rather than on the resolver. `internal/resolver/transport_test.go` binds a real nameserver on `127.0.0.1` instead and points the query at it. `nameserverAddr` dials an address that already carries a port as written, so no production behaviour is bypassed to arrange this. **This is permitted, and it is not a mock.** The rule above bans substituting `DNSClient` or any other DNS abstraction, which lets the code under test skip DNS and hands it a manufactured verdict. A loopback nameserver does the opposite: the resolver dials a real socket, writes a real query with the real `miekg/dns` client, and applies its real deadline and its real classification logic to what comes back. Choosing which nameserver a live query is sent to is not faking DNS — the resolver is aimed at a nameserver of the caller's choosing in production too. The distinction to hold on to: **substituting the client is banned; choosing the server is not.** A test that reaches for a fake `DNSClient` to force a classification is still forbidden, no matter how awkward the alternative looks. Such a test must stay cheap. The resolver asks for eight record types and retries each once, so a nameserver silent on every type costs sixteen query timeouts. `TestQueryNameserverIP_Timeout` is silent on `A` alone and answers the rest, which is all the resolver needs to classify the response and keeps the test to two. ### 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