@checkstack/healthcheck-backend 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,229 @@
1
+ import { describe, it, expect, beforeEach } from "bun:test";
2
+ import {
3
+ setupHealthCheckWorker,
4
+ scheduleHealthCheck,
5
+ bootstrapHealthChecks,
6
+ type HealthCheckJobPayload,
7
+ } from "./queue-executor";
8
+ import {
9
+ createMockLogger,
10
+ createMockQueueManager,
11
+ createMockDb,
12
+ createMockSignalService,
13
+ } from "@checkstack/test-utils-backend";
14
+ import {
15
+ type HealthCheckRegistry,
16
+ Versioned,
17
+ z,
18
+ } from "@checkstack/backend-api";
19
+ import { mock } from "bun:test";
20
+
21
+ // Helper to create mock health check registry
22
+ const createMockRegistry = (): HealthCheckRegistry => ({
23
+ getStrategy: mock((id: string) => ({
24
+ id,
25
+ displayName: "Mock Strategy",
26
+ description: "Mock",
27
+ config: new Versioned({
28
+ version: 1,
29
+ schema: z.object({}),
30
+ }),
31
+ aggregatedResult: new Versioned({
32
+ version: 1,
33
+ schema: z.object({}),
34
+ }),
35
+ execute: mock(async () => ({
36
+ status: "healthy" as const,
37
+ message: "Mock check passed",
38
+ timestamp: new Date().toISOString(),
39
+ })),
40
+ aggregateResult: mock(() => ({})),
41
+ })),
42
+ register: mock(() => {}),
43
+ getStrategies: mock(() => []),
44
+ });
45
+
46
+ // Helper to create mock catalog client for notification delegation
47
+ const createMockCatalogClient = () => ({
48
+ notifySystemSubscribers: mock(async () => ({ notifiedCount: 0 })),
49
+ // Other methods not used in queue-executor
50
+ getEntities: mock(async () => ({ systems: [], groups: [] })),
51
+ getSystems: mock(async () => []),
52
+ getGroups: mock(async () => []),
53
+ createSystem: mock(async () => ({})),
54
+ updateSystem: mock(async () => ({})),
55
+ deleteSystem: mock(async () => ({ success: true })),
56
+ createGroup: mock(async () => ({})),
57
+ updateGroup: mock(async () => ({})),
58
+ deleteGroup: mock(async () => ({ success: true })),
59
+ addSystemToGroup: mock(async () => ({ success: true })),
60
+ removeSystemFromGroup: mock(async () => ({ success: true })),
61
+ getViews: mock(async () => []),
62
+ createView: mock(async () => ({})),
63
+ });
64
+
65
+ describe("Queue-Based Health Check Executor", () => {
66
+ describe("scheduleHealthCheck", () => {
67
+ it("should enqueue a health check with delay and deterministic jobId", async () => {
68
+ const mockQueueManager = createMockQueueManager();
69
+ const mockLogger = createMockLogger();
70
+
71
+ const payload: HealthCheckJobPayload = {
72
+ configId: "config-1",
73
+ systemId: "system-1",
74
+ };
75
+
76
+ await scheduleHealthCheck({
77
+ queueManager: mockQueueManager,
78
+ payload,
79
+ intervalSeconds: 60,
80
+ logger: mockLogger,
81
+ });
82
+
83
+ // Verify queue was created with correct name
84
+ const queue = mockQueueManager.getQueue("health-checks");
85
+ expect(queue).toBeDefined();
86
+ });
87
+
88
+ it("should use deterministic job IDs", async () => {
89
+ const mockQueueManager = createMockQueueManager();
90
+ const mockLogger = createMockLogger();
91
+
92
+ const payload: HealthCheckJobPayload = {
93
+ configId: "config-1",
94
+ systemId: "system-1",
95
+ };
96
+
97
+ const result = await scheduleHealthCheck({
98
+ queueManager: mockQueueManager,
99
+ payload,
100
+ intervalSeconds: 60,
101
+ logger: mockLogger,
102
+ });
103
+
104
+ // The result should be a job ID
105
+ expect(result).toBeDefined();
106
+ expect(typeof result).toBe("string");
107
+ });
108
+ });
109
+
110
+ describe("setupHealthCheckWorker", () => {
111
+ it("should subscribe to the health-checks queue in work-queue mode", async () => {
112
+ const mockDb = createMockDb();
113
+ const mockRegistry = createMockRegistry();
114
+ const mockLogger = createMockLogger();
115
+ const mockQueueManager = createMockQueueManager();
116
+ const mockCatalogClient = createMockCatalogClient();
117
+
118
+ await setupHealthCheckWorker({
119
+ db: mockDb as unknown as Parameters<
120
+ typeof setupHealthCheckWorker
121
+ >[0]["db"],
122
+ registry: mockRegistry,
123
+ logger: mockLogger,
124
+ queueManager: mockQueueManager,
125
+ signalService: createMockSignalService(),
126
+ catalogClient: mockCatalogClient as unknown as Parameters<
127
+ typeof setupHealthCheckWorker
128
+ >[0]["catalogClient"],
129
+ getEmitHook: () => undefined,
130
+ });
131
+
132
+ expect(mockLogger.debug).toHaveBeenCalledWith(
133
+ expect.stringContaining("Health Check Worker subscribed")
134
+ );
135
+ });
136
+ });
137
+
138
+ describe("bootstrapHealthChecks", () => {
139
+ beforeEach(() => {
140
+ // Reset all mocks between tests
141
+ });
142
+
143
+ it("should enqueue all enabled health checks", async () => {
144
+ const mockQueueManager = createMockQueueManager();
145
+ const mockLogger = createMockLogger();
146
+ const mockDb = createMockDb();
147
+
148
+ // Configure the mock database to return some enabled checks
149
+ const mockData = [
150
+ {
151
+ systemId: "system-1",
152
+ configId: "config-1",
153
+ interval: 30,
154
+ lastRun: null,
155
+ },
156
+ {
157
+ systemId: "system-2",
158
+ configId: "config-2",
159
+ interval: 60,
160
+ lastRun: null,
161
+ },
162
+ ];
163
+
164
+ // Override select to return a chain that handles subquery with groupBy
165
+ // First call: for enabledChecks query (innerJoin().where)
166
+ // Second call: for latestRuns query (groupBy)
167
+ let selectCallCount = 0;
168
+ (mockDb.select as any) = mock(() => {
169
+ selectCallCount++;
170
+ if (selectCallCount === 1) {
171
+ // enabledChecks query
172
+ return {
173
+ from: mock(() => ({
174
+ innerJoin: mock(() => ({
175
+ where: mock(() => Promise.resolve(mockData)),
176
+ })),
177
+ })),
178
+ };
179
+ } else {
180
+ // latestRuns query
181
+ return {
182
+ from: mock(() => ({
183
+ groupBy: mock(() => Promise.resolve([])),
184
+ })),
185
+ };
186
+ }
187
+ });
188
+
189
+ await bootstrapHealthChecks({
190
+ db: mockDb as any,
191
+ queueManager: mockQueueManager,
192
+ logger: mockLogger,
193
+ });
194
+
195
+ expect(mockLogger.debug).toHaveBeenCalledWith(
196
+ "Bootstrapping 2 health checks"
197
+ );
198
+ expect(mockLogger.debug).toHaveBeenCalledWith(
199
+ "✅ Bootstrapped 2 health checks"
200
+ );
201
+ });
202
+
203
+ it("should handle empty health check list", async () => {
204
+ const mockQueueManager = createMockQueueManager();
205
+ const mockLogger = createMockLogger();
206
+ const mockDb = createMockDb();
207
+
208
+ // Override to return empty array
209
+ const mockSelectChain = mockDb.select();
210
+ const mockFromResult = (mockSelectChain as any).from();
211
+ Object.assign(mockFromResult, {
212
+ then: (resolve: any) => resolve([]),
213
+ });
214
+
215
+ await bootstrapHealthChecks({
216
+ db: mockDb as any,
217
+ queueManager: mockQueueManager,
218
+ logger: mockLogger,
219
+ });
220
+
221
+ expect(mockLogger.debug).toHaveBeenCalledWith(
222
+ "Bootstrapping 0 health checks"
223
+ );
224
+ expect(mockLogger.debug).toHaveBeenCalledWith(
225
+ "✅ Bootstrapped 0 health checks"
226
+ );
227
+ });
228
+ });
229
+ });