package traverse import ( "testing" "github.com/miekg/dns" ) func newTestReferral(server string, ips []string) *Referral { r := &Referral{ RefID: "1", Qname: "www.example.com", Qclass: dns.ClassINET, Qtype: dns.TypeA, NSAType: dns.TypeA, Server: server, ServerIPs: ips, Bailiwick: "com", InfoCache: NewInfoCache(nil), Status: RefStatusNormal, Responses: make(map[string]*ServerResponse), Children: make(map[string][]*Referral), ServerWeights: make(map[string]float64), maxdepth: DefaultMaxDepth, } for _, ip := range ips { r.ServerWeights[ip] = 1.0 / float64(len(ips)) } return r } func TestRefidDepth(t *testing.T) { tests := []struct { refid string want int }{ {"", 0}, {"1", 1}, {"1.1.2", 3}, {"1.2.0.1", 3}, {"1.1.2.0.1.4.2.0.2.0.2", 8}, } for _, tt := range tests { if got := refidDepth(tt.refid); got != tt.want { t.Errorf("refidDepth(%q) = %d, want %d", tt.refid, got, tt.want) } } } func TestTxtIPsVerbose(t *testing.T) { r := newTestReferral("ns1.example.com", []string{"2.2.2.2", "1.1.1.1"}) if got := r.TxtIPsVerbose(); got != "50.0%=1.1.1.1,50.0%=2.2.2.2" { t.Errorf("TxtIPsVerbose = %q", got) } // key: pseudo entries keep only the first two fields. r2 := newTestReferral("ns2.example.com", nil) r2.ServerIPs = []string{"key:noglue:9.9.9.9:www.example.com:IN:A:x:y"} r2.ServerWeights = map[string]float64{r2.ServerIPs[0]: 1.0} if got := r2.TxtIPsVerbose(); got != "100.0%=noglue:9.9.9.9" { t.Errorf("TxtIPsVerbose key entry = %q", got) } var unresolved Referral if got := unresolved.TxtIPsVerbose(); got != "" { t.Errorf("unresolved TxtIPsVerbose = %q, want empty", got) } } func TestFastKeyLowercases(t *testing.T) { r := newTestReferral("NS1.Example.COM", []string{"1.1.1.1"}) r.Server = "NS1.Example.COM" // bypass canonicalisation to prove downcasing key := fastKey(r) if key != "www.example.com:in:a:ns1.example.com:100.0%=1.1.1.1" { t.Errorf("fastKey = %q", key) } } func TestIsNoGlueAndIsLoop(t *testing.T) { inBailiwick := newTestReferral("ns1.sub.com", nil) if !inBailiwick.isNoGlue() { t.Error("in-bailiwick NS without addresses should be noglue") } outOfBailiwick := newTestReferral("ns1.other.net", nil) if outOfBailiwick.isNoGlue() { t.Error("out-of-bailiwick NS is resolvable, not noglue") } resolved := newTestReferral("ns1.sub.com", []string{"1.1.1.1"}) if resolved.isNoGlue() || resolved.isLoop() { t.Error("resolved referral is neither noglue nor loop") } parent := newTestReferral("ns.b.net", nil) parent.Qname = "ns.a.net" child := newTestReferral("ns.b.net", nil) child.Qname = "ns.a.net" child.Parent = parent if !child.isLoop() { t.Error("same qname/qclass/qtype/server with unresolved ancestor should loop") } child.Server = "ns.c.net" if child.isLoop() { t.Error("different server must not loop") } } func TestChainHasQuery(t *testing.T) { parent := newTestReferral("ns1.example.com", []string{"1.1.1.1"}) parent.Qname = "www.a.com" child := newTestReferral("ns2.example.com", []string{"2.2.2.2"}) child.Qname = "www.b.net" child.Parent = parent if !child.chainHasQuery("www.a.com") { t.Error("ancestor qname should be found") } if !child.chainHasQuery("WWW.B.NET.") { t.Error("own qname should be found case-insensitively") } if child.chainHasQuery("www.c.org") { t.Error("unknown qname must not match") } } func TestReplaceChild(t *testing.T) { parent := newTestReferral("ns1.example.com", []string{"1.1.1.1"}) before := newTestReferral("ns2.example.com", []string{"2.2.2.2"}) after := newTestReferral("ns2.example.com", []string{"2.2.2.2"}) parent.Children["1.1.1.1"] = []*Referral{before} parent.Resolves = []*Referral{before} parent.replaceChild(before, after) if parent.Children["1.1.1.1"][0] != after || parent.Resolves[0] != after { t.Error("replaceChild did not swap the node everywhere") } if before.ReplacedBy != after { t.Error("replaced node should point at its replacement") } } func TestOverallStatus(t *testing.T) { r := newTestReferral("ns1.example.com", nil) r.Status = RefStatusNoGlue if got := r.OverallStatus(); got != StatusNoGlue { t.Errorf("noglue overall = %q", got) } r.Status = RefStatusLoop if got := r.OverallStatus(); got != StatusLoop { t.Errorf("loop overall = %q", got) } n := newTestReferral("ns1.example.com", []string{"1.1.1.1", "2.2.2.2"}) if got := n.OverallStatus(); got != "" { t.Errorf("no responses overall = %q, want empty", got) } n.Responses["1.1.1.1"] = &ServerResponse{Status: StatusAnswered} if got := n.OverallStatus(); got != StatusAnswered { t.Errorf("single status overall = %q", got) } n.Responses["2.2.2.2"] = &ServerResponse{Status: StatusError} if got := n.OverallStatus(); got != "mixed" { t.Errorf("mixed overall = %q", got) } } func TestIPsAsArraySkipsPseudoKeys(t *testing.T) { r := newTestReferral("ns1.example.com", []string{"1.1.1.1", "key:noglue:2.2.2.2"}) got := r.IPsAsArray() if len(got) != 1 || got[0] != "1.1.1.1" { t.Errorf("IPsAsArray = %v", got) } } func TestToASCII(t *testing.T) { if got := toASCII("bücher.example"); got != "xn--bcher-kva.example" { t.Errorf("toASCII = %q", got) } if got := toASCII("plain.example"); got != "plain.example" { t.Errorf("ascii name changed: %q", got) } if got := toASCII(""); got != "" { t.Errorf("empty name changed: %q", got) } } func TestServerResponseString(t *testing.T) { noglue := NewNoGlueResponse("www.example.com", dns.ClassINET, dns.TypeA, "1.1.1.1", "ns1.example.com", "example.com") if got := noglue.String(); got != "No glue for ns1.example.com" { t.Errorf("noglue String = %q", got) } loop := NewLoopResponse("www.example.com", dns.ClassINET, dns.TypeA, "1.1.1.1", "ns1.example.com", "example.com") if got := loop.String(); got != "Loop encountered resolving ns1.example.com" { t.Errorf("loop String = %q", got) } }