206 lines
5.7 KiB
Go
206 lines
5.7 KiB
Go
package checker
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
|
)
|
|
|
|
func TestProvider(t *testing.T) {
|
|
p := Provider()
|
|
if p == nil {
|
|
t.Fatal("Provider() returned nil")
|
|
}
|
|
if p.Key() != ObservationKey {
|
|
t.Errorf("Key()=%q, want %q", p.Key(), ObservationKey)
|
|
}
|
|
}
|
|
|
|
func TestDefinition(t *testing.T) {
|
|
p := &reverseZoneProvider{}
|
|
def := p.Definition()
|
|
if def == nil {
|
|
t.Fatal("Definition() returned nil")
|
|
}
|
|
if def.ID != "reverse-zone" {
|
|
t.Errorf("ID=%q, want reverse-zone", def.ID)
|
|
}
|
|
if def.Version == "" {
|
|
t.Error("Version is empty")
|
|
}
|
|
if !def.HasHTMLReport {
|
|
t.Error("HasHTMLReport should be true")
|
|
}
|
|
if !def.Availability.ApplyToDomain || !def.Availability.ApplyToZone {
|
|
t.Errorf("Availability=%+v, want both true", def.Availability)
|
|
}
|
|
if len(def.ObservationKeys) != 1 || def.ObservationKeys[0] != ObservationKey {
|
|
t.Errorf("ObservationKeys=%v", def.ObservationKeys)
|
|
}
|
|
if len(def.Rules) == 0 {
|
|
t.Error("Rules() should not be empty")
|
|
}
|
|
if def.Interval == nil || def.Interval.Default == 0 {
|
|
t.Errorf("Interval=%+v", def.Interval)
|
|
}
|
|
// Ensure each user option is documented with non-empty fields.
|
|
for _, opt := range def.Options.UserOpts {
|
|
if opt.Id == "" || opt.Type == "" || opt.Label == "" {
|
|
t.Errorf("incomplete user option: %+v", opt)
|
|
}
|
|
}
|
|
// Ensure domain options include both autofills.
|
|
gotKeys := map[string]bool{}
|
|
for _, opt := range def.Options.DomainOpts {
|
|
gotKeys[opt.Id] = true
|
|
}
|
|
for _, want := range []string{"domain_name", "zone"} {
|
|
if !gotKeys[want] {
|
|
t.Errorf("missing domain option %q", want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestVersionPropagation(t *testing.T) {
|
|
old := Version
|
|
defer func() { Version = old }()
|
|
Version = "v9.9.9-test"
|
|
p := &reverseZoneProvider{}
|
|
def := p.Definition()
|
|
if def.Version != "v9.9.9-test" {
|
|
t.Errorf("def.Version=%q, want v9.9.9-test", def.Version)
|
|
}
|
|
}
|
|
|
|
// TestObservationRoundTrip ensures the published JSON shape stays stable for
|
|
// downstream consumers (the report renderer, related-observation consumers).
|
|
func TestObservationRoundTrip(t *testing.T) {
|
|
in := ReverseZoneData{
|
|
Zone: "1.168.192.in-addr.arpa.",
|
|
IsReverseZone: true,
|
|
PTRCount: 1,
|
|
Entries: []PTREntry{{
|
|
OwnerName: "42.1.168.192.in-addr.arpa.",
|
|
ReverseIP: "192.168.1.42",
|
|
Targets: []string{"a.example."},
|
|
TargetSyntaxValid: true,
|
|
ForwardAddresses: []ForwardAddress{{Type: "A", Address: "192.168.1.42", TTL: 300}},
|
|
ForwardMatch: true,
|
|
TargetResolves: true,
|
|
}},
|
|
}
|
|
raw, err := json.Marshal(in)
|
|
if err != nil {
|
|
t.Fatalf("marshal: %v", err)
|
|
}
|
|
var out ReverseZoneData
|
|
if err := json.Unmarshal(raw, &out); err != nil {
|
|
t.Fatalf("unmarshal: %v", err)
|
|
}
|
|
if len(out.Entries) != 1 || out.Entries[0].ReverseIP != "192.168.1.42" {
|
|
t.Errorf("round-trip lost data: %+v", out)
|
|
}
|
|
|
|
// Spot-check the JSON shape: snake_case field names that consumers rely on.
|
|
var raw2 map[string]any
|
|
if err := json.Unmarshal(raw, &raw2); err != nil {
|
|
t.Fatalf("unmarshal map: %v", err)
|
|
}
|
|
for _, key := range []string{"zone", "is_reverse_zone", "ptr_count", "entries"} {
|
|
if _, ok := raw2[key]; !ok {
|
|
t.Errorf("missing JSON key %q in %s", key, raw)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestStaticReportContext_Empty exercises the report renderer with no data:
|
|
// it should not crash and should produce some output.
|
|
func TestReport_EmptyData(t *testing.T) {
|
|
p := &reverseZoneProvider{}
|
|
html, err := p.GetHTMLReport(sdk.StaticReportContext(nil))
|
|
if err != nil {
|
|
t.Fatalf("GetHTMLReport: %v", err)
|
|
}
|
|
if html == "" {
|
|
t.Error("expected some HTML output even for empty data")
|
|
}
|
|
}
|
|
|
|
func TestReport_LoadError(t *testing.T) {
|
|
p := &reverseZoneProvider{}
|
|
raw, _ := json.Marshal(ReverseZoneData{LoadError: "no zone autofill"})
|
|
html, err := p.GetHTMLReport(sdk.StaticReportContext(raw))
|
|
if err != nil {
|
|
t.Fatalf("GetHTMLReport: %v", err)
|
|
}
|
|
if !contains([]string{html}, html) || !containsString(html, "no zone autofill") {
|
|
t.Errorf("expected LoadError message in output:\n%s", html)
|
|
}
|
|
if !containsString(html, "Could not load zone data") {
|
|
t.Errorf("expected load-error banner in output:\n%s", html)
|
|
}
|
|
}
|
|
|
|
func TestReport_InvalidJSON(t *testing.T) {
|
|
p := &reverseZoneProvider{}
|
|
_, err := p.GetHTMLReport(sdk.StaticReportContext([]byte("{not valid")))
|
|
if err == nil {
|
|
t.Error("expected error for invalid JSON")
|
|
}
|
|
}
|
|
|
|
func TestStatusToSeverity(t *testing.T) {
|
|
cases := []struct {
|
|
s sdk.Status
|
|
want string
|
|
}{
|
|
{sdk.StatusCrit, "crit"},
|
|
{sdk.StatusError, "crit"},
|
|
{sdk.StatusWarn, "warn"},
|
|
{sdk.StatusInfo, "info"},
|
|
{sdk.StatusOK, ""},
|
|
{sdk.StatusUnknown, ""},
|
|
}
|
|
for _, c := range cases {
|
|
if got := statusToSeverity(c.s); got != c.want {
|
|
t.Errorf("statusToSeverity(%v)=%q, want %q", c.s, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSeverityWeight(t *testing.T) {
|
|
if severityWeight("crit") <= severityWeight("warn") {
|
|
t.Error("crit should outweigh warn")
|
|
}
|
|
if severityWeight("warn") <= severityWeight("info") {
|
|
t.Error("warn should outweigh info")
|
|
}
|
|
if severityWeight("info") <= severityWeight("") {
|
|
t.Error("info should outweigh empty")
|
|
}
|
|
}
|
|
|
|
func TestHintFromMeta(t *testing.T) {
|
|
if hintFromMeta(nil) != "" {
|
|
t.Error("nil meta should yield empty hint")
|
|
}
|
|
if got := hintFromMeta(map[string]any{"hint": "do this"}); got != "do this" {
|
|
t.Errorf("hint key: %q", got)
|
|
}
|
|
if got := hintFromMeta(map[string]any{"hint": 42}); got != "" {
|
|
t.Errorf("non-string hint should be ignored, got %q", got)
|
|
}
|
|
if got := hintFromMeta(map[string]any{"unrelated": "x"}); got != "" {
|
|
t.Errorf("missing hint key should yield empty, got %q", got)
|
|
}
|
|
}
|
|
|
|
func containsString(haystack, needle string) bool {
|
|
for i := 0; i+len(needle) <= len(haystack); i++ {
|
|
if haystack[i:i+len(needle)] == needle {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|