import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; const { readMock, pendingMock } = vi.hoisted(() => ({ readMock: vi.fn(), pendingMock: vi.fn(), })); vi.mock('../../src/backend/services/WorkflowStatusManager', () => ({ WorkflowStatusManager: vi.fn().mockImplementation(() => ({ read: readMock, })), })); vi.mock('../../src/backend/services/PhaseUpdateQueue', () => ({ getPendingPhaseUpdates: pendingMock, })); import main from '../morning-report'; describe('morning-report', () => { let consoleSpy: any; beforeEach(() => { vi.clearAllMocks(); consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); }); afterEach(() => { consoleSpy.mockRestore(); }); it('detects stalled workflow and formats report', async () => { const now = new Date('2026-03-26T18:00:00.000Z'); readMock.mockResolvedValue({ currentPhase: 'parse', overallStatus: 'running', lastUpdated: '2026-03-26T16:00:00.000Z', lastFailureReason: null, nextAction: '', completedPhases: ['a'], }); pendingMock.mockResolvedValue([ { id: 'foo', eventType: 'phase_started', phase: 'parse', timestamp: '2026-03-26T16:00:00.000Z', summary: 'Phase parse started', relayStatus: 'pending', }, ]); await main({ commitWindowHours: 24, stalledThresholdMinutes: 60, now, getRecentCommitsFn: async () => [ { hash: 'abc123', msg: 'test commit', date: '2026-03-26T10:00:00.000Z' }, ], }); const output = consoleSpy.mock.calls[0][0] as string; expect(output).toContain('⚠️ Workflow Stalled'); expect(output).toContain('abc123'); expect(output).toContain('[phase_started]'); expect(output).toContain('[id: foo]'); }); it('shows empty/no-status states', async () => { const now = new Date('2026-03-26T11:00:00.000Z'); readMock.mockResolvedValue(null); pendingMock.mockResolvedValue([]); await main({ commitWindowHours: 24, stalledThresholdMinutes: 60, now, getRecentCommitsFn: async () => [], }); const output = consoleSpy.mock.calls[0][0] as string; expect(output).toContain('(No commits in window)'); expect(output).toContain('No workflow status available'); expect(output).toContain('All phase updates relayed'); }); });