import { Logger } from '@nestjs/common'; import { JobPartialFailure } from '../system/job-run.service'; import { DAILY_EVALUATION_STEP_ORDER, DailyEvaluationStepName, runDailyEvaluationSteps, } from './daily-evaluation-steps'; import type { JobResult } from './job'; function step( name: DailyEvaluationStepName, run: (today: string) => Promise, ): { name: DailyEvaluationStepName; run: jest.Mock } { return { name, run: jest.fn(run) }; } describe('runDailyEvaluationSteps', () => { const logger = new Logger('test'); let errorSpy: jest.SpyInstance; beforeEach(() => { errorSpy = jest.spyOn(logger, 'error').mockImplementation(() => undefined); }); afterEach(() => jest.restoreAllMocks()); it('has the steps of the plan in this order', () => expect(DAILY_EVALUATION_STEP_ORDER).toEqual([ 'phenology', 'time-events', 'autopilot', 'suggestions-expiry', 'digest', ])); it('runs the steps in the fixed order with the day and merges their counters', async () => { const order: string[] = []; const digest = step('digest', () => { order.push('digest'); return Promise.resolve({ digests: 3 }); }); const timeEvents = step('time-events', () => { order.push('time-events'); return Promise.resolve({ TaskDue: 1 }); }); const { result, failed } = await runDailyEvaluationSteps( [digest, timeEvents], '2026-06-10', logger, ); expect(order).toEqual(['time-events', 'digest']); expect(timeEvents.run).toHaveBeenCalledWith('2026-06-10'); expect(result).toEqual({ TaskDue: 1, digests: 3 }); expect(failed).toEqual([]); }); it('a failing step leaves the others running and the run FAILED with all counters', async () => { const phenology = step('phenology', () => Promise.resolve({ phenologyApiaries: 2 }), ); const timeEvents = step('time-events', () => Promise.reject( new JobPartialFailure({ WithdrawalEnded: 1, TaskDue: 0 }, ['TaskDue']), ), ); const autopilot = step('autopilot', () => Promise.reject(new Error('connection reset')), ); const digest = step('digest', () => Promise.resolve({ digests: 4 })); const { result, failed, failures } = await runDailyEvaluationSteps( [phenology, timeEvents, autopilot, digest], '2026-06-10', logger, ); expect(digest.run).toHaveBeenCalled(); expect(failed).toEqual(['time-events', 'autopilot']); expect(failures).toEqual(['time-events: TaskDue', 'autopilot']); expect(result).toEqual({ phenologyApiaries: 2, WithdrawalEnded: 1, TaskDue: 0, digests: 4, }); expect(errorSpy).toHaveBeenCalledWith( 'Step "autopilot" failed: connection reset', expect.any(String), ); }); });