import { TrainSchedulingService } from './services/train-scheduling.service'; import { TrainCheckpointEvent } from './entities/train-checkpoint-event.entity'; /** * Loading and unloading stamps decide two published figures — total handling * (unloading start to loading end) and the other activity left over from the * stay. A crossed or future pair would publish negative hours, so the guard is * the thing worth pinning down. */ const svc = Object.create(TrainSchedulingService.prototype) as { handlingPatch: ( dto: Record, existing?: TrainCheckpointEvent | null, ) => Record; }; const iso = (h: number): string => new Date(Date.UTC(2026, 6, 3, h)).toISOString(); const stop = (fields: Partial) => fields as TrainCheckpointEvent; describe('checkpoint handling times', () => { it('takes a sane handling window', () => { const patch = svc.handlingPatch({ unloadingStartedAt: iso(4), loadingCompletedAt: iso(9), }); expect(patch.unloadingStartedAt).toEqual(new Date(iso(4))); expect(patch.loadingCompletedAt).toEqual(new Date(iso(9))); }); it('rejects loading finishing before unloading started', () => { expect(() => svc.handlingPatch({ unloadingStartedAt: iso(9), loadingCompletedAt: iso(4) }), ).toThrow('Loading cannot finish before unloading started'); }); it('rejects a window that runs backwards', () => { expect(() => svc.handlingPatch({ loadingStartedAt: iso(9), loadingCompletedAt: iso(8) }), ).toThrow('Loading cannot finish before it started'); }); it('rejects a stamp in the future', () => { const tomorrow = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(); expect(() => svc.handlingPatch({ unloadingStartedAt: tomorrow })).toThrow( 'Unloading start cannot be in the future', ); }); // A body that moves one end of a window is still checked against the end // already stored, or a two-step edit could walk the stop into a crossed pair. it('checks a one-sided edit against the stored stop', () => { expect(() => svc.handlingPatch( { loadingCompletedAt: iso(4) }, stop({ unloadingStartedAt: new Date(iso(9)) }), ), ).toThrow('Loading cannot finish before unloading started'); }); it('clears a stamp on null and leaves an untouched one alone', () => { const patch = svc.handlingPatch( { unloadingStartedAt: null }, stop({ unloadingStartedAt: new Date(iso(4)), loadingCompletedAt: new Date(iso(9)) }), ); expect(patch).toEqual({ unloadingStartedAt: null }); }); });