package dns import ( "net" "testing" ) func TestRootHintsCount(t *testing.T) { if len(RootHints) != 13 { t.Errorf("expected 13 root hints, got %d", len(RootHints)) } } func TestRootHintsNames(t *testing.T) { wantNames := []string{ "a.root-servers.net.", "b.root-servers.net.", "c.root-servers.net.", "d.root-servers.net.", "e.root-servers.net.", "f.root-servers.net.", "g.root-servers.net.", "h.root-servers.net.", "i.root-servers.net.", "j.root-servers.net.", "k.root-servers.net.", "l.root-servers.net.", "m.root-servers.net.", } for i, rs := range RootHints { if rs.Name != wantNames[i] { t.Errorf("RootHints[%d].Name = %q, want %q", i, rs.Name, wantNames[i]) } } } func TestRootHintsHaveIPv4(t *testing.T) { for _, rs := range RootHints { if len(rs.IPv4) == 0 { t.Errorf("root server %q has no IPv4 address", rs.Name) } for _, ip := range rs.IPv4 { if ip.To4() == nil { t.Errorf("root server %q: expected IPv4, got %v", rs.Name, ip) } } } } func TestRootHintsHaveIPv6(t *testing.T) { for _, rs := range RootHints { if len(rs.IPv6) == 0 { t.Errorf("root server %q has no IPv6 address", rs.Name) } for _, ip := range rs.IPv6 { if ip.To4() != nil { t.Errorf("root server %q: expected IPv6, got IPv4-mappable %v", rs.Name, ip) } } } } func TestRootHintsAllIPsIPv4Only(t *testing.T) { for _, rs := range RootHints { ips := rs.AllIPs(false) if len(ips) != len(rs.IPv4) { t.Errorf("root server %q: AllIPs(false) = %d, want %d", rs.Name, len(ips), len(rs.IPv4)) } } } func TestRootHintsAllIPsBoth(t *testing.T) { for _, rs := range RootHints { ips := rs.AllIPs(true) want := len(rs.IPv4) + len(rs.IPv6) if len(ips) != want { t.Errorf("root server %q: AllIPs(true) = %d, want %d", rs.Name, len(ips), want) } } } func TestRootHintsNoParseFail(t *testing.T) { // Ensure none of the IPs failed to parse (net.ParseIP returns nil on failure). for _, rs := range RootHints { for _, ip := range rs.IPv4 { if ip == nil { t.Errorf("root server %q has nil IPv4 (parse failed)", rs.Name) } } for _, ip := range rs.IPv6 { if ip == nil { t.Errorf("root server %q has nil IPv6 (parse failed)", rs.Name) } } } } func TestRootHintsKnownAddress(t *testing.T) { // Spot-check a.root-servers.net. which has been stable for decades. for _, rs := range RootHints { if rs.Name == "a.root-servers.net." { want := net.ParseIP("198.41.0.4") if !rs.IPv4[0].Equal(want) { t.Errorf("a.root-servers.net. IPv4 = %v, want %v", rs.IPv4[0], want) } return } } t.Error("a.root-servers.net. not found in RootHints") } func TestResolverFromConfigExplicit(t *testing.T) { cfg := &RootDiscoveryConfig{Resolver: "8.8.8.8:53"} got := resolverFromConfig(cfg) if got != "8.8.8.8:53" { t.Errorf("resolverFromConfig = %q, want 8.8.8.8:53", got) } } func TestResolverFromConfigEmpty(t *testing.T) { cfg := &RootDiscoveryConfig{} got := resolverFromConfig(cfg) // Should return the system resolver; just check it's non-empty and contains a port. if got == "" { t.Error("resolverFromConfig with empty Resolver returned empty string") } } func TestResolverFromConfigNil(t *testing.T) { got := resolverFromConfig(nil) if got == "" { t.Error("resolverFromConfig(nil) returned empty string") } } func TestSystemResolverNonEmpty(t *testing.T) { got := systemResolver() if got == "" { t.Error("systemResolver() returned empty string") } // Must contain a colon (host:port format). host, port, err := splitHostPort(got) if err != nil { t.Errorf("systemResolver() = %q: not a valid host:port: %v", got, err) } if host == "" { t.Errorf("systemResolver() host is empty in %q", got) } if port == "" { t.Errorf("systemResolver() port is empty in %q", got) } } // splitHostPort is a thin wrapper around net.SplitHostPort for test use. func splitHostPort(addr string) (host, port string, err error) { return net.SplitHostPort(addr) }