manicanldes-backend/test/pricing.spec.ts

132 lines
4.3 KiB
TypeScript

import { randomUUID } from 'node:crypto';
import { identityApp, type IdentityApp } from './helpers/identity-app';
import { checkoutFixture } from './helpers/checkout';
import { activatePricing, pricingInput } from './helpers/pricing';
import { secondActor } from './helpers/commerce';
describe('versioned pricing snapshots', () => {
let ctx: IdentityApp;
beforeAll(async () => {
ctx = await identityApp();
}, 60000);
afterAll(async () => {
await ctx.close();
});
beforeEach(async () => {
await ctx.clearLimits();
await ctx.db.pricingPolicy.updateMany({ data: { active: false } });
});
it('finalizes configured totals and preserves the exact policy on retries and reads', async () => {
const policy = await activatePricing(ctx);
const f = await checkoutFixture(ctx);
const order = await ctx
.api()
.post('/api/v1/checkout')
.auth(f.actor.token, { type: 'bearer' })
.send(f.input)
.expect(201);
expect(order.body).toMatchObject({
pricingStatus: 'FINALIZED',
taxTotal: '188.64',
shippingTotal: '50',
payableTotal: '1236.64',
paymentAvailable: false,
pricing: { policyId: policy.id },
});
await activatePricing(ctx, { merchandiseTaxBps: 0, shippingFee: '0.00' });
const replay = await ctx
.api()
.post('/api/v1/checkout')
.auth(f.actor.token, { type: 'bearer' })
.send(f.input)
.expect(201);
expect(replay.body).toEqual(order.body);
const detail = await ctx
.api()
.get('/api/v1/orders/' + order.body.id)
.auth(f.actor.token, { type: 'bearer' })
.expect(200);
expect(detail.body).toEqual(order.body);
await expect(
ctx.executeSql(
`UPDATE order_pricing SET payable_total = 0 WHERE order_id = '${order.body.id}'`,
),
).rejects.toThrow();
await expect(
ctx.executeSql(
`DELETE FROM order_pricing WHERE order_id = '${order.body.id}'`,
),
).rejects.toThrow();
await expect(
ctx.executeSql(
`UPDATE pricing_policies SET shipping_fee = 0 WHERE id = '${policy.id}'`,
),
).rejects.toThrow();
});
it('prefers a region-specific policy and never reprices earlier unconfigured orders', async () => {
const f = await checkoutFixture(ctx);
const old = await ctx
.api()
.post('/api/v1/checkout')
.auth(f.actor.token, { type: 'bearer' })
.send(f.input)
.expect(201);
expect(old.body.pricingStatus).toBe('UNFINALIZED');
await activatePricing(ctx);
const regional = await activatePricing(ctx, {
region: 'maharashtra',
merchandiseTaxBps: 0,
shippingFee: '0.00',
});
const next = await checkoutFixture(ctx);
const order = await ctx
.api()
.post('/api/v1/checkout')
.auth(next.actor.token, { type: 'bearer' })
.send(next.input)
.expect(201);
expect(order.body.pricing.policyId).toBe(regional.id);
expect(order.body.payableTotal).toBe('998');
const unchanged = await ctx
.api()
.get('/api/v1/orders/' + old.body.id)
.auth(f.actor.token, { type: 'bearer' })
.expect(200);
expect(unchanged.body.pricingStatus).toBe('UNFINALIZED');
});
it('enforces permissions and organization scoping for policy administration', async () => {
const customer = await secondActor(ctx);
await ctx
.api()
.post('/api/v1/admin/pricing-policies')
.auth(customer.token, { type: 'bearer' })
.send(pricingInput())
.expect(403);
const policy = await activatePricing(ctx);
const other = await secondActor(ctx, false, ['pricing.manage']);
const list = await ctx
.api()
.get('/api/v1/admin/pricing-policies')
.auth(other.token, { type: 'bearer' })
.expect(200);
expect(list.body).toEqual([]);
await ctx
.api()
.patch('/api/v1/admin/pricing-policies/' + policy.id + '/status')
.auth(other.token, { type: 'bearer' })
.send({ active: false })
.expect(404);
await ctx
.api()
.patch('/api/v1/admin/pricing-policies/' + randomUUID() + '/status')
.auth(ctx.token, { type: 'bearer' })
.send({ active: false })
.expect(404);
await ctx
.api()
.patch('/api/v1/admin/pricing-policies/' + policy.id + '/status')
.auth(ctx.token, { type: 'bearer' })
.send({ active: false })
.expect(200);
});
});