manicanldes-backend/test/checkout-limits.spec.ts

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',
);
});
});