Files
edr-platform/apps/edr-freight-api/src/modules/rule-engine/rule-engine.service.spec.ts
Marshal 835c9e111c feat(train-scheduling): implement container movement between wagons
- Added functionality to move containers between wagons in the train scheduling system.
- Introduced  API endpoint and service method to handle container movement.
- Updated  component to support drag-and-drop for rearranging containers.
- Enhanced  to allow moving containers to other wagons via a context menu.
- Implemented UI feedback for container movement actions, including loading states and success/error notifications.
- Updated relevant types and constants to accommodate new container movement logic.
- Added tests for the rule engine to ensure proper handling of hazardous bookings.
2026-07-21 23:02:06 +00:00

79 lines
2.7 KiB
TypeScript

import { RuleEngineService } from './rule-engine.service';
import type { BookingEvaluationInput } from './rule-engine.service';
import type { Rate } from './entities/rate.entity';
describe('RuleEngineService — requested service without a configured surcharge rate', () => {
const hazardRate: Rate = {
id: 'rate-hazard',
rateType: 'HAZARD_SURCHARGE',
trigger: 'HAZARDOUS',
rateValue: 50,
rateUnit: 'PER_CONTAINER',
currency: 'USD',
status: 'LIVE',
containerTypeId: null,
cargoTypeId: null,
} as Rate;
let ratesRepo: { findLiveRates: jest.Mock };
let service: RuleEngineService;
beforeEach(() => {
ratesRepo = { findLiveRates: jest.fn().mockResolvedValue([]) };
service = new RuleEngineService(
{ findById: jest.fn().mockResolvedValue(null) } as never, // cargoTypes
{ findById: jest.fn().mockResolvedValue(null) } as never, // serviceTypes
{ findActiveByContainerTypeId: jest.fn().mockResolvedValue([]) } as never, // weightLimits
{ findAllActive: jest.fn().mockResolvedValue([]) } as never, // priorityConfigs
ratesRepo as never,
{ findById: jest.fn().mockResolvedValue(null) } as never, // shippingLines
{} as never, // dataSource (unused by evaluate)
);
});
const input = (overrides: Partial<BookingEvaluationInput>): BookingEvaluationInput => ({
serviceTypeId: 'svc-1',
paymentCurrency: 'USD',
tradeDirection: 'IMPORT',
isHazardous: false,
totalWagons: 1,
containers: [],
...overrides,
});
it('hard-blocks a hazardous booking when no HAZARDOUS surcharge rate is LIVE', async () => {
const result = await service.evaluate(input({ isHazardous: true }));
expect(result.hardBlocked).toHaveLength(1);
expect(result.hardBlocked[0]).toContain('hazardous');
});
it('passes a hazardous booking when a HAZARDOUS surcharge rate is LIVE', async () => {
ratesRepo.findLiveRates.mockResolvedValue([hazardRate]);
const result = await service.evaluate(input({ isHazardous: true }));
expect(result.hardBlocked).toHaveLength(0);
});
it('does not block a non-hazardous booking when no surcharge rates exist', async () => {
const result = await service.evaluate(input({}));
expect(result.hardBlocked).toHaveLength(0);
});
it('hard-blocks on per-container opt-in counts even without the booking-level flag', async () => {
const result = await service.evaluate(
input({
containers: [
{
containerTypeId: 'ct-20',
quantity: 2,
vgmPerUnitTons: 10,
totalVgmTons: 20,
reeferQuantity: 1,
},
],
}),
);
expect(result.hardBlocked).toHaveLength(1);
expect(result.hardBlocked[0]).toContain('reefer');
});
});