import { randomUUID } from 'node:crypto'; import { identityApp, type IdentityApp } from './helpers/identity-app'; import { checkoutFixture, couponInput } from './helpers/checkout'; import { secondActor } from './helpers/commerce'; describe('coupon administration', () => { let ctx: IdentityApp; beforeAll(async () => { ctx = await identityApp(); }, 60000); afterAll(async () => { await ctx.close(); }); beforeEach(async () => { await ctx.clearLimits(); }); it('requires permissions, scopes reads and deactivates immutable coupon rules', async () => { const customer = await secondActor(ctx); await ctx .api() .post('/api/v1/coupons') .auth(customer.token, { type: 'bearer' }) .send(couponInput()) .expect(403); const created = await ctx .api() .post('/api/v1/coupons') .auth(ctx.token, { type: 'bearer' }) .send(couponInput()) .expect(201); const list = await ctx .api() .get('/api/v1/coupons') .auth(ctx.token, { type: 'bearer' }) .expect(200); expect( list.body.some((row: { id: string }) => row.id === created.body.id), ).toBe(true); const foreign = await secondActor(ctx, false, ['coupons.manage']); const empty = await ctx .api() .get('/api/v1/coupons') .auth(foreign.token, { type: 'bearer' }) .expect(200); expect(empty.body).toEqual([]); await ctx .api() .patch('/api/v1/coupons/' + created.body.id + '/status') .auth(foreign.token, { type: 'bearer' }) .send({ active: false }) .expect(404); await ctx .api() .patch('/api/v1/coupons/' + created.body.id + '/status') .auth(ctx.token, { type: 'bearer' }) .send({ active: false }) .expect(200); await expect( ctx.executeSql( `UPDATE coupons SET percent_bps = 5000 WHERE id = '${created.body.id}'`, ), ).rejects.toThrow(); const f = await checkoutFixture(ctx); await ctx .api() .post('/api/v1/checkout') .auth(f.actor.token, { type: 'bearer' }) .send({ ...f.input, couponCode: created.body.code }) .expect(409); }); it('caps fixed discounts at subtotal and rejects duplicate normalized codes', async () => { const input = couponInput(); const { percentBps: _percent, ...common } = input; const fixed = { ...common, kind: 'FIXED', amount: '9999.00' }; const coupon = await ctx .api() .post('/api/v1/coupons') .auth(ctx.token, { type: 'bearer' }) .send(fixed) .expect(201); await ctx .api() .post('/api/v1/coupons') .auth(ctx.token, { type: 'bearer' }) .send({ ...fixed, code: fixed.code.toLowerCase() }) .expect(409); const f = await checkoutFixture(ctx); const order = await ctx .api() .post('/api/v1/checkout') .auth(f.actor.token, { type: 'bearer' }) .send({ ...f.input, couponCode: coupon.body.code }) .expect(201); expect(order.body.merchandiseTotal).toBe('0'); expect(order.body.paymentAvailable).toBe(false); await ctx .api() .patch('/api/v1/coupons/' + randomUUID() + '/status') .auth(ctx.token, { type: 'bearer' }) .send({ active: false }) .expect(404); }); });