package traverse import ( "net" "testing" "github.com/hits/ExploreDNS/internal/dns" ) const TypeA = dns.TypeA type State = ResolutionState func TestNewReferral(t *testing.T) { ref := NewReferral("example.com", TypeA, ".", 0, 1.0, nil) if ref.Name != "example.com." { t.Errorf("Name = %q, want %q", ref.Name, "example.com.") } if ref.Qtype != TypeA { t.Errorf("Qtype = %d, want %d", ref.Qtype, TypeA) } if ref.Qclass != 1 { t.Errorf("Qclass = %d, want 1", ref.Qclass) } if ref.State != StateUnresolved { t.Errorf("State = %d, want %d", ref.State, StateUnresolved) } if ref.Depth != 0 { t.Errorf("Depth = %d, want 0", ref.Depth) } if ref.Prob != 1.0 { t.Errorf("Prob = %f, want 1.0", ref.Prob) } if ref.Parent != nil { t.Error("Parent should be nil") } } func TestReferralInBailiwick(t *testing.T) { tests := []struct { name string bailiwick string testName string want bool }{ {"root bailiwick accepts all", ".", "example.com.", true}, {"empty bailiwick accepts all", "", "example.com.", true}, {"subdomain in bailiwick", "com.", "example.com.", true}, {"deeper subdomain", "com.", "www.example.com.", true}, {"not in bailiwick", "org.", "example.com.", false}, {"same zone", "example.com.", "example.com.", true}, {"sibling zone", "example.com.", "other.com.", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ref := &Referral{Bailiwick: tt.bailiwick} if got := ref.InBailiwick(tt.testName); got != tt.want { t.Errorf("InBailiwick(%q) = %v, want %v", tt.testName, got, tt.want) } }) } } func TestReferralHasAddresses(t *testing.T) { ref := &Referral{} if ref.HasAddresses() { t.Error("empty referral should not have addresses") } ref.Addresses = []net.IP{net.ParseIP("1.2.3.4")} if !ref.HasAddresses() { t.Error("referral with address should have addresses") } } func TestReferralSetAddresses(t *testing.T) { ref := &Referral{} ref.SetAddresses([]net.IP{net.ParseIP("1.2.3.4")}) if ref.State != StateResolved { t.Errorf("State = %d, want %d", ref.State, StateResolved) } if !ref.HasAddresses() { t.Error("should have addresses after SetAddresses") } ref.SetAddresses(nil) if ref.State != StateUnresolved { t.Errorf("State = %d, want %d", ref.State, StateUnresolved) } } func TestResolutionStateString(t *testing.T) { tests := []struct { state State want string }{ {StateUnresolved, "unresolved"}, {StateResolving, "resolving"}, {StateResolved, "resolved"}, } for _, tt := range tests { t.Run(tt.want, func(t *testing.T) { if got := tt.state.String(); got != tt.want { t.Errorf("String() = %q, want %q", got, tt.want) } }) } } func TestCircularReferralError(t *testing.T) { err := &CircularReferralError{ Name: "ns.example.com.", Chain: []string{"ns1.example.com.", "ns2.example.com."}, } expected := "circular referral detected for ns.example.com.: [ns1.example.com. ns2.example.com.]" if err.Error() != expected { t.Errorf("Error() = %q, want %q", err.Error(), expected) } } func TestUnresolvableNameserverError(t *testing.T) { err := &UnresolvableNameserverError{ Name: "ns.example.com.", Reason: "NXDOMAIN", } expected := "unresolvable nameserver ns.example.com.: NXDOMAIN" if err.Error() != expected { t.Errorf("Error() = %q, want %q", err.Error(), expected) } } func TestGetVisitedNames(t *testing.T) { visited := map[string]bool{ "ns1.example.com.": true, "ns2.example.com.": true, "ns3.example.com.": true, } names := getVisitedNames(visited) if len(names) != 3 { t.Errorf("got %d names, want 3", len(names)) } seen := make(map[string]bool) for _, name := range names { if seen[name] { t.Errorf("duplicate name: %s", name) } seen[name] = true if !visited[name] { t.Errorf("unexpected name: %s", name) } } }