import { computeScheduleWagonUsage } from './schedule-wagon-usage.util'; /** A coupled slot; `allocated` = a booking actually sits on it. */ const slot = (allocated = false) => ({ allocations: allocated ? [{}] : [] }); const booking = (wagonsRequired: number | null) => ({ booking: { wagonsRequired } }); describe('computeScheduleWagonUsage', () => { it('reports allocated slots as used, not the coupled consist size', () => { // The reported bug: a 37-wagon consist carrying 3 allocated bookings read // "37 wgn used" in the list while the detail page read "3 in use". const slots = [...Array(34).fill(slot(false)), ...Array(3).fill(slot(true))]; const usage = computeScheduleWagonUsage({ wagonSlots: slots, storedWagonCount: 37, scheduleBookings: [], }); expect(usage.wagonsUsed).toBe(3); expect(usage.wagonsTotal).toBe(37); }); it('counts a built train with no bookings as 0 used', () => { const usage = computeScheduleWagonUsage({ wagonSlots: Array(40).fill(slot(false)), storedWagonCount: 40, scheduleBookings: [], }); expect(usage.wagonsUsed).toBe(0); expect(usage.wagonsRemaining).toBe(40); }); it('treats wagons of an unpaid booking as reserved, so they are not bookable', () => { // Booking claims 5 wagons but has no wagon plan yet: 0 used, still only 5 // bookable on a 10-wagon train — the reservation is not free space. const usage = computeScheduleWagonUsage({ wagonSlots: Array(10).fill(slot(false)), storedWagonCount: 10, scheduleBookings: [booking(5)], }); expect(usage.wagonsUsed).toBe(0); expect(usage.wagonsReserved).toBe(5); expect(usage.wagonsRemaining).toBe(5); }); it('does not double-count a booking that is both reserved and allocated', () => { // 3 allocated slots for a booking that reserved 3 wagons: 7 remain, not 4. const usage = computeScheduleWagonUsage({ wagonSlots: [...Array(7).fill(slot(false)), ...Array(3).fill(slot(true))], storedWagonCount: 10, scheduleBookings: [booking(3)], }); expect(usage.wagonsUsed).toBe(3); expect(usage.wagonsReserved).toBe(3); expect(usage.wagonsRemaining).toBe(7); }); it('never reports negative remaining when claims exceed the consist', () => { const usage = computeScheduleWagonUsage({ wagonSlots: Array(2).fill(slot(false)), storedWagonCount: 2, scheduleBookings: [booking(5)], }); expect(usage.wagonsRemaining).toBe(0); }); it('falls back to the stored counter when slot rows were not loaded', () => { const usage = computeScheduleWagonUsage({ wagonSlots: [], storedWagonCount: 12, scheduleBookings: [], }); expect(usage.wagonsTotal).toBe(12); expect(usage.wagonsUsed).toBe(0); }); it('tolerates missing relations and null wagonsRequired', () => { const usage = computeScheduleWagonUsage({ wagonSlots: null, storedWagonCount: null, scheduleBookings: [booking(null)], }); expect(usage).toEqual({ wagonsUsed: 0, wagonsTotal: 0, wagonsReserved: 0, wagonsRemaining: 0, }); }); });