mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 10:10:57 +00:00
99 lines
3.2 KiB
TypeScript
99 lines
3.2 KiB
TypeScript
import { ContractRateScheduleBuilder } from './contract-rate-schedule.builder';
|
|
import { Rate } from '../modules/rule-engine/entities/rate.entity';
|
|
|
|
/** Minimal Rate factory for the builder unit tests. */
|
|
function rate(partial: Partial<Rate>): Rate {
|
|
return {
|
|
trigger: 'ALWAYS',
|
|
appliesTo: 'CONTAINER',
|
|
tradeDirection: 'IMPORT',
|
|
rateType: 'CONTAINER_IMPORT',
|
|
currency: 'USD',
|
|
rateValue: 200,
|
|
rateUnit: 'PER_CONTAINER',
|
|
...partial,
|
|
} as Rate;
|
|
}
|
|
|
|
describe('ContractRateScheduleBuilder', () => {
|
|
const LIVE: Rate[] = [
|
|
rate({
|
|
appliesTo: 'CONTAINER',
|
|
tradeDirection: 'IMPORT',
|
|
rateType: 'CONTAINER_IMPORT',
|
|
rateValue: 200,
|
|
rateUnit: 'PER_CONTAINER',
|
|
originYard: { label: 'Negad' } as never,
|
|
destinationYard: { label: 'Mojo Dry Port' } as never,
|
|
containerType: { label: '40ft GP' } as never,
|
|
}),
|
|
rate({
|
|
appliesTo: 'CONTAINER',
|
|
tradeDirection: 'EXPORT', // wrong direction — must be filtered out for import
|
|
rateType: 'CONTAINER_EXPORT',
|
|
rateValue: 819,
|
|
originYard: { label: 'GMP' } as never,
|
|
destinationYard: { label: 'SGTD' } as never,
|
|
}),
|
|
rate({
|
|
appliesTo: 'BULK', // wrong freight — filtered out for a container contract
|
|
tradeDirection: 'IMPORT',
|
|
rateType: 'BULK_IMPORT',
|
|
rateUnit: 'PER_WAGON',
|
|
rateValue: 100,
|
|
}),
|
|
rate({
|
|
appliesTo: 'FIRST_MILE',
|
|
trigger: 'ALWAYS',
|
|
tradeDirection: null,
|
|
rateUnit: 'PER_CONTAINER',
|
|
rateValue: 50,
|
|
}),
|
|
rate({
|
|
appliesTo: 'OTHER',
|
|
trigger: 'CUSTOMS_CLEARANCE',
|
|
tradeDirection: null,
|
|
rateType: 'CUSTOMS_CLEARANCE',
|
|
rateUnit: 'FLAT',
|
|
rateValue: 120,
|
|
}),
|
|
];
|
|
|
|
const build = (dir: 'IMP' | 'EXP' | 'DOM', freight: 'CON' | 'BULK') => {
|
|
const service = { findLiveRatesDetailed: jest.fn().mockResolvedValue(LIVE) };
|
|
return new ContractRateScheduleBuilder(service as never).build(dir, freight);
|
|
};
|
|
|
|
it('shows only import container lanes for an import container contract', async () => {
|
|
const s = await build('IMP', 'CON');
|
|
expect(s.freightLanes).toHaveLength(1);
|
|
expect(s.freightLanes[0]).toMatchObject({
|
|
route: 'Negad → Mojo Dry Port',
|
|
cargo: '40ft GP',
|
|
currency: 'USD',
|
|
amount: '200',
|
|
unit: 'per container',
|
|
});
|
|
});
|
|
|
|
it('always lists route-agnostic services and surcharges', async () => {
|
|
const s = await build('IMP', 'CON');
|
|
expect(s.additionalServices).toHaveLength(1);
|
|
expect(s.additionalServices[0].route).toBe('First-mile pickup by truck');
|
|
expect(s.surcharges).toHaveLength(1);
|
|
expect(s.surcharges[0].route).toBe('Customs clearance service');
|
|
});
|
|
|
|
it('excludes container lanes from a bulk contract', async () => {
|
|
const s = await build('IMP', 'BULK');
|
|
expect(s.freightLanes).toHaveLength(1);
|
|
expect(s.freightLanes[0]).toMatchObject({ amount: '100', unit: 'per wagon' });
|
|
});
|
|
|
|
it('flags an empty schedule when nothing priced matches', async () => {
|
|
const service = { findLiveRatesDetailed: jest.fn().mockResolvedValue([]) };
|
|
const s = await new ContractRateScheduleBuilder(service as never).build('DOM', 'CON');
|
|
expect(s.isEmpty).toBe(true);
|
|
});
|
|
});
|