tests: add coverage for options, types, and HTTP server
- options_test.go: GetOption, GetFloatOption, GetIntOption, GetBoolOption with native types, JSON round-trips, missing keys, and wrong types - types_test.go: Status JSON marshal/unmarshal (strings, legacy ints, round-trip, unknown values), CheckTarget.Scope/String, BuildRulesInfo, empty-ID rejection - server_test.go: /health, /collect (success, error, bad body), /definition, /evaluate (all rules, disabled rule), /report (HTML, metrics, bad body), missing endpoints without CheckerDefinitionProvider
This commit is contained in:
parent
ec4efcf671
commit
ef7fffd4b7
4 changed files with 631 additions and 0 deletions
127
checker/options_test.go
Normal file
127
checker/options_test.go
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
// Copyright 2020-2026 The happyDomain Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package checker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetOption_DirectType(t *testing.T) {
|
||||
opts := CheckerOptions{"key": "hello"}
|
||||
got, ok := GetOption[string](opts, "key")
|
||||
if !ok || got != "hello" {
|
||||
t.Errorf("GetOption[string] = (%q, %v), want (\"hello\", true)", got, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOption_MissingKey(t *testing.T) {
|
||||
opts := CheckerOptions{}
|
||||
got, ok := GetOption[string](opts, "missing")
|
||||
if ok || got != "" {
|
||||
t.Errorf("GetOption[string] missing key = (%q, %v), want (\"\", false)", got, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOption_JSONRoundTrip(t *testing.T) {
|
||||
// Simulate what happens when options come from JSON: numbers become float64,
|
||||
// structs become map[string]any.
|
||||
type inner struct {
|
||||
X int `json:"x"`
|
||||
Y string `json:"y"`
|
||||
}
|
||||
opts := CheckerOptions{
|
||||
"obj": map[string]any{"x": float64(42), "y": "test"},
|
||||
}
|
||||
got, ok := GetOption[inner](opts, "obj")
|
||||
if !ok {
|
||||
t.Fatal("GetOption[inner] returned false")
|
||||
}
|
||||
if got.X != 42 || got.Y != "test" {
|
||||
t.Errorf("GetOption[inner] = %+v, want {X:42 Y:test}", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOption_WrongType(t *testing.T) {
|
||||
opts := CheckerOptions{"key": 123}
|
||||
got, ok := GetOption[string](opts, "key")
|
||||
// int 123 cannot be unmarshaled into string via JSON
|
||||
if ok {
|
||||
t.Errorf("GetOption[string] with int value = (%q, true), want (\"\", false)", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFloatOption_NativeFloat(t *testing.T) {
|
||||
opts := CheckerOptions{"f": 3.14}
|
||||
if got := GetFloatOption(opts, "f", 0); got != 3.14 {
|
||||
t.Errorf("GetFloatOption = %v, want 3.14", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFloatOption_JSONNumber(t *testing.T) {
|
||||
opts := CheckerOptions{"f": json.Number("2.718")}
|
||||
if got := GetFloatOption(opts, "f", 0); got != 2.718 {
|
||||
t.Errorf("GetFloatOption(json.Number) = %v, want 2.718", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFloatOption_Missing(t *testing.T) {
|
||||
opts := CheckerOptions{}
|
||||
if got := GetFloatOption(opts, "f", 99.9); got != 99.9 {
|
||||
t.Errorf("GetFloatOption missing = %v, want 99.9", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFloatOption_WrongType(t *testing.T) {
|
||||
opts := CheckerOptions{"f": "not a number"}
|
||||
if got := GetFloatOption(opts, "f", 1.0); got != 1.0 {
|
||||
t.Errorf("GetFloatOption wrong type = %v, want 1.0", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIntOption(t *testing.T) {
|
||||
opts := CheckerOptions{"i": float64(42)}
|
||||
if got := GetIntOption(opts, "i", 0); got != 42 {
|
||||
t.Errorf("GetIntOption = %v, want 42", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIntOption_Missing(t *testing.T) {
|
||||
opts := CheckerOptions{}
|
||||
if got := GetIntOption(opts, "i", 10); got != 10 {
|
||||
t.Errorf("GetIntOption missing = %v, want 10", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBoolOption(t *testing.T) {
|
||||
opts := CheckerOptions{"b": true}
|
||||
if got := GetBoolOption(opts, "b", false); got != true {
|
||||
t.Errorf("GetBoolOption = %v, want true", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBoolOption_Missing(t *testing.T) {
|
||||
opts := CheckerOptions{}
|
||||
if got := GetBoolOption(opts, "b", true); got != true {
|
||||
t.Errorf("GetBoolOption missing = %v, want true", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBoolOption_WrongType(t *testing.T) {
|
||||
opts := CheckerOptions{"b": "yes"}
|
||||
if got := GetBoolOption(opts, "b", false); got != false {
|
||||
t.Errorf("GetBoolOption wrong type = %v, want false", got)
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,9 @@ import (
|
|||
// resetRegistries clears the global registries between tests so that one
|
||||
// test's registration cannot leak into the next. The package-level maps are
|
||||
// the only shared state.
|
||||
//
|
||||
// Tests in this package MUST NOT use t.Parallel() because they mutate
|
||||
// these shared maps without synchronization.
|
||||
func resetRegistries() {
|
||||
checkerRegistry = map[string]*CheckerDefinition{}
|
||||
observationProviderRegistry = map[ObservationKey]ObservationProvider{}
|
||||
|
|
|
|||
319
checker/server_test.go
Normal file
319
checker/server_test.go
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
// Copyright 2020-2026 The happyDomain Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package checker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// --- test doubles ---
|
||||
|
||||
type testProvider struct {
|
||||
key ObservationKey
|
||||
collectFn func(ctx context.Context, opts CheckerOptions) (any, error)
|
||||
definition *CheckerDefinition
|
||||
htmlFn func(raw json.RawMessage) (string, error)
|
||||
metricsFn func(raw json.RawMessage, t time.Time) ([]CheckMetric, error)
|
||||
}
|
||||
|
||||
func (p *testProvider) Key() ObservationKey { return p.key }
|
||||
func (p *testProvider) Collect(ctx context.Context, opts CheckerOptions) (any, error) {
|
||||
if p.collectFn != nil {
|
||||
return p.collectFn(ctx, opts)
|
||||
}
|
||||
return map[string]string{"result": "ok"}, nil
|
||||
}
|
||||
func (p *testProvider) Definition() *CheckerDefinition { return p.definition }
|
||||
func (p *testProvider) GetHTMLReport(raw json.RawMessage) (string, error) {
|
||||
if p.htmlFn != nil {
|
||||
return p.htmlFn(raw)
|
||||
}
|
||||
return "<h1>report</h1>", nil
|
||||
}
|
||||
func (p *testProvider) ExtractMetrics(raw json.RawMessage, t time.Time) ([]CheckMetric, error) {
|
||||
if p.metricsFn != nil {
|
||||
return p.metricsFn(raw, t)
|
||||
}
|
||||
return []CheckMetric{{Name: "m1", Value: 1.0, Timestamp: t}}, nil
|
||||
}
|
||||
|
||||
// dummyRule is a minimal CheckRule for testing evaluate.
|
||||
type dummyRule struct {
|
||||
name string
|
||||
desc string
|
||||
}
|
||||
|
||||
func (r *dummyRule) Name() string { return r.name }
|
||||
func (r *dummyRule) Description() string { return r.desc }
|
||||
func (r *dummyRule) Evaluate(ctx context.Context, obs ObservationGetter, opts CheckerOptions) CheckState {
|
||||
return CheckState{Status: StatusOK, Message: r.name + " passed"}
|
||||
}
|
||||
|
||||
// --- helpers ---
|
||||
|
||||
func newTestServer(p *testProvider) *Server {
|
||||
return NewServer(p)
|
||||
}
|
||||
|
||||
func doRequest(handler http.Handler, method, path string, body any, headers map[string]string) *httptest.ResponseRecorder {
|
||||
var buf bytes.Buffer
|
||||
if body != nil {
|
||||
json.NewEncoder(&buf).Encode(body)
|
||||
}
|
||||
req := httptest.NewRequest(method, path, &buf)
|
||||
if body != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
for k, v := range headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
rec := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rec, req)
|
||||
return rec
|
||||
}
|
||||
|
||||
// --- tests ---
|
||||
|
||||
func TestServer_Health(t *testing.T) {
|
||||
p := &testProvider{key: "test", definition: &CheckerDefinition{ID: "test", Rules: []CheckRule{}}}
|
||||
srv := newTestServer(p)
|
||||
rec := doRequest(srv.Handler(), "GET", "/health", nil, nil)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("GET /health = %d, want %d", rec.Code, http.StatusOK)
|
||||
}
|
||||
var resp map[string]string
|
||||
json.NewDecoder(rec.Body).Decode(&resp)
|
||||
if resp["status"] != "ok" {
|
||||
t.Errorf("GET /health status = %q, want \"ok\"", resp["status"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_Collect_Success(t *testing.T) {
|
||||
p := &testProvider{
|
||||
key: "test",
|
||||
definition: &CheckerDefinition{ID: "test", Rules: []CheckRule{}},
|
||||
collectFn: func(ctx context.Context, opts CheckerOptions) (any, error) {
|
||||
return map[string]int{"count": 42}, nil
|
||||
},
|
||||
}
|
||||
srv := newTestServer(p)
|
||||
rec := doRequest(srv.Handler(), "POST", "/collect", ExternalCollectRequest{
|
||||
Key: "test",
|
||||
Options: CheckerOptions{"a": "b"},
|
||||
}, nil)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("POST /collect = %d, want %d", rec.Code, http.StatusOK)
|
||||
}
|
||||
var resp ExternalCollectResponse
|
||||
json.NewDecoder(rec.Body).Decode(&resp)
|
||||
if resp.Error != "" {
|
||||
t.Errorf("POST /collect error = %q, want empty", resp.Error)
|
||||
}
|
||||
if resp.Data == nil {
|
||||
t.Fatal("POST /collect data is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_Collect_ProviderError(t *testing.T) {
|
||||
p := &testProvider{
|
||||
key: "test",
|
||||
definition: &CheckerDefinition{ID: "test", Rules: []CheckRule{}},
|
||||
collectFn: func(ctx context.Context, opts CheckerOptions) (any, error) {
|
||||
return nil, errors.New("provider failed")
|
||||
},
|
||||
}
|
||||
srv := newTestServer(p)
|
||||
rec := doRequest(srv.Handler(), "POST", "/collect", ExternalCollectRequest{Key: "test"}, nil)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("POST /collect = %d, want %d", rec.Code, http.StatusOK)
|
||||
}
|
||||
var resp ExternalCollectResponse
|
||||
json.NewDecoder(rec.Body).Decode(&resp)
|
||||
if resp.Error == "" {
|
||||
t.Error("expected error in response, got empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_Collect_BadBody(t *testing.T) {
|
||||
p := &testProvider{key: "test", definition: &CheckerDefinition{ID: "test", Rules: []CheckRule{}}}
|
||||
srv := newTestServer(p)
|
||||
req := httptest.NewRequest("POST", "/collect", bytes.NewBufferString("{invalid"))
|
||||
rec := httptest.NewRecorder()
|
||||
srv.Handler().ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusBadRequest {
|
||||
t.Errorf("POST /collect bad body = %d, want %d", rec.Code, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_Definition(t *testing.T) {
|
||||
def := &CheckerDefinition{
|
||||
ID: "test-checker",
|
||||
Name: "Test Checker",
|
||||
Rules: []CheckRule{
|
||||
&dummyRule{name: "rule1", desc: "first rule"},
|
||||
},
|
||||
}
|
||||
p := &testProvider{key: "test", definition: def}
|
||||
srv := newTestServer(p)
|
||||
rec := doRequest(srv.Handler(), "GET", "/definition", nil, nil)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("GET /definition = %d, want %d", rec.Code, http.StatusOK)
|
||||
}
|
||||
var got CheckerDefinition
|
||||
json.NewDecoder(rec.Body).Decode(&got)
|
||||
if got.ID != "test-checker" {
|
||||
t.Errorf("definition ID = %q, want \"test-checker\"", got.ID)
|
||||
}
|
||||
if len(got.RulesInfo) != 1 {
|
||||
t.Errorf("definition rules = %d, want 1", len(got.RulesInfo))
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_Evaluate(t *testing.T) {
|
||||
def := &CheckerDefinition{
|
||||
ID: "test-checker",
|
||||
Name: "Test Checker",
|
||||
Rules: []CheckRule{
|
||||
&dummyRule{name: "rule1", desc: "first rule"},
|
||||
&dummyRule{name: "rule2", desc: "second rule"},
|
||||
},
|
||||
}
|
||||
p := &testProvider{key: "test", definition: def}
|
||||
srv := newTestServer(p)
|
||||
|
||||
rec := doRequest(srv.Handler(), "POST", "/evaluate", ExternalEvaluateRequest{
|
||||
Observations: map[ObservationKey]json.RawMessage{
|
||||
"test": json.RawMessage(`{"count":42}`),
|
||||
},
|
||||
Options: CheckerOptions{},
|
||||
}, nil)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("POST /evaluate = %d, want %d", rec.Code, http.StatusOK)
|
||||
}
|
||||
var resp ExternalEvaluateResponse
|
||||
json.NewDecoder(rec.Body).Decode(&resp)
|
||||
if len(resp.States) != 2 {
|
||||
t.Fatalf("evaluate states = %d, want 2", len(resp.States))
|
||||
}
|
||||
if resp.States[0].Code != "rule1" {
|
||||
t.Errorf("evaluate state[0].Code = %q, want \"rule1\"", resp.States[0].Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_Evaluate_DisabledRule(t *testing.T) {
|
||||
def := &CheckerDefinition{
|
||||
ID: "test-checker",
|
||||
Rules: []CheckRule{
|
||||
&dummyRule{name: "rule1", desc: "first"},
|
||||
&dummyRule{name: "rule2", desc: "second"},
|
||||
},
|
||||
}
|
||||
p := &testProvider{key: "test", definition: def}
|
||||
srv := newTestServer(p)
|
||||
|
||||
rec := doRequest(srv.Handler(), "POST", "/evaluate", ExternalEvaluateRequest{
|
||||
Observations: map[ObservationKey]json.RawMessage{
|
||||
"test": json.RawMessage(`{}`),
|
||||
},
|
||||
EnabledRules: map[string]bool{"rule1": false},
|
||||
}, nil)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("POST /evaluate = %d, want %d", rec.Code, http.StatusOK)
|
||||
}
|
||||
var resp ExternalEvaluateResponse
|
||||
json.NewDecoder(rec.Body).Decode(&resp)
|
||||
if len(resp.States) != 1 {
|
||||
t.Fatalf("evaluate with disabled rule: states = %d, want 1", len(resp.States))
|
||||
}
|
||||
if resp.States[0].Code != "rule2" {
|
||||
t.Errorf("remaining state code = %q, want \"rule2\"", resp.States[0].Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_Report_HTML(t *testing.T) {
|
||||
p := &testProvider{
|
||||
key: "test",
|
||||
definition: &CheckerDefinition{ID: "test-checker", Rules: []CheckRule{}},
|
||||
htmlFn: func(raw json.RawMessage) (string, error) {
|
||||
return "<p>hello</p>", nil
|
||||
},
|
||||
}
|
||||
srv := newTestServer(p)
|
||||
rec := doRequest(srv.Handler(), "POST", "/report", ExternalReportRequest{
|
||||
Key: "test",
|
||||
Data: json.RawMessage(`{}`),
|
||||
}, map[string]string{"Accept": "text/html"})
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("POST /report html = %d, want %d", rec.Code, http.StatusOK)
|
||||
}
|
||||
if ct := rec.Header().Get("Content-Type"); ct != "text/html; charset=utf-8" {
|
||||
t.Errorf("Content-Type = %q, want text/html", ct)
|
||||
}
|
||||
if body := rec.Body.String(); body != "<p>hello</p>" {
|
||||
t.Errorf("body = %q, want \"<p>hello</p>\"", body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_Report_Metrics(t *testing.T) {
|
||||
p := &testProvider{
|
||||
key: "test",
|
||||
definition: &CheckerDefinition{ID: "test-checker", Rules: []CheckRule{}},
|
||||
}
|
||||
srv := newTestServer(p)
|
||||
rec := doRequest(srv.Handler(), "POST", "/report", ExternalReportRequest{
|
||||
Key: "test",
|
||||
Data: json.RawMessage(`{}`),
|
||||
}, map[string]string{"Accept": "application/json"})
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("POST /report metrics = %d, want %d", rec.Code, http.StatusOK)
|
||||
}
|
||||
var metrics []CheckMetric
|
||||
json.NewDecoder(rec.Body).Decode(&metrics)
|
||||
if len(metrics) != 1 {
|
||||
t.Errorf("metrics count = %d, want 1", len(metrics))
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_Report_BadBody(t *testing.T) {
|
||||
p := &testProvider{
|
||||
key: "test",
|
||||
definition: &CheckerDefinition{ID: "test-checker", Rules: []CheckRule{}},
|
||||
}
|
||||
srv := newTestServer(p)
|
||||
req := httptest.NewRequest("POST", "/report", bytes.NewBufferString("{bad"))
|
||||
rec := httptest.NewRecorder()
|
||||
srv.Handler().ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusBadRequest {
|
||||
t.Errorf("POST /report bad body = %d, want %d", rec.Code, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_NoDefinition_NoEvaluateEndpoint(t *testing.T) {
|
||||
// A provider that does NOT implement CheckerDefinitionProvider
|
||||
p := &stubProvider{key: "basic"}
|
||||
srv := NewServer(p)
|
||||
rec := doRequest(srv.Handler(), "POST", "/evaluate", nil, nil)
|
||||
// Should 404 or 405 since /evaluate is not registered
|
||||
if rec.Code == http.StatusOK {
|
||||
t.Error("POST /evaluate should not be available without CheckerDefinitionProvider")
|
||||
}
|
||||
}
|
||||
182
checker/types_test.go
Normal file
182
checker/types_test.go
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
// Copyright 2020-2026 The happyDomain Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package checker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStatus_MarshalJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
status Status
|
||||
want string
|
||||
}{
|
||||
{StatusOK, `"OK"`},
|
||||
{StatusInfo, `"INFO"`},
|
||||
{StatusUnknown, `"UNKNOWN"`},
|
||||
{StatusWarn, `"WARN"`},
|
||||
{StatusCrit, `"CRIT"`},
|
||||
{StatusError, `"ERROR"`},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got, err := json.Marshal(tt.status)
|
||||
if err != nil {
|
||||
t.Errorf("Marshal(%v) error: %v", tt.status, err)
|
||||
continue
|
||||
}
|
||||
if string(got) != tt.want {
|
||||
t.Errorf("Marshal(%v) = %s, want %s", tt.status, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatus_UnmarshalJSON_String(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want Status
|
||||
}{
|
||||
{`"OK"`, StatusOK},
|
||||
{`"INFO"`, StatusInfo},
|
||||
{`"UNKNOWN"`, StatusUnknown},
|
||||
{`""`, StatusUnknown},
|
||||
{`"WARN"`, StatusWarn},
|
||||
{`"CRIT"`, StatusCrit},
|
||||
{`"ERROR"`, StatusError},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
var got Status
|
||||
if err := json.Unmarshal([]byte(tt.input), &got); err != nil {
|
||||
t.Errorf("Unmarshal(%s) error: %v", tt.input, err)
|
||||
continue
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("Unmarshal(%s) = %v, want %v", tt.input, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatus_UnmarshalJSON_LegacyInt(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want Status
|
||||
}{
|
||||
{`-2`, StatusOK},
|
||||
{`-1`, StatusInfo},
|
||||
{`0`, StatusUnknown},
|
||||
{`1`, StatusWarn},
|
||||
{`2`, StatusCrit},
|
||||
{`3`, StatusError},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
var got Status
|
||||
if err := json.Unmarshal([]byte(tt.input), &got); err != nil {
|
||||
t.Errorf("Unmarshal(%s) error: %v", tt.input, err)
|
||||
continue
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("Unmarshal(%s) = %v, want %v", tt.input, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatus_UnmarshalJSON_UnknownString(t *testing.T) {
|
||||
var s Status
|
||||
err := json.Unmarshal([]byte(`"BOGUS"`), &s)
|
||||
if err == nil {
|
||||
t.Error("Unmarshal(\"BOGUS\") should return error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatus_RoundTrip(t *testing.T) {
|
||||
for _, s := range []Status{StatusOK, StatusInfo, StatusUnknown, StatusWarn, StatusCrit, StatusError} {
|
||||
data, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal(%v) error: %v", s, err)
|
||||
}
|
||||
var got Status
|
||||
if err := json.Unmarshal(data, &got); err != nil {
|
||||
t.Fatalf("Unmarshal(%s) error: %v", data, err)
|
||||
}
|
||||
if got != s {
|
||||
t.Errorf("round-trip %v: got %v", s, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatus_String(t *testing.T) {
|
||||
if got := StatusOK.String(); got != "OK" {
|
||||
t.Errorf("StatusOK.String() = %q, want \"OK\"", got)
|
||||
}
|
||||
if got := Status(99).String(); got != "Status(99)" {
|
||||
t.Errorf("Status(99).String() = %q, want \"Status(99)\"", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckTarget_Scope(t *testing.T) {
|
||||
tests := []struct {
|
||||
target CheckTarget
|
||||
want CheckScopeType
|
||||
}{
|
||||
{CheckTarget{}, CheckScopeUser},
|
||||
{CheckTarget{UserId: "u1"}, CheckScopeUser},
|
||||
{CheckTarget{DomainId: "d1"}, CheckScopeDomain},
|
||||
{CheckTarget{DomainId: "d1", ServiceId: "s1"}, CheckScopeService},
|
||||
{CheckTarget{ServiceId: "s1"}, CheckScopeService},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
if got := tt.target.Scope(); got != tt.want {
|
||||
t.Errorf("%+v.Scope() = %v, want %v", tt.target, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckTarget_String(t *testing.T) {
|
||||
tests := []struct {
|
||||
target CheckTarget
|
||||
want string
|
||||
}{
|
||||
{CheckTarget{}, ""},
|
||||
{CheckTarget{UserId: "u1"}, "u1"},
|
||||
{CheckTarget{UserId: "u1", DomainId: "d1"}, "u1/d1"},
|
||||
{CheckTarget{UserId: "u1", DomainId: "d1", ServiceId: "s1"}, "u1/d1/s1"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
if got := tt.target.String(); got != tt.want {
|
||||
t.Errorf("%+v.String() = %q, want %q", tt.target, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckerDefinition_BuildRulesInfo(t *testing.T) {
|
||||
d := &CheckerDefinition{
|
||||
Rules: []CheckRule{&dummyRule{name: "r1", desc: "desc1"}},
|
||||
}
|
||||
d.BuildRulesInfo()
|
||||
if len(d.RulesInfo) != 1 {
|
||||
t.Fatalf("BuildRulesInfo: got %d rules, want 1", len(d.RulesInfo))
|
||||
}
|
||||
if d.RulesInfo[0].Name != "r1" || d.RulesInfo[0].Description != "desc1" {
|
||||
t.Errorf("BuildRulesInfo: got %+v, want {Name:r1, Description:desc1}", d.RulesInfo[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterChecker_EmptyIDRejected(t *testing.T) {
|
||||
resetRegistries()
|
||||
RegisterChecker(&CheckerDefinition{ID: "", Name: "bad"})
|
||||
if len(GetCheckers()) != 0 {
|
||||
t.Error("checker with empty ID should not be registered")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue