~bigbes/lethe

ref: 8aeda698dc352ac3d1e93b667f56ad5dbd68d8b9 lethe/internal/platform/health/health_test.go -rw-r--r-- 2.7 KiB
8aeda698 — Eugene Blikh feat: add search UI layer — SearchTable, SearchFilters, SaveSearchForm, route, and styles 24 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package health

import (
	"context"
	"errors"
	"testing"
	"time"
)

// fakeChecker is a hand-built Checker for unit tests. Tests construct Set
// directly via &Set{Checks: []Checker{...}} — no steward involvement.
type fakeChecker struct {
	name  string
	err   error
	delay time.Duration
}

func (f *fakeChecker) Name() string { return f.name }

func (f *fakeChecker) Check(ctx context.Context) error {
	if f.delay > 0 {
		select {
		case <-time.After(f.delay):
		case <-ctx.Done():
			return ctx.Err()
		}
	}
	return f.err
}

func TestSetRunAllOK(t *testing.T) {
	s := &Set{Checks: []Checker{
		&fakeChecker{name: "a"},
		&fakeChecker{name: "b"},
	}}
	results, allOK := s.Run(context.Background())
	if !allOK {
		t.Fatalf("allOK = false; want true")
	}
	if len(results) != 2 {
		t.Fatalf("len(results) = %d; want 2", len(results))
	}
	for name, err := range results {
		if err != nil {
			t.Errorf("results[%q] = %v; want nil", name, err)
		}
	}
}

func TestSetRunAggregatesFailures(t *testing.T) {
	boom := errors.New("boom")
	s := &Set{Checks: []Checker{
		&fakeChecker{name: "a"},
		&fakeChecker{name: "b", err: boom},
		&fakeChecker{name: "c"},
	}}
	results, allOK := s.Run(context.Background())
	if allOK {
		t.Fatalf("allOK = true; want false (one check failed)")
	}
	if got := results["a"]; got != nil {
		t.Errorf("results[a] = %v; want nil", got)
	}
	if got := results["b"]; !errors.Is(got, boom) {
		t.Errorf("results[b] = %v; want errors.Is boom", got)
	}
	if got := results["c"]; got != nil {
		t.Errorf("results[c] = %v; want nil", got)
	}
}

func TestSetRunEmptyChecksReturnsAllOK(t *testing.T) {
	s := &Set{}
	results, allOK := s.Run(context.Background())
	if !allOK {
		t.Fatalf("allOK = false; want true (empty Checks is intentional)")
	}
	if len(results) != 0 {
		t.Fatalf("len(results) = %d; want 0", len(results))
	}
}

func TestSetRunEnforcesPerCheckTimeout(t *testing.T) {
	// fakeChecker with a delay much larger than the 2s per-check budget.
	// Use 10s delay; if the timeout works the test returns in ~2s.
	s := &Set{Checks: []Checker{
		&fakeChecker{name: "slow", delay: 10 * time.Second},
	}}
	start := time.Now()
	results, allOK := s.Run(context.Background())
	elapsed := time.Since(start)

	// The 2s per-check timeout must fire. Allow generous slack for CI but
	// firmly less than the 10s delay we set.
	if elapsed >= 5*time.Second {
		t.Fatalf("Run took %s; per-check timeout did not fire", elapsed)
	}
	if allOK {
		t.Fatalf("allOK = true; want false (timeout should surface as error)")
	}
	got := results["slow"]
	if got == nil {
		t.Fatalf("results[slow] = nil; want timeout error")
	}
	if !errors.Is(got, context.DeadlineExceeded) {
		t.Errorf("results[slow] = %v; want errors.Is(context.DeadlineExceeded)", got)
	}
}