import { randomUUID } from 'node:crypto'; import { identityApp, type IdentityApp } from './helpers/identity-app'; import { checkoutFixture, couponInput } from './helpers/checkout'; import { secondActor, seedStock, addressInput } from './helpers/commerce'; describe('atomic checkout and order snapshots', () => { let ctx: IdentityApp; beforeAll(async () => { ctx = await identityApp(); }); afterAll(async () => { await ctx.close(); }); beforeEach(async () => { await ctx.clearLimits(); }); it('snapshots server prices and address, holds stock, clears cart and replays safely', async () => { const f = await checkoutFixture(ctx); const created = await ctx .api() .post('/api/v1/checkout') .auth(f.actor.token, { type: 'bearer' }) .send(f.input) .expect(201); expect(created.body).toMatchObject({ subtotal: '998', discount: '0', merchandiseTotal: '998', status: 'PENDING_PAYMENT', pricingStatus: 'UNFINALIZED', payableTotal: null, paymentAvailable: false, }); expect(created.body.requestHash).toBeUndefined(); expect( await ctx.db.stockReservation.count({ where: { orderId: created.body.id }, }), ).toBe(1); expect( (await ctx.db.stockItem.findUniqueOrThrow({ where: { id: f.stock.id } })) .onHand, ).toBe(10); await ctx.db.productVariant.update({ where: { id: f.variant.id }, data: { price: '1.00', name: 'Changed' }, }); await ctx .api() .put('/api/v1/addresses/' + f.address.id) .auth(f.actor.token, { type: 'bearer' }) .send({ ...addressInput, recipient: 'Changed' }) .expect(200); const replay = await ctx .api() .post('/api/v1/checkout') .auth(f.actor.token, { type: 'bearer' }) .send(f.input) .expect(201); expect(replay.body).toEqual(created.body); expect(replay.body.address.recipient).toBe(addressInput.recipient); const cart = await ctx .api() .get('/api/v1/cart') .auth(f.actor.token, { type: 'bearer' }) .expect(200); expect(cart.body).toEqual({ version: 2, lines: [] }); const conflict = await ctx .api() .post('/api/v1/checkout') .auth(f.actor.token, { type: 'bearer' }) .send({ ...f.input, cartVersion: 2 }) .expect(409); expect(conflict.body.code).toBe('IDEMPOTENCY_CONFLICT'); }); it('rolls back orders and partial holds on a stock shortage, preserving cart', async () => { const f = await checkoutFixture(ctx); const unavailable = await seedStock(ctx, 0); await ctx .api() .put('/api/v1/cart/lines/' + unavailable.variant.id) .auth(f.actor.token, { type: 'bearer' }) .send({ quantity: 1, version: 1 }) .expect(200); const response = await ctx .api() .post('/api/v1/checkout') .auth(f.actor.token, { type: 'bearer' }) .send({ ...f.input, cartVersion: 2 }) .expect(409); expect(response.body.code).toBe('STOCK_INSUFFICIENT'); expect( await ctx.db.order.count({ where: { userId: f.actor.userId } }), ).toBe(0); expect( await ctx.db.stockReservation.count({ where: { userId: f.actor.userId }, }), ).toBe(0); expect( await ctx.db.cartLine.count({ where: { cart: { userId: f.actor.userId } }, }), ).toBe(2); }); it('validates live cart version, private address and coupon eligibility before writes', async () => { const f = await checkoutFixture(ctx); for (const [change, code] of [ [{ cartVersion: 0 }, 'CART_CHANGED'], [{ addressId: randomUUID() }, 'ADDRESS_NOT_FOUND'], [{ couponCode: 'MISSING' }, 'COUPON_INELIGIBLE'], ] as const) { const response = await ctx .api() .post('/api/v1/checkout') .auth(f.actor.token, { type: 'bearer' }) .send({ ...f.input, ...change }) .expect(code === 'ADDRESS_NOT_FOUND' ? 404 : 409); expect(response.body.code).toBe(code); } const other = await secondActor(ctx); await ctx .api() .post('/api/v1/checkout') .auth(other.token, { type: 'bearer' }) .send(f.input) .expect(409); await ctx .api() .post('/api/v1/checkout') .auth(f.actor.token, { type: 'bearer' }) .send({ ...f.input, subtotal: '0.01' }) .expect(400); }); it('applies a coupon exactly once and releases its usage on cancellation', async () => { const f = await checkoutFixture(ctx); const coupon = await ctx .api() .post('/api/v1/coupons') .auth(ctx.token, { type: 'bearer' }) .send(couponInput()) .expect(201); const input = { ...f.input, couponCode: coupon.body.code }; const order = await ctx .api() .post('/api/v1/checkout') .auth(f.actor.token, { type: 'bearer' }) .send(input) .expect(201); expect(order.body).toMatchObject({ subtotal: '998', discount: '99.8', merchandiseTotal: '898.2', }); await ctx .api() .put('/api/v1/cart/lines/' + f.variant.id) .auth(f.actor.token, { type: 'bearer' }) .send({ quantity: 1, version: 2 }) .expect(200); const next = { ...input, cartVersion: 3, idempotencyKey: randomUUID() }; await ctx .api() .post('/api/v1/checkout') .auth(f.actor.token, { type: 'bearer' }) .send(next) .expect(409); await ctx .api() .post('/api/v1/orders/' + order.body.id + '/cancel') .auth(f.actor.token, { type: 'bearer' }) .expect(201); await ctx .api() .post('/api/v1/checkout') .auth(f.actor.token, { type: 'bearer' }) .send(next) .expect(201); }); });