fix(release): 0.2.8 harden admin proxy, cart stock caps, selfpay and refund timeline
- admin proxy: 25s timeout, body guards, structured failure logs - cart: addItem enforces stock cap (409 INSUFFICIENT_STOCK), UI clamps qty - tpv selfpay: hide sidebar/discounts/save-pending, rename button, receipt-settings 400 fix - pos admin: quick products slot count aligned to 8 - returns: human-readable history message + metadata jsonb (migrations 064-065) + admin fallback formatter - storefront: product card white background - product page: remove duplicate stock label under add-to-cart button
This commit is contained in:
@@ -105,28 +105,52 @@ describe.skipIf(!hasDb)('cart flows (real PostgreSQL)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('flags cart item unavailable when variant is out of stock (AC2)', async () => {
|
||||
it('rejects add when stock is zero (AC2 — F-138)', async () => {
|
||||
const productId = randomUUID();
|
||||
const variantId = randomUUID();
|
||||
await setPrice(variantId, 500);
|
||||
await setStock(variantId, 0);
|
||||
await app.inject({
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/cart/items',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
cookies: { [SESSION_COOKIE_NAME]: cookie },
|
||||
payload: { productId, variantId, quantity: 1 },
|
||||
});
|
||||
expect(response.statusCode).toBe(409);
|
||||
expect(response.json()).toMatchObject({ error: { code: 'INSUFFICIENT_STOCK' } });
|
||||
|
||||
const cart = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/cart',
|
||||
cookies: { [SESSION_COOKIE_NAME]: cookie },
|
||||
});
|
||||
const item = (cart.json().items as Array<{ variantId: string; available: boolean }>).find(
|
||||
(entry) => entry.variantId === variantId,
|
||||
);
|
||||
expect(item).toMatchObject({ available: false });
|
||||
const items = cart.json().items as Array<{ variantId: string }>;
|
||||
expect(items.find((entry) => entry.variantId === variantId)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('rejects add when total quantity exceeds stock (AC2b — F-138)', async () => {
|
||||
const productId = randomUUID();
|
||||
const variantId = randomUUID();
|
||||
await setPrice(variantId, 500);
|
||||
await setStock(variantId, 3);
|
||||
const first = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/cart/items',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
cookies: { [SESSION_COOKIE_NAME]: cookie },
|
||||
payload: { productId, variantId, quantity: 2 },
|
||||
});
|
||||
expect(first.statusCode).toBe(201);
|
||||
const oversell = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/cart/items',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
cookies: { [SESSION_COOKIE_NAME]: cookie },
|
||||
payload: { productId, variantId, quantity: 2 },
|
||||
});
|
||||
expect(oversell.statusCode).toBe(409);
|
||||
expect(oversell.json()).toMatchObject({ error: { code: 'INSUFFICIENT_STOCK' } });
|
||||
});
|
||||
|
||||
it('ignores client-supplied price fields in cart payloads (AC3)', async () => {
|
||||
|
||||
Reference in New Issue
Block a user