import { ContractsRepository } from './contracts.repository'; /** * A ONE_TIME contract stops blocking a duplicate request only once its booking * is PAID. The existing duplicate-guard spec stubs the repository out, so the * candidate SQL itself is unchecked there — this pins the predicate. */ describe('findDuplicateCandidates ONE_TIME paid gate', () => { const candidateSql = (): string => { const conditions: string[] = []; const qb = { leftJoinAndSelect: () => qb, where: () => qb, andWhere: (condition: string) => { if (typeof condition === 'string') conditions.push(condition); return qb; }, getMany: async () => [], }; const repository = new ContractsRepository( { createQueryBuilder: () => qb } as never, {} as never, ); void repository.findDuplicateCandidates('company-1', 'svc-1'); return conditions.join(' AND '); }; it('spends the contract on payment, not on the booking row existing', () => { const sql = candidateSql(); expect(sql).toContain("contract.contract_kind <> 'ONE_TIME'"); // The gate: an unpaid booking must NOT free the lane. expect(sql).toContain("b.payment_status = 'PAID'"); expect(sql).toContain('b.deleted_at IS NULL'); }); });