94 lines
3.1 KiB
TypeScript
94 lines
3.1 KiB
TypeScript
|
|
import { describe, it, expect } from 'vitest';
|
||
|
|
import { createPending, activate, endReservation } from '@/lib/services/berth-reservations.service';
|
||
|
|
import { makeBerth, makeClient, makePort, makeYacht, makeAuditMeta } from '../helpers/factories';
|
||
|
|
import { ConflictError } from '@/lib/errors';
|
||
|
|
|
||
|
|
describe('reservation exclusivity', () => {
|
||
|
|
it('two concurrent activates on same berth: one succeeds, one throws ConflictError', async () => {
|
||
|
|
const port = await makePort();
|
||
|
|
const berth = await makeBerth({ portId: port.id });
|
||
|
|
const clientA = await makeClient({ portId: port.id });
|
||
|
|
const clientB = await makeClient({ portId: port.id });
|
||
|
|
const yachtA = await makeYacht({
|
||
|
|
portId: port.id,
|
||
|
|
ownerType: 'client',
|
||
|
|
ownerId: clientA.id,
|
||
|
|
});
|
||
|
|
const yachtB = await makeYacht({
|
||
|
|
portId: port.id,
|
||
|
|
ownerType: 'client',
|
||
|
|
ownerId: clientB.id,
|
||
|
|
});
|
||
|
|
|
||
|
|
const resA = await createPending(
|
||
|
|
port.id,
|
||
|
|
{
|
||
|
|
berthId: berth.id,
|
||
|
|
clientId: clientA.id,
|
||
|
|
yachtId: yachtA.id,
|
||
|
|
startDate: new Date(),
|
||
|
|
},
|
||
|
|
makeAuditMeta({ portId: port.id }),
|
||
|
|
);
|
||
|
|
const resB = await createPending(
|
||
|
|
port.id,
|
||
|
|
{
|
||
|
|
berthId: berth.id,
|
||
|
|
clientId: clientB.id,
|
||
|
|
yachtId: yachtB.id,
|
||
|
|
startDate: new Date(),
|
||
|
|
},
|
||
|
|
makeAuditMeta({ portId: port.id }),
|
||
|
|
);
|
||
|
|
|
||
|
|
const results = await Promise.allSettled([
|
||
|
|
activate(resA.id, port.id, {}, makeAuditMeta({ portId: port.id })),
|
||
|
|
activate(resB.id, port.id, {}, makeAuditMeta({ portId: port.id })),
|
||
|
|
]);
|
||
|
|
const successes = results.filter((r) => r.status === 'fulfilled');
|
||
|
|
const failures = results.filter((r) => r.status === 'rejected');
|
||
|
|
expect(successes).toHaveLength(1);
|
||
|
|
expect(failures).toHaveLength(1);
|
||
|
|
expect((failures[0] as PromiseRejectedResult).reason).toBeInstanceOf(ConflictError);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('activating a second reservation after first is ended succeeds', async () => {
|
||
|
|
const port = await makePort();
|
||
|
|
const berth = await makeBerth({ portId: port.id });
|
||
|
|
const clientA = await makeClient({ portId: port.id });
|
||
|
|
const clientB = await makeClient({ portId: port.id });
|
||
|
|
const yachtA = await makeYacht({
|
||
|
|
portId: port.id,
|
||
|
|
ownerType: 'client',
|
||
|
|
ownerId: clientA.id,
|
||
|
|
});
|
||
|
|
const yachtB = await makeYacht({
|
||
|
|
portId: port.id,
|
||
|
|
ownerType: 'client',
|
||
|
|
ownerId: clientB.id,
|
||
|
|
});
|
||
|
|
|
||
|
|
const resA = await createPending(
|
||
|
|
port.id,
|
||
|
|
{ berthId: berth.id, clientId: clientA.id, yachtId: yachtA.id, startDate: new Date() },
|
||
|
|
makeAuditMeta({ portId: port.id }),
|
||
|
|
);
|
||
|
|
await activate(resA.id, port.id, {}, makeAuditMeta({ portId: port.id }));
|
||
|
|
await endReservation(
|
||
|
|
resA.id,
|
||
|
|
port.id,
|
||
|
|
{ endDate: new Date() },
|
||
|
|
makeAuditMeta({ portId: port.id }),
|
||
|
|
);
|
||
|
|
|
||
|
|
const resB = await createPending(
|
||
|
|
port.id,
|
||
|
|
{ berthId: berth.id, clientId: clientB.id, yachtId: yachtB.id, startDate: new Date() },
|
||
|
|
makeAuditMeta({ portId: port.id }),
|
||
|
|
);
|
||
|
|
await expect(
|
||
|
|
activate(resB.id, port.id, {}, makeAuditMeta({ portId: port.id })),
|
||
|
|
).resolves.toBeDefined();
|
||
|
|
});
|
||
|
|
});
|