132 lines
4.3 KiB
TypeScript
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);
|
|
});
|
|
});
|