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:
Deploy
2026-08-25 23:41:29 +02:00
parent b6adf681d1
commit b6af852b54
29 changed files with 671 additions and 279 deletions

View File

@@ -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 () => {