import { Logger } from '@nestjs/common'; import { JobPartialFailure } from '../system/job-run.service'; import { DailyEvaluationJob } from './daily-evaluation.job'; import type { TimeEventsStep } from './steps/time-events.step'; describe('DailyEvaluationJob', () => { const COUNTS = { WithdrawalEnded: 1, TaskDue: 1, QueenAged: 0, VarroaMeasurementDue: 0, InspectionOverdue: 0, }; let timeEvents: { name: 'time-events'; run: jest.Mock }; let job: DailyEvaluationJob; beforeEach(() => { jest.useFakeTimers({ now: new Date('2026-06-10T02:30:00.000Z') }); timeEvents = { name: 'time-events', run: jest.fn() }; timeEvents.run.mockResolvedValue(COUNTS); job = new DailyEvaluationJob(timeEvents as unknown as TimeEventsStep); }); afterEach(() => { jest.useRealTimers(); jest.restoreAllMocks(); }); it('is the daily-evaluation job', () => { expect(job.name).toBe('daily-evaluation'); }); it('runs the steps for the Berlin day of the run and reports their counters', async () => { expect(await job.run()).toEqual(COUNTS); expect(timeEvents.run).toHaveBeenCalledWith('2026-06-10'); }); it('takes the Berlin day, not the UTC day, around midnight', async () => { jest.setSystemTime(new Date('2026-06-09T22:30:00.000Z')); await job.run(); expect(timeEvents.run).toHaveBeenCalledWith('2026-06-10'); }); it('a failing step fails the run with the counters of all steps', async () => { jest.spyOn(Logger.prototype, 'error').mockImplementation(() => undefined); timeEvents.run.mockRejectedValue( new JobPartialFailure({ WithdrawalEnded: 1 }, ['TaskDue']), ); const failure = await job.run().then( () => null, (error: unknown) => error, ); expect(failure).toBeInstanceOf(JobPartialFailure); expect(failure).toMatchObject({ failedSteps: ['time-events'], result: { WithdrawalEnded: 1 }, message: 'Daily evaluation failed for: time-events: TaskDue', }); }); });