- Unit tests for all internal packages exceeding 80% coverage:
- internal/config: 86.2% (ParseMaxDepth, ParseRetries, validation paths)
- internal/dns: 82.9% (IterativeQueryWithExchange, mock DNS server, roots)
- internal/fingerprint: 96.4% (unchanged, already excellent)
- internal/output: 86.7% (formatters, stats, JSON/text output, hooks)
- internal/traverse: 86.7% (SetHooks, ResolveNS, processReferral,
ensureRDFalse, resolveGlueViaSystem, newAQuery, Referral.Resolve)
- Integration tests in internal/integration/:
- End-to-end traversal with mock DNS exchange function
- Referral chain traversal (root -> TLD -> authoritative)
- CNAME resolution and loop detection
- NXDOMAIN and SERVFAIL response handling
- Max depth enforcement
- Context cancellation
- TraverserHooks event delivery
- Mock DNS server helper in internal/dns/roots_test.go using miekg/dns
(enables deterministic testing without network dependency)
- CI updated with coverage reporting step
- Makefile: added 'cover' target for local HTML coverage reports
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Copilot
multica-agent
parent
fe1afe2a97
commit
5d1e5ca86c
@@ -186,3 +186,190 @@ func TestRunTraversalUsesHooks(t *testing.T) {
|
||||
t.Fatalf("expected formatted summary output, got %q", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONFormatterWriteResolveAndResult(t *testing.T) {
|
||||
ref := traverse.NewReferral("example.com.", dns.TypeA, ".", 0, 1.0, nil)
|
||||
server := net.ParseIP("198.41.0.4")
|
||||
resp := &traverse.Response{
|
||||
Referral: ref,
|
||||
Server: server,
|
||||
Type: traverse.RespAnswer,
|
||||
Decoded: &dns.DecodedResponse{
|
||||
Answers: []miekgdns.RR{
|
||||
&miekgdns.A{
|
||||
Hdr: miekgdns.RR_Header{Name: "example.com.", Rrtype: dns.TypeA, Class: miekgdns.ClassINET},
|
||||
A: net.ParseIP("1.2.3.4"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
cfg := DefaultConfig()
|
||||
cfg.Format = FormatJSON
|
||||
cfg.Domain = "example.com"
|
||||
cfg.QueryType = "A"
|
||||
cfg.ShowResolves = true
|
||||
cfg.ShowAllStats = true
|
||||
cfg.ShowProgress = true
|
||||
f := NewFormatter(cfg, &buf).(*jsonFormatter)
|
||||
|
||||
// WriteResolve
|
||||
if err := f.WriteResolve(traverse.TraversalEvent{
|
||||
Stage: traverse.EventStart,
|
||||
Result: traverse.TraversalResult{Referral: ref, Response: resp},
|
||||
}); err != nil {
|
||||
t.Fatalf("WriteResolve: %v", err)
|
||||
}
|
||||
|
||||
// WriteResult
|
||||
if err := f.WriteResult(traverse.TraversalResult{Referral: ref, Response: resp}); err != nil {
|
||||
t.Fatalf("WriteResult: %v", err)
|
||||
}
|
||||
|
||||
// WriteProgress with EventComplete to cover stageName "complete"
|
||||
if err := f.WriteProgress(traverse.TraversalEvent{
|
||||
Stage: traverse.EventComplete,
|
||||
Result: traverse.TraversalResult{Referral: ref, Response: resp},
|
||||
}); err != nil {
|
||||
t.Fatalf("WriteProgress EventComplete: %v", err)
|
||||
}
|
||||
|
||||
if err := f.Flush(); err != nil {
|
||||
t.Fatalf("Flush: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONFormatterWriteResolveFlagOff(t *testing.T) {
|
||||
ref := traverse.NewReferral("example.com.", dns.TypeA, ".", 0, 1.0, nil)
|
||||
|
||||
var buf bytes.Buffer
|
||||
cfg := DefaultConfig()
|
||||
cfg.Format = FormatJSON
|
||||
cfg.ShowResolves = false
|
||||
cfg.ShowAllStats = false
|
||||
f := NewFormatter(cfg, &buf).(*jsonFormatter)
|
||||
|
||||
if err := f.WriteResolve(traverse.TraversalEvent{
|
||||
Stage: traverse.EventStart,
|
||||
Result: traverse.TraversalResult{Referral: ref},
|
||||
}); err != nil {
|
||||
t.Fatalf("WriteResolve: %v", err)
|
||||
}
|
||||
if err := f.WriteResult(traverse.TraversalResult{Referral: ref}); err != nil {
|
||||
t.Fatalf("WriteResult: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONFormatterWriteSummaryWithServers(t *testing.T) {
|
||||
ref := traverse.NewReferral("example.com.", dns.TypeA, "com.", 1, 1.0, nil)
|
||||
server := net.ParseIP("1.2.3.4")
|
||||
resp := &traverse.Response{
|
||||
Referral: ref,
|
||||
Server: server,
|
||||
Type: traverse.RespAnswer,
|
||||
Decoded: &dns.DecodedResponse{
|
||||
Answers: []miekgdns.RR{
|
||||
&miekgdns.A{
|
||||
Hdr: miekgdns.RR_Header{Name: "example.com.", Rrtype: dns.TypeA, Class: miekgdns.ClassINET},
|
||||
A: net.ParseIP("1.2.3.4"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
results := []traverse.TraversalResult{{Referral: ref, Response: resp}}
|
||||
|
||||
var buf bytes.Buffer
|
||||
cfg := DefaultConfig()
|
||||
cfg.Format = FormatJSON
|
||||
cfg.Domain = "example.com"
|
||||
cfg.QueryType = "A"
|
||||
cfg.ShowServers = true
|
||||
cfg.ShowVersions = false
|
||||
cfg.ShowResults = true
|
||||
cfg.ShowSummaryResults = true
|
||||
f := NewFormatter(cfg, &buf).(*jsonFormatter)
|
||||
|
||||
if err := f.WriteSummary(results); err != nil {
|
||||
t.Fatalf("WriteSummary: %v", err)
|
||||
}
|
||||
if err := f.Flush(); err != nil {
|
||||
t.Fatalf("Flush: %v", err)
|
||||
}
|
||||
|
||||
var payload map[string]any
|
||||
if err := json.Unmarshal(buf.Bytes(), &payload); err != nil {
|
||||
t.Fatalf("invalid JSON: %v\n%s", err, buf.String())
|
||||
}
|
||||
if _, ok := payload["servers"]; !ok {
|
||||
t.Error("expected 'servers' field in JSON output")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewFormatterNilWriter(t *testing.T) {
|
||||
// Should not panic with nil writer
|
||||
cfg := DefaultConfig()
|
||||
f := NewFormatter(cfg, nil)
|
||||
if f == nil {
|
||||
t.Error("NewFormatter should not return nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttachHooksShowResolves(t *testing.T) {
|
||||
ref := traverse.NewReferral("example.com.", dns.TypeA, ".", 0, 1.0, nil)
|
||||
server := net.ParseIP("1.2.3.4")
|
||||
resp := &traverse.Response{
|
||||
Referral: ref,
|
||||
Server: server,
|
||||
Type: traverse.RespAnswer,
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
cfg := DefaultConfig()
|
||||
cfg.ShowProgress = false
|
||||
cfg.ShowResolves = true
|
||||
cfg.ShowAllStats = true
|
||||
cfg.Color = false
|
||||
formatter := NewFormatter(cfg, &buf)
|
||||
hooks := AttachHooks(cfg, formatter)
|
||||
|
||||
// Trigger a resolve event
|
||||
hooks.OnEvent(traverse.TraversalEvent{
|
||||
Stage: traverse.EventStart,
|
||||
IsResolve: true,
|
||||
Result: traverse.TraversalResult{Referral: ref, Response: resp},
|
||||
})
|
||||
|
||||
if buf.Len() == 0 {
|
||||
t.Error("expected resolve output when ShowResolves is true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttachHooksShowAllStats(t *testing.T) {
|
||||
ref := traverse.NewReferral("example.com.", dns.TypeA, ".", 0, 1.0, nil)
|
||||
server := net.ParseIP("1.2.3.4")
|
||||
resp := &traverse.Response{
|
||||
Referral: ref,
|
||||
Server: server,
|
||||
Type: traverse.RespAnswer,
|
||||
Decoded: &dns.DecodedResponse{
|
||||
Answers: []miekgdns.RR{
|
||||
&miekgdns.A{Hdr: miekgdns.RR_Header{Name: "example.com.", Rrtype: dns.TypeA, Class: miekgdns.ClassINET}, A: net.ParseIP("1.2.3.4")},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
cfg := DefaultConfig()
|
||||
cfg.ShowProgress = false
|
||||
cfg.ShowResolves = false
|
||||
cfg.ShowAllStats = true
|
||||
cfg.Color = false
|
||||
formatter := NewFormatter(cfg, &buf)
|
||||
hooks := AttachHooks(cfg, formatter)
|
||||
|
||||
hooks.OnEvent(traverse.TraversalEvent{
|
||||
Stage: traverse.EventComplete,
|
||||
Result: traverse.TraversalResult{Referral: ref, Response: resp},
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user