Skip to content

Commit

Permalink
test(health): status tests
Browse files Browse the repository at this point in the history
  • Loading branch information
vknabel committed Jul 30, 2024
1 parent a6f5b99 commit 9c4dedb
Show file tree
Hide file tree
Showing 3 changed files with 447 additions and 0 deletions.
141 changes: 141 additions & 0 deletions pkg/healthstatus/async-check_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package healthstatus

import (
"context"
"errors"
"log/slog"
"testing"
"time"

"github.com/google/go-cmp/cmp"
)

type countedCheck struct {
name string
state currentState
checks int
}

func (c *countedCheck) ServiceName() string {
return c.name
}
func (c *countedCheck) Check(context.Context) (HealthResult, error) {
c.checks++
return c.state.status, c.state.err
}

func TestAsync(t *testing.T) {
slog.SetLogLoggerLevel(slog.LevelDebug)
log := slog.Default()

tests := []struct {
name string
interval time.Duration
callIntervals []time.Duration
wantChecks int
hc *countedCheck
want currentState
}{
{
name: "succeeding call",
interval: 2 * time.Second,
callIntervals: []time.Duration{
500 * time.Millisecond,
100 * time.Millisecond,
},
wantChecks: 1,
hc: &countedCheck{
state: currentState{
status: HealthResult{
Status: HealthStatusHealthy,
},
},
},
want: currentState{
status: HealthResult{
Status: HealthStatusHealthy,
},
},
},
{
name: "multiple calls",
interval: 2 * time.Second,
callIntervals: []time.Duration{
500 * time.Millisecond,
600 * time.Millisecond,
},
wantChecks: 1,
hc: &countedCheck{
state: currentState{
status: HealthResult{
Status: HealthStatusHealthy,
},
},
},
want: currentState{
status: HealthResult{
Status: HealthStatusHealthy,
},
},
},
{
name: "error propagated",
interval: 2 * time.Second,
callIntervals: []time.Duration{
100 * time.Millisecond,
},
wantChecks: 1,
hc: &countedCheck{
state: currentState{
status: HealthResult{
Status: HealthStatusUnhealthy,
},
err: errors.New("intentional"),
},
},
want: currentState{
status: HealthResult{
Status: HealthStatusUnhealthy,
},
err: errors.New("intentional"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

hc := Async(log, tt.interval, tt.hc)
hc.Start(ctx)

for _, timeout := range tt.callIntervals {
time.Sleep(timeout)

got, gotErr := hc.Check(ctx)

var (
wantErrStr = "<nil>"
gotErrStr = "<nil>"
)
if tt.want.err != nil {
wantErrStr = tt.want.err.Error()
}
if gotErr != nil {
gotErrStr = gotErr.Error()
}
if diff := cmp.Diff(wantErrStr, gotErrStr); diff != "" {
t.Errorf("mismatch error (-want +got):\n%s", diff)
}
if diff := cmp.Diff(tt.want.status, got); diff != "" {
t.Errorf("mismatch result (-want +got):\n%s", diff)
}

}

if tt.hc.checks != tt.wantChecks {
t.Errorf("mismatched calls (want %d, got %d)", tt.wantChecks, tt.hc.checks)
}
})
}
}
158 changes: 158 additions & 0 deletions pkg/healthstatus/delayed-error-check_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package healthstatus

import (
"context"
"errors"
"testing"

"github.com/google/go-cmp/cmp"
)

type recordedCheck struct {
name string
states []currentState
}

func (c *recordedCheck) ServiceName() string {
return c.name
}
func (c *recordedCheck) Check(context.Context) (HealthResult, error) {
cur := c.states[0]
if len(c.states) > 1 {
c.states = c.states[1:]
}
return cur.status, cur.err
}

func TestDelayErrors(t *testing.T) {
tests := []struct {
name string
hc *DelayedErrorHealthCheck
want []currentState
}{
{
name: "check always returns first result even on error",
hc: DelayErrors(1, &recordedCheck{
name: "record",
states: []currentState{
{
status: HealthResult{
Status: HealthStatusUnhealthy,
Message: "intentional",
Services: map[string]HealthResult{},
},
err: errors.New("initial error"),
},
{
status: HealthResult{
Status: HealthStatusUnhealthy,
Message: "intentional",
Services: map[string]HealthResult{},
},
err: errors.New("secondary error"),
},
},
}),
want: []currentState{
{
status: HealthResult{
Status: HealthStatusUnhealthy,
Message: "intentional",
Services: map[string]HealthResult{},
},
err: errors.New("initial error"),
},
{
status: HealthResult{
Status: HealthStatusUnhealthy,
Message: "intentional",
Services: map[string]HealthResult{},
},
err: errors.New("secondary error"),
},
},
},
{
name: "ignores first error after inital success",
hc: DelayErrors(1, &recordedCheck{
name: "record",
states: []currentState{
{
status: HealthResult{
Status: HealthStatusHealthy,
Message: "",
Services: map[string]HealthResult{},
},
},
{
status: HealthResult{
Status: HealthStatusUnhealthy,
Message: "intentional",
Services: map[string]HealthResult{},
},
err: errors.New("hidden error"),
},
{
status: HealthResult{
Status: HealthStatusUnhealthy,
Message: "intentional",
Services: map[string]HealthResult{},
},
err: errors.New("presented error"),
},
},
}),
want: []currentState{
{
status: HealthResult{
Status: HealthStatusHealthy,
Message: "",
Services: map[string]HealthResult{},
},
},
{
status: HealthResult{
Status: HealthStatusHealthy,
Message: "",
Services: map[string]HealthResult{},
},
},
{
status: HealthResult{
Status: HealthStatusUnhealthy,
Message: "intentional",
Services: map[string]HealthResult{},
},
err: errors.New("presented error"),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

for i, w := range tt.want {
got, gotErr := tt.hc.Check(ctx)

var (
wantErrStr = "<nil>"
gotErrStr = "<nil>"
)
if w.err != nil {
wantErrStr = w.err.Error()
}
if gotErr != nil {
gotErrStr = gotErr.Error()
}
if diff := cmp.Diff(wantErrStr, gotErrStr); diff != "" {
t.Errorf("mismatch error on call %d (-want +got):\n%s", i, diff)
}
if diff := cmp.Diff(w.status, got); diff != "" {
t.Errorf("mismatch result on call %d (-want +got):\n%s", i, diff)
}
}
})
}
}
Loading

0 comments on commit 9c4dedb

Please sign in to comment.