// SPDX-License-Identifier: MIT package checker import ( "context" "errors" "strings" "testing" sdk "git.happydns.org/checker-sdk-go/checker" ) func sampleData() *DNSVizData { return &DNSVizData{ Domain: "example.com", Order: []string{"example.com.", "com.", "."}, Zones: map[string]ZoneAnalysis{ "example.com.": { Status: "BOGUS", Errors: []Finding{{Code: "RRSIG_EXPIRED", Description: "signature has expired"}}, }, "com.": {Status: "SECURE"}, ".": {Status: "SECURE"}, }, } } func TestOverallStatusRule(t *testing.T) { r := &overallStatusRule{} states := r.Evaluate(context.Background(), stubObs{value: sampleData()}, nil) if len(states) != 1 { t.Fatalf("expected 1 state, got %d", len(states)) } if states[0].Status != sdk.StatusCrit { t.Errorf("expected StatusCrit for BOGUS leaf, got %v", states[0].Status) } if states[0].Subject != "example.com." { t.Errorf("subject: got %q", states[0].Subject) } } func TestOverallStatusRule_FallbackToFirstZone(t *testing.T) { d := &DNSVizData{ Domain: "missing", Order: []string{"other.zone."}, Zones: map[string]ZoneAnalysis{"other.zone.": {Status: "SECURE"}}, } r := &overallStatusRule{} states := r.Evaluate(context.Background(), stubObs{value: d}, nil) if states[0].Subject != "other.zone." { t.Errorf("expected fallback to first zone, got %q", states[0].Subject) } if states[0].Status != sdk.StatusOK { t.Errorf("expected SECURE -> OK, got %v", states[0].Status) } } func TestOverallStatusRule_NoZones(t *testing.T) { r := &overallStatusRule{} states := r.Evaluate(context.Background(), stubObs{value: &DNSVizData{Domain: "x"}}, nil) if states[0].Status != sdk.StatusUnknown { t.Errorf("expected Unknown for empty zones, got %v", states[0].Status) } } func TestOverallStatusRule_LoadError(t *testing.T) { r := &overallStatusRule{} states := r.Evaluate(context.Background(), stubObs{err: errors.New("nope")}, nil) if len(states) != 1 || states[0].Status != sdk.StatusError { t.Errorf("expected one error state, got %+v", states) } } func TestPerZoneStatusRule(t *testing.T) { r := &perZoneStatusRule{} states := r.Evaluate(context.Background(), stubObs{value: sampleData()}, nil) if len(states) != 3 { t.Fatalf("expected 3 per-zone states, got %d", len(states)) } subjects := make([]string, len(states)) for i, s := range states { subjects[i] = s.Subject } want := []string{"example.com.", "com.", "."} for i := range want { if subjects[i] != want[i] { t.Errorf("subjects[%d]=%q, want %q", i, subjects[i], want[i]) } } } func TestPerZoneStatusRule_NoZones(t *testing.T) { r := &perZoneStatusRule{} states := r.Evaluate(context.Background(), stubObs{value: &DNSVizData{}}, nil) if len(states) != 1 || states[0].Status != sdk.StatusUnknown { t.Errorf("expected single Unknown state, got %+v", states) } } func TestZoneErrorsRule(t *testing.T) { r := &zoneErrorsRule{} states := r.Evaluate(context.Background(), stubObs{value: sampleData()}, nil) if len(states) != 1 { t.Fatalf("expected 1 error state, got %d", len(states)) } if states[0].Status != sdk.StatusCrit { t.Errorf("expected Crit, got %v", states[0].Status) } if states[0].Code != "RRSIG_EXPIRED" { t.Errorf("expected the finding code to be used, got %q", states[0].Code) } } func TestZoneErrorsRule_NoFindings(t *testing.T) { d := &DNSVizData{Order: []string{"a."}, Zones: map[string]ZoneAnalysis{"a.": {Status: "SECURE"}}} r := &zoneErrorsRule{} states := r.Evaluate(context.Background(), stubObs{value: d}, nil) if len(states) != 1 || states[0].Status != sdk.StatusOK { t.Errorf("expected OK summary state, got %+v", states) } } func TestZoneWarningsRule(t *testing.T) { d := &DNSVizData{ Order: []string{"a."}, Zones: map[string]ZoneAnalysis{"a.": {Warnings: []Finding{{Description: "soft"}}}}, } r := &zoneWarningsRule{} states := r.Evaluate(context.Background(), stubObs{value: d}, nil) if len(states) != 1 || states[0].Status != sdk.StatusWarn { t.Errorf("expected Warn state, got %+v", states) } // Code falls back to the rule code when finding has none. if states[0].Code != "dnsviz_zone_warnings" { t.Errorf("expected fallback code, got %q", states[0].Code) } } func TestEmptyAsUnknown(t *testing.T) { if emptyAsUnknown("") != "UNKNOWN" { t.Error("empty should map to UNKNOWN") } if emptyAsUnknown("X") != "X" { t.Error("non-empty should pass through") } } func TestNonEmpty(t *testing.T) { if nonEmpty("a", "b") != "a" || nonEmpty("", "b") != "b" { t.Error("nonEmpty did not pick non-empty") } } func TestFindingMeta(t *testing.T) { if findingMeta(Finding{}) != nil { t.Error("expected nil for empty finding") } m := findingMeta(Finding{Code: "C", Servers: []string{"a"}}) if m["code"] != "C" { t.Errorf("missing code in meta: %v", m) } srvs, _ := m["servers"].([]string) if len(srvs) != 1 || srvs[0] != "a" { t.Errorf("missing servers in meta: %v", m) } } func TestZoneErrorsRule_ConcatPerZone(t *testing.T) { d := &DNSVizData{ Order: []string{"leaf.", "tld."}, Zones: map[string]ZoneAnalysis{ "leaf.": {Errors: []Finding{{Description: "leaf-err"}}}, "tld.": {Errors: []Finding{{Description: "tld-err"}}}, }, } r := &zoneErrorsRule{} states := r.Evaluate(context.Background(), stubObs{value: d}, nil) if len(states) != 2 { t.Fatalf("expected 2 states, got %d", len(states)) } // Subjects should both appear, leaf first per Order. if states[0].Subject != "leaf." || states[1].Subject != "tld." { t.Errorf("subjects out of order: %q,%q", states[0].Subject, states[1].Subject) } if !strings.Contains(states[0].Message, "leaf-err") { t.Errorf("leaf message lost: %q", states[0].Message) } }