Initial commit
This commit is contained in:
commit
1d93a25983
23 changed files with 2654 additions and 0 deletions
206
checker/provider_test.go
Normal file
206
checker/provider_test.go
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue