- Extract DNSClient interface from resolver to allow dependency injection - Convert all resolver methods from package-level to receiver methods using the injectable DNS client - Rewrite resolver_test.go with a mock DNS client that simulates the full delegation chain (root → TLD → authoritative) in-process - Move 2 integration tests (real DNS) behind //go:build integration tag - Add NewFromLoggerWithClient constructor for test injection - Add LookupAllRecords implementation (was returning ErrNotImplemented) All unit tests are hermetic (no network) and complete in <1s. Total make check passes in ~5s. Closes #12
49 lines
968 B
Go
49 lines
968 B
Go
package resolver
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
// DNSClient abstracts DNS wire-protocol exchanges so the resolver
|
|
// can be tested without hitting real nameservers.
|
|
type DNSClient interface {
|
|
ExchangeContext(
|
|
ctx context.Context,
|
|
msg *dns.Msg,
|
|
addr string,
|
|
) (*dns.Msg, time.Duration, error)
|
|
}
|
|
|
|
// udpClient wraps a real dns.Client for production use.
|
|
type udpClient struct {
|
|
timeout time.Duration
|
|
}
|
|
|
|
func (c *udpClient) ExchangeContext(
|
|
ctx context.Context,
|
|
msg *dns.Msg,
|
|
addr string,
|
|
) (*dns.Msg, time.Duration, error) {
|
|
cl := &dns.Client{Timeout: c.timeout}
|
|
|
|
return cl.ExchangeContext(ctx, msg, addr)
|
|
}
|
|
|
|
// tcpClient wraps a real dns.Client using TCP.
|
|
type tcpClient struct {
|
|
timeout time.Duration
|
|
}
|
|
|
|
func (c *tcpClient) ExchangeContext(
|
|
ctx context.Context,
|
|
msg *dns.Msg,
|
|
addr string,
|
|
) (*dns.Msg, time.Duration, error) {
|
|
cl := &dns.Client{Net: "tcp", Timeout: c.timeout}
|
|
|
|
return cl.ExchangeContext(ctx, msg, addr)
|
|
}
|