Files
edr-platform/apps/edr-freight-api/src/modules/warehouses/booking-unloaded-at-yard.spec.ts

80 lines
3.6 KiB
TypeScript

import { WarehouseInventoryService } from './warehouse-inventory.service';
/**
* A mid-corridor booking (import destined at an intermediate yard, or any
* DOMESTIC/intercity ride-along) used to have its booking.status flipped by
* the checkpoint-driven unload but never got a warehouse_inventory row — the
* Arrival Queue's unload count never moved and the booking was effectively
* stranded. handleBookingUnloadedAtYard reacts to the 'booking.unloadedAtYard'
* event BookingJourneyService.unloadBooking() emits and creates that row.
*/
function makeService(opts: {
existingInventory?: unknown;
booking?: Record<string, unknown> | null;
}) {
const created: Record<string, unknown>[] = [];
const inventoryRepository = {
findAll: jest.fn().mockResolvedValue(opts.existingInventory ? [opts.existingInventory] : []),
create: jest.fn((row: Record<string, unknown>) => {
created.push(row);
return Promise.resolve({ id: 'new-inv', ...row });
}),
};
const bookingRow =
opts.booking === undefined
? [{ weight: '10', freightType: 'CONTAINER', cargoTypeCode: 'GEN', customer: 'Acme' }]
: opts.booking
? [opts.booking]
: [];
const service = Object.create(WarehouseInventoryService.prototype) as Record<string, unknown>;
service.inventoryRepository = inventoryRepository;
service.dataSource = { query: jest.fn().mockResolvedValue(bookingRow), manager: {} };
service.allocation = { resolveLocation: jest.fn().mockResolvedValue(null) };
service.pickDefaultLocation = jest.fn().mockResolvedValue({ warehouseId: 'w1', yardId: 'y1', zoneId: 'z1' });
service.applyCapacityDelta = jest.fn().mockResolvedValue(undefined);
service.activityLog = { record: jest.fn().mockResolvedValue(undefined) };
service.logger = { warn: jest.fn() };
return { service: service as unknown as WarehouseInventoryService, created };
}
describe('handleBookingUnloadedAtYard', () => {
it('creates an UNLOADED row with an IMPORT GRN for a fresh import booking', async () => {
const { service, created } = makeService({});
await (service as unknown as { handleBookingUnloadedAtYard: (p: unknown) => Promise<void> })
.handleBookingUnloadedAtYard({ bookingId: 'b1', tradeDirection: 'IMPORT' });
expect(created).toHaveLength(1);
expect(created[0]).toMatchObject({ bookingId: 'b1', status: 'UNLOADED' });
expect(created[0].grnNumber).toMatch(/^GRN-IMPORT-/);
});
it('creates one for a DOMESTIC/intercity ride-along too', async () => {
const { service, created } = makeService({});
await (service as unknown as { handleBookingUnloadedAtYard: (p: unknown) => Promise<void> })
.handleBookingUnloadedAtYard({ bookingId: 'b2', tradeDirection: 'DOMESTIC' });
expect(created).toHaveLength(1);
expect(created[0].grnNumber).toMatch(/^GRN-DOMESTIC-/);
});
it('skips EXPORT — its warehouse record already exists from the origin receive', async () => {
const { service, created } = makeService({});
await (service as unknown as { handleBookingUnloadedAtYard: (p: unknown) => Promise<void> })
.handleBookingUnloadedAtYard({ bookingId: 'b3', tradeDirection: 'EXPORT' });
expect(created).toHaveLength(0);
});
it('is idempotent — a booking that already has an inventory row is left alone', async () => {
const { service, created } = makeService({ existingInventory: { id: 'existing' } });
await (service as unknown as { handleBookingUnloadedAtYard: (p: unknown) => Promise<void> })
.handleBookingUnloadedAtYard({ bookingId: 'b4', tradeDirection: 'IMPORT' });
expect(created).toHaveLength(0);
});
});