159 lines
5 KiB
Go
159 lines
5 KiB
Go
package checker
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
|
)
|
|
|
|
type fakeObs struct {
|
|
data HappyDeliverData
|
|
err error
|
|
}
|
|
|
|
func (f fakeObs) Get(_ context.Context, _ sdk.ObservationKey, dest any) error {
|
|
if f.err != nil {
|
|
return f.err
|
|
}
|
|
raw, err := json.Marshal(f.data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return json.Unmarshal(raw, dest)
|
|
}
|
|
|
|
func (fakeObs) GetRelated(context.Context, sdk.ObservationKey) ([]sdk.RelatedObservation, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func okData() HappyDeliverData {
|
|
return HappyDeliverData{
|
|
Phase: "ok",
|
|
Scores: map[string]int{
|
|
SectionOverall: 90, SectionDNS: 80, SectionAuthentication: 60,
|
|
},
|
|
Grades: map[string]string{
|
|
SectionOverall: "A", SectionDNS: "B", SectionAuthentication: "D",
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestSectionRuleAboveThreshold(t *testing.T) {
|
|
r := NewSectionRule(SectionOverall, "Overall", 70)
|
|
states := r.Evaluate(context.Background(), fakeObs{data: okData()}, sdk.CheckerOptions{})
|
|
if len(states) != 1 || states[0].Status != sdk.StatusOK {
|
|
t.Fatalf("got %+v", states)
|
|
}
|
|
if !strings.Contains(states[0].Message, "score: 90") {
|
|
t.Errorf("message = %q", states[0].Message)
|
|
}
|
|
if states[0].Meta["score"] != 90 {
|
|
t.Errorf("meta score = %v", states[0].Meta["score"])
|
|
}
|
|
}
|
|
|
|
func TestSectionRuleBelowThresholdCRIT(t *testing.T) {
|
|
r := NewSectionRule(SectionAuthentication, "Authentication", 80)
|
|
states := r.Evaluate(context.Background(), fakeObs{data: okData()}, sdk.CheckerOptions{})
|
|
if states[0].Status != sdk.StatusCrit {
|
|
t.Errorf("status = %v, want CRIT", states[0].Status)
|
|
}
|
|
}
|
|
|
|
func TestSectionRuleOptionOverridesDefault(t *testing.T) {
|
|
r := NewSectionRule(SectionDNS, "DNS", 70)
|
|
// Score is 80, default threshold is 70 (OK), but we raise it to 95 -> CRIT.
|
|
states := r.Evaluate(context.Background(), fakeObs{data: okData()},
|
|
sdk.CheckerOptions{"min_score_dns": float64(95)})
|
|
if states[0].Status != sdk.StatusCrit {
|
|
t.Errorf("status = %v, want CRIT", states[0].Status)
|
|
}
|
|
}
|
|
|
|
func TestSectionRuleNoReportYet(t *testing.T) {
|
|
r := NewSectionRule(SectionOverall, "Overall", 70)
|
|
states := r.Evaluate(context.Background(), fakeObs{data: HappyDeliverData{Phase: "send"}}, sdk.CheckerOptions{})
|
|
if states[0].Status != sdk.StatusInfo || states[0].Code != "happydeliver.score.unavailable" {
|
|
t.Errorf("got %+v", states[0])
|
|
}
|
|
}
|
|
|
|
func TestSectionRuleScoreMissing(t *testing.T) {
|
|
r := NewSectionRule(SectionContent, "Content", 70)
|
|
states := r.Evaluate(context.Background(), fakeObs{data: okData()}, sdk.CheckerOptions{})
|
|
if states[0].Status != sdk.StatusInfo || states[0].Code != "happydeliver.score.missing" {
|
|
t.Errorf("got %+v", states[0])
|
|
}
|
|
}
|
|
|
|
func TestSectionRuleObservationError(t *testing.T) {
|
|
r := NewSectionRule(SectionOverall, "Overall", 70)
|
|
states := r.Evaluate(context.Background(), fakeObs{err: errors.New("boom")}, sdk.CheckerOptions{})
|
|
if states[0].Status != sdk.StatusError {
|
|
t.Errorf("status = %v", states[0].Status)
|
|
}
|
|
if !strings.Contains(states[0].Message, "boom") {
|
|
t.Errorf("message = %q", states[0].Message)
|
|
}
|
|
}
|
|
|
|
func TestSectionRuleNameAndOptionsDoc(t *testing.T) {
|
|
r := NewSectionRule(SectionDNS, "DNS", 70).(*sectionRule)
|
|
if r.Name() != "happydeliver.score.dns" {
|
|
t.Errorf("Name = %q", r.Name())
|
|
}
|
|
rwo, ok := any(r).(sdk.CheckRuleWithOptions)
|
|
if !ok {
|
|
t.Fatal("sectionRule should implement CheckRuleWithOptions")
|
|
}
|
|
doc := rwo.Options()
|
|
if len(doc.UserOpts) != 1 || doc.UserOpts[0].Id != "min_score_dns" {
|
|
t.Errorf("doc = %+v", doc)
|
|
}
|
|
if doc.UserOpts[0].Default != 70.0 {
|
|
t.Errorf("default = %v", doc.UserOpts[0].Default)
|
|
}
|
|
}
|
|
|
|
func TestLifecycleRulePhases(t *testing.T) {
|
|
cases := []struct {
|
|
phase string
|
|
errMsg string
|
|
latency float64
|
|
wantSt sdk.Status
|
|
wantCode string
|
|
}{
|
|
{"ok", "", 1.5, sdk.StatusOK, "happydeliver.lifecycle.ok"},
|
|
{"allocate", "down", 0, sdk.StatusCrit, "happydeliver.api.unavailable"},
|
|
{"send", "auth fail", 0, sdk.StatusCrit, "happydeliver.send.failed"},
|
|
{"timeout", "", 0, sdk.StatusWarn, "happydeliver.no_message_received"},
|
|
{"wait", "x", 0, sdk.StatusCrit, "happydeliver.wait.failed"},
|
|
{"fetch", "x", 0, sdk.StatusCrit, "happydeliver.fetch.failed"},
|
|
{"parse", "bad", 0, sdk.StatusCrit, "happydeliver.parse.failed"},
|
|
{"weird", "", 0, sdk.StatusInfo, "happydeliver.lifecycle.unknown"},
|
|
}
|
|
r := NewLifecycleRule()
|
|
for _, tc := range cases {
|
|
t.Run(tc.phase, func(t *testing.T) {
|
|
d := HappyDeliverData{Phase: tc.phase, Error: tc.errMsg, LatencySeconds: tc.latency}
|
|
states := r.Evaluate(context.Background(), fakeObs{data: d}, sdk.CheckerOptions{})
|
|
if states[0].Status != tc.wantSt {
|
|
t.Errorf("status = %v, want %v", states[0].Status, tc.wantSt)
|
|
}
|
|
if states[0].Code != tc.wantCode {
|
|
t.Errorf("code = %q, want %q", states[0].Code, tc.wantCode)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLifecycleRuleObservationError(t *testing.T) {
|
|
states := NewLifecycleRule().Evaluate(context.Background(), fakeObs{err: errors.New("io")}, sdk.CheckerOptions{})
|
|
if states[0].Status != sdk.StatusError {
|
|
t.Errorf("got %+v", states[0])
|
|
}
|
|
}
|