checker-dnsviz/checker/rules_status_test.go

189 lines
5.6 KiB
Go

// 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)
}
}