import { ConflictException } from '@nestjs/common'; import { ContractsService } from './contracts.service'; import type { CreateContractDto } from './dto/create-contract.dto'; /** * The duplicate guard blocks a new request only when EVERY commercial * dimension matches a live contract — service type, operation type, contract * kind, cargo scope and route. Any one differing must let the request through. */ describe('ContractsService duplicate guard', () => { const LANE = { originYardId: 'yard-dj', destinationYardId: 'yard-mj' }; const existing = { id: 'c-1', reference: 'CTR-2026-00001', status: 'PENDING_APPROVAL', contractValidUntil: null, tradeDirection: 'IMPORT', contractKind: 'ONE_TIME', freightType: 'CONTAINER', routes: [LANE], cargoScope: [{ containerSize: '20ft' }, { containerSize: '40ft' }], }; const dto = (overrides: Partial = {}) => ({ serviceTypeId: 'svc-1', tradeDirection: 'IMPORT', contractKind: 'ONE_TIME', freightType: 'CONTAINER', routes: [LANE], cargoScope: [{ containerSize: '20ft' }, { containerSize: '40ft' }], ...overrides, }) as CreateContractDto; const guard = (input: CreateContractDto) => { const service = new ContractsService( {} as never, { findDuplicateCandidates: async () => [existing] } as never, {} as never, {} as never, {} as never, {} as never, ); return ( service as unknown as { assertNoDuplicateContract(companyId: string, dto: CreateContractDto): Promise; } ).assertNoDuplicateContract('company-1', input); }; it('blocks an identical request', async () => { await expect(guard(dto())).rejects.toBeInstanceOf(ConflictException); }); it.each([ ['operation type', { tradeDirection: 'EXPORT' }], ['contract kind', { contractKind: 'GENERAL' }], ['freight type', { freightType: 'BULK' }], ['cargo scope', { cargoScope: [{ containerSize: '20ft' }] }], ['route', { routes: [{ originYardId: 'yard-dj', destinationYardId: 'yard-aa' }] }], ])('allows a request with a different %s', async (_label, overrides) => { await expect(guard(dto(overrides as Partial))).resolves.toBeUndefined(); }); it('ignores quantity caps when comparing cargo scope', async () => { await expect( guard( dto({ cargoScope: [ { containerSize: '20ft', quantityCap: 10 }, { containerSize: '40ft', quantityCap: 5 }, ], }), ), ).rejects.toBeInstanceOf(ConflictException); }); });