103 lines
3.3 KiB
TypeScript
103 lines
3.3 KiB
TypeScript
import { CartStore } from '../src/checkout/cart.store';
|
|
import { CheckoutStore } from '../src/checkout/checkout.store';
|
|
import { holdOrderStock } from '../src/checkout/stock-allocation';
|
|
import { orderView } from '../src/checkout/order-view';
|
|
import type { AccessStore } from '../src/identity/access.store';
|
|
import type { Principal } from '../src/identity/identity.types';
|
|
import { Prisma, type Order } from '../src/generated/prisma/client';
|
|
|
|
const actor = { userId: 'user', organizationId: 'org' } as Principal;
|
|
function accessFor(tx: unknown) {
|
|
return {
|
|
mutate: (
|
|
_actor: unknown,
|
|
_permission: unknown,
|
|
work: (tx: unknown) => unknown,
|
|
) => work(tx),
|
|
} as AccessStore;
|
|
}
|
|
describe('checkout resource limits and expiration', () => {
|
|
it('rejects a 21st cart line before writing', async () => {
|
|
const tx = {
|
|
cart: { upsert: jest.fn().mockResolvedValue({ id: 'cart', version: 1 }) },
|
|
productVariant: {
|
|
findFirst: jest.fn().mockResolvedValue({ currency: 'INR' }),
|
|
},
|
|
cartLine: {
|
|
findMany: jest.fn().mockResolvedValue(
|
|
Array.from({ length: 20 }, (_, index) => ({
|
|
variantId: String(index),
|
|
variant: { currency: 'INR' },
|
|
})),
|
|
),
|
|
upsert: jest.fn(),
|
|
},
|
|
};
|
|
await expect(
|
|
new CartStore(accessFor(tx)).set(actor, 'new', 1, 1),
|
|
).rejects.toMatchObject({ code: 'CART_LIMIT' });
|
|
expect(tx.cartLine.upsert).not.toHaveBeenCalled();
|
|
});
|
|
it('rejects an eleventh active order before creating an order or stock hold', async () => {
|
|
const tx = {
|
|
order: {
|
|
findUnique: jest.fn().mockResolvedValue(null),
|
|
count: jest.fn().mockResolvedValue(10),
|
|
create: jest.fn(),
|
|
},
|
|
};
|
|
await expect(
|
|
new CheckoutStore(accessFor(tx)).create(actor, {
|
|
cartVersion: 1,
|
|
addressId: 'address',
|
|
idempotencyKey: 'key',
|
|
}),
|
|
).rejects.toMatchObject({ code: 'ORDER_LIMIT' });
|
|
expect(tx.order.create).not.toHaveBeenCalled();
|
|
});
|
|
it('bounds stock allocation work before acquiring locks', async () => {
|
|
const tx = {
|
|
stockItem: { findMany: jest.fn().mockResolvedValue(Array(201).fill({})) },
|
|
$queryRaw: jest.fn(),
|
|
};
|
|
await expect(
|
|
holdOrderStock(
|
|
tx as unknown as Prisma.TransactionClient,
|
|
actor,
|
|
'order',
|
|
new Date(),
|
|
[],
|
|
),
|
|
).rejects.toMatchObject({ code: 'STOCK_ALLOCATION_LIMIT' });
|
|
expect(tx.$queryRaw).not.toHaveBeenCalled();
|
|
});
|
|
it('exposes expiry without pretending an unfinalized order is payable', () => {
|
|
const order: Order & { lines: [] } = {
|
|
id: 'order',
|
|
organizationId: 'org',
|
|
userId: 'user',
|
|
status: 'PENDING_PAYMENT',
|
|
currency: 'INR',
|
|
subtotal: new Prisma.Decimal(1),
|
|
discount: new Prisma.Decimal(0),
|
|
merchandiseTotal: new Prisma.Decimal(1),
|
|
addressSnapshot: {},
|
|
couponSnapshot: null,
|
|
couponId: null,
|
|
idempotencyKey: 'key',
|
|
requestHash: 'hash',
|
|
createdAt: new Date(0),
|
|
expiresAt: new Date(1),
|
|
lines: [],
|
|
};
|
|
expect(orderView(order)).toMatchObject({
|
|
status: 'EXPIRED',
|
|
payableTotal: null,
|
|
paymentAvailable: false,
|
|
});
|
|
expect(orderView({ ...order, status: 'CANCELLED' }).status).toBe(
|
|
'CANCELLED',
|
|
);
|
|
});
|
|
});
|