From 9744a7ac23d75665c7653278ee43ba76102083b5 Mon Sep 17 00:00:00 2001 From: Paul Huliganga Date: Thu, 26 Mar 2026 15:55:12 -0400 Subject: [PATCH] test(workflow): add scheduler and health-check script tests --- .../__tests__/check-workflow-health.test.ts | 40 +++++++++++++++++++ scripts/__tests__/schedule-workflow.test.ts | 40 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 scripts/__tests__/check-workflow-health.test.ts create mode 100644 scripts/__tests__/schedule-workflow.test.ts diff --git a/scripts/__tests__/check-workflow-health.test.ts b/scripts/__tests__/check-workflow-health.test.ts new file mode 100644 index 0000000..2927afc --- /dev/null +++ b/scripts/__tests__/check-workflow-health.test.ts @@ -0,0 +1,40 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { mkdtemp, writeFile } from 'fs/promises'; +import * as os from 'os'; +import * as path from 'path'; +import { checkWorkflowHealth } from '../check-workflow-health.ts'; + +describe('check-workflow-health', () => { + let consoleSpy: any; + + beforeEach(() => { + consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); + }); + + afterEach(() => { + consoleSpy.mockRestore(); + }); + + it('maps status values to expected health outcomes', async () => { + const dir = await mkdtemp(path.join(os.tmpdir(), 'workflow-health-test-')); + + const cases: Array<{ status: string; expectedCode: number; expectedOk: boolean }> = [ + { status: 'idle', expectedCode: 0, expectedOk: true }, + { status: 'running', expectedCode: 0, expectedOk: true }, + { status: 'completed', expectedCode: 0, expectedOk: true }, + { status: 'failed', expectedCode: 1, expectedOk: false }, + { status: 'blocked', expectedCode: 1, expectedOk: false }, + ]; + + for (const c of cases) { + const statusPath = path.join(dir, `${c.status}.json`); + await writeFile(statusPath, JSON.stringify({ overallStatus: c.status }), 'utf8'); + + const code = await checkWorkflowHealth(statusPath); + expect(code).toBe(c.expectedCode); + + const lastOutput = consoleSpy.mock.calls.at(-1)?.[0] as string; + expect(JSON.parse(lastOutput)).toMatchObject({ ok: c.expectedOk, status: c.status }); + } + }); +}); diff --git a/scripts/__tests__/schedule-workflow.test.ts b/scripts/__tests__/schedule-workflow.test.ts new file mode 100644 index 0000000..882f850 --- /dev/null +++ b/scripts/__tests__/schedule-workflow.test.ts @@ -0,0 +1,40 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { runWorkflowMock, generateMorningReportMock } = vi.hoisted(() => ({ + runWorkflowMock: vi.fn(), + generateMorningReportMock: vi.fn(), +})); + +vi.mock('../run-workflow.ts', () => ({ + runWorkflow: runWorkflowMock, +})); + +vi.mock('../morning-report.ts', () => ({ + generateMorningReport: generateMorningReportMock, +})); + +import { runScheduledWorkflow } from '../schedule-workflow.ts'; + +describe('schedule-workflow', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('runs workflow in resume mode and then generates morning report', async () => { + runWorkflowMock.mockResolvedValue(undefined); + generateMorningReportMock.mockResolvedValue(undefined); + + await runScheduledWorkflow(); + + expect(runWorkflowMock).toHaveBeenCalledWith({ mode: 'resume' }); + expect(generateMorningReportMock).toHaveBeenCalledTimes(1); + }); + + it('propagates workflow failures and does not run report generation', async () => { + const boom = new Error('workflow exploded'); + runWorkflowMock.mockRejectedValue(boom); + + await expect(runScheduledWorkflow()).rejects.toThrow('workflow exploded'); + expect(generateMorningReportMock).not.toHaveBeenCalled(); + }); +});