Co-authored-by: Dmitry Fedotov <dmitry@uint32.ru>
Co-committed-by: Dmitry Fedotov <dmitry@uint32.ru>
This commit is contained in:
2025-07-21 18:09:23 +03:00
committed by dmitry
parent 2f50eefbb2
commit 82a4641ab0
7 changed files with 338 additions and 914 deletions

183
watchdog_test.go Normal file
View File

@@ -0,0 +1,183 @@
package watchdog_test
import (
"context"
"reflect"
"testing"
"time"
"code.uint32.ru/tiny/watchdog"
)
type mockChecker struct {
name string
status watchdog.Status
err error
called bool
}
func (m *mockChecker) Func(_ context.Context) (watchdog.Status, error) {
m.called = true
return m.status, m.err
}
func (m *mockChecker) HasBeenCalled() bool {
return m.called
}
func (m *mockChecker) Check() watchdog.Check {
return watchdog.Check{
Name: m.name,
Interval: time.Minute,
Check: m.Func,
}
}
func newMockChecker(name string, s watchdog.Status, err error) *mockChecker {
return &mockChecker{name: name, status: s, err: err}
}
func TestCreateWith_new(t *testing.T) {
w := new(watchdog.Watchdog)
if len(w.ListChecks()) != 0 {
t.Errorf("expected len = 0")
}
}
func TestNew(t *testing.T) {
w := watchdog.New()
if len(w.ListChecks()) != 0 {
t.Errorf("expected len = 0")
}
m1 := newMockChecker("mock", watchdog.StatusOK, nil)
m2 := newMockChecker("mock2", watchdog.StatusOK, nil)
w = watchdog.New()
w.AddChecks(m1.Check(), m2.Check())
if len(w.ListChecks()) != 2 {
t.Errorf("expected len = 2")
}
w.RemoveChecks("mock")
if len(w.ListChecks()) != 1 {
t.Errorf("expected len = 1")
}
w.RemoveChecks("mock2")
if len(w.ListChecks()) != 0 {
t.Errorf("expected len = 0")
}
w = watchdog.New(m1.Check(), m2.Check())
if len(w.ListChecks()) != 2 {
t.Errorf("")
}
}
func TestRunImmediately(t *testing.T) {
w := new(watchdog.Watchdog)
out, err := w.RunImmediately(t.Context(), 1)
if err == nil {
t.Error("empty instance must return error on RunImmediately")
}
if len(out) != 0 {
t.Errorf("expected zero len slice for empty instance, got %d", len(out))
}
m1 := newMockChecker("mock", watchdog.StatusOK, nil)
m2 := newMockChecker("mock2", watchdog.StatusOK, nil)
w = watchdog.New(m1.Check(), m2.Check())
out, _ = w.RunImmediately(t.Context(), 0)
if len(out) != 2 {
t.Error("expected result of len 2")
}
for _, m := range []*mockChecker{m1, m2} {
if !m.HasBeenCalled() {
t.Errorf("mock %s has not been called", m.name)
}
}
}
func TestStart(t *testing.T) {
w := new(watchdog.Watchdog)
if _, err := w.Start(t.Context(), 0); err == nil {
t.Error("Start doen't error on empty checks slice")
}
m1 := newMockChecker("mock", watchdog.StatusOK, nil)
m2 := newMockChecker("mock2", watchdog.StatusOK, nil)
w.AddChecks(m1.Check(), m2.Check())
out, err := w.Start(t.Context(), 0)
if err != nil {
t.Error("Start returns error", err)
}
out2, err := w.Start(t.Context(), 0)
if err != nil {
t.Error("second call to Start returns error")
}
if !reflect.DeepEqual(out, out2) {
t.Error("returned channels are not equal")
}
count := 0
go func() {
time.Sleep(time.Second)
if err := w.Stop(); err != nil {
t.Error("Stop returned error", err)
}
}()
for res := range out {
if res.Status != watchdog.StatusOK || res.Error != nil {
t.Errorf("incorrect status for check %s", res.Name)
}
count++
}
if count != 2 {
t.Error("incorrect result count received from chan")
}
for _, m := range []*mockChecker{m1, m2} {
if !m.HasBeenCalled() {
t.Errorf("mock %s has not been called", m.name)
}
}
if err := w.Stop(); err == nil {
t.Error("call to stop on stopped instance does not return err")
}
}
func TestWatchdogObeysContext(t *testing.T) {
w := new(watchdog.Watchdog)
m1 := newMockChecker("mock", watchdog.StatusOK, nil)
m2 := newMockChecker("mock2", watchdog.StatusOK, nil)
w.AddChecks(m1.Check(), m2.Check())
ctx, stop := context.WithCancel(t.Context())
if _, err := w.Start(ctx, 2); err != nil {
t.Error(err)
}
stop()
// wait for goroutines to finish
time.Sleep(time.Second)
if err := w.Stop(); err == nil {
t.Error("no error calling Stop for stopped instance")
}
}