Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { z } from 'zod';

import { ShopAdminTopbar } from '@/components/shop/admin/ShopAdminTopbar';
import { db } from '@/db';
import { productPrices,products } from '@/db/schema';
import { productPrices, products } from '@/db/schema';
import { guardShopAdminPage } from '@/lib/auth/guard-shop-admin-page';
import { issueCsrfToken } from '@/lib/security/csrf';
import type { CurrencyCode } from '@/lib/shop/currency';
Expand Down
4 changes: 2 additions & 2 deletions frontend/app/[locale]/shop/admin/products/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ export default async function AdminProductsPage({
<div className="grid grid-cols-2 gap-2">
<Link
href={`/shop/products/${row.slug}`}
className="border-border text-foreground hover:bg-secondary inline-flex items-center justify-center rounded-md border px-2 py-1 text-center text-xs leading-tight font-medium break-words whitespace-normal transition-colors"
className="border-border text-foreground hover:bg-secondary break-words inline-flex items-center justify-center rounded-md border px-2 py-1 text-center text-xs leading-tight font-medium whitespace-normal transition-colors"
aria-label={t('actions.viewProduct', {
title: row.title,
})}
Expand All @@ -409,7 +409,7 @@ export default async function AdminProductsPage({

<Link
href={`/shop/admin/products/${row.id}/edit`}
className="border-border text-foreground hover:bg-secondary inline-flex items-center justify-center rounded-md border px-2 py-1 text-center text-xs leading-tight font-medium break-words whitespace-normal transition-colors"
className="border-border text-foreground hover:bg-secondary break-words inline-flex items-center justify-center rounded-md border px-2 py-1 text-center text-xs leading-tight font-medium whitespace-normal transition-colors"
aria-label={t('actions.editProduct', {
title: row.title,
})}
Expand Down
4 changes: 2 additions & 2 deletions frontend/app/[locale]/shop/cart/CartPageClient.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
'use client';

import { Minus, Plus, ShoppingBag,Trash2 } from 'lucide-react';
import { Minus, Plus, ShoppingBag, Trash2 } from 'lucide-react';
import Image from 'next/image';
import { useParams } from 'next/navigation';
import { useTranslations } from 'next-intl';
import { useState } from 'react';

import { useCart } from '@/components/shop/CartProvider';
import { Link,useRouter } from '@/i18n/routing';
import { Link, useRouter } from '@/i18n/routing';
import { formatMoney } from '@/lib/shop/currency';
import { generateIdempotencyKey } from '@/lib/shop/idempotency';
import {
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/[locale]/shop/orders/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { orderItems, orders } from '@/db/schema';
import { Link } from '@/i18n/routing';
import { getCurrentUser } from '@/lib/auth';
import { logError } from '@/lib/logging';
import { type CurrencyCode,formatMoney } from '@/lib/shop/currency';
import { type CurrencyCode, formatMoney } from '@/lib/shop/currency';
import { fromDbMoney } from '@/lib/shop/money';
import {
SHOP_FOCUS,
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/api/shop/admin/orders/[id]/refund/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import { logError, logWarn } from '@/lib/logging';
import { requireAdminCsrf } from '@/lib/security/admin-csrf';
import { guardBrowserSameOrigin } from '@/lib/security/origin';
import { InvalidPayloadError,OrderNotFoundError } from '@/lib/services/errors';
import { InvalidPayloadError, OrderNotFoundError } from '@/lib/services/errors';
import { refundOrder } from '@/lib/services/orders';
import { orderIdParamSchema, orderSummarySchema } from '@/lib/validation/shop';

Expand Down
4 changes: 2 additions & 2 deletions frontend/app/api/shop/webhooks/stripe/route.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import crypto from 'node:crypto';

import { and, eq, isNull, lt,ne, or } from 'drizzle-orm';
import { and, eq, isNull, lt, ne, or } from 'drizzle-orm';
import { NextRequest, NextResponse } from 'next/server';
import Stripe from 'stripe';

import { db } from '@/db';
import { orders, stripeEvents } from '@/db/schema';
import { logError, logInfo, logWarn } from '@/lib/logging';
import { retrieveCharge,verifyWebhookSignature } from '@/lib/psp/stripe';
import { retrieveCharge, verifyWebhookSignature } from '@/lib/psp/stripe';
import { guardNonBrowserOnly } from '@/lib/security/origin';
import {
enforceRateLimit,
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/shop/CatalogProductsClient.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { type ReadonlyURLSearchParams,useSearchParams } from 'next/navigation';
import { type ReadonlyURLSearchParams, useSearchParams } from 'next/navigation';
import { useTranslations } from 'next-intl';
import React from 'react';

Expand Down
2 changes: 1 addition & 1 deletion frontend/components/shop/header/NavLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export function NavLinks({
href="/"
onClick={onNavigate}
icon={Home}
className={cn(isHomeActive && '[color:var(--accent-primary)]')}
className={cn(isHomeActive && 'text-(--accent-primary)')}
>
{tNav('home')}
</HeaderButton>
Expand Down
4 changes: 2 additions & 2 deletions frontend/db/queries/shop/products.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import {
} from 'drizzle-orm';

import { db } from '@/db';
import { productPrices,products } from '@/db/schema';
import { productPrices, products } from '@/db/schema';
import type { CatalogSort } from '@/lib/config/catalog';
import type { CurrencyCode } from '@/lib/shop/currency';
import { type DbProduct,dbProductSchema } from '@/lib/validation/shop';
import { type DbProduct, dbProductSchema } from '@/lib/validation/shop';

const publicProductBaseSelect = {
id: products.id,
Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/admin/parseAdminProductForm.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from 'zod';

import { type CurrencyCode,currencyValues } from '@/lib/shop/currency';
import { type CurrencyCode, currencyValues } from '@/lib/shop/currency';
import { toCents } from '@/lib/shop/money';
import {
productAdminSchema,
Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/services/orders.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export { createOrderWithItems } from './orders/checkout';
export { setOrderPaymentIntent } from './orders/payment-intent';
export { refundOrder } from './orders/refund';
export { restockOrder as restock,restockOrder } from './orders/restock';
export { restockOrder as restock, restockOrder } from './orders/restock';
export { getOrderById, getOrderSummary } from './orders/summary';
export {
restockStaleNoPaymentOrders,
Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/services/orders/sweeps.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import crypto from 'crypto';
import { and, eq, inArray, isNull, lt, ne,or, sql } from 'drizzle-orm';
import { and, eq, inArray, isNull, lt, ne, or, sql } from 'drizzle-orm';

import { db } from '@/db';
import { orders } from '@/db/schema/shop';
Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/services/products/admin/queries.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { and, eq, type SQL } from 'drizzle-orm';

import { db } from '@/db';
import { productPrices,products } from '@/db/schema';
import { productPrices, products } from '@/db/schema';
import { ProductNotFoundError } from '@/lib/errors/products';
import type { CurrencyCode } from '@/lib/shop/currency';
import type { DbProduct } from '@/lib/types/shop';
Expand Down
4 changes: 2 additions & 2 deletions frontend/lib/services/products/cart/rehydrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { and, eq, inArray } from 'drizzle-orm';

import { db } from '@/db';
import { coercePriceFromDb } from '@/db/queries/shop/orders';
import { productPrices,products } from '@/db/schema';
import { productPrices, products } from '@/db/schema';
import { logWarn } from '@/lib/logging';
import { createCartItemKey } from '@/lib/shop/cart-item-key';
import { type CurrencyCode,isTwoDecimalCurrency } from '@/lib/shop/currency';
import { type CurrencyCode, isTwoDecimalCurrency } from '@/lib/shop/currency';
import { calculateLineTotal, fromCents, toCents } from '@/lib/shop/money';
import type {
CartClientItem,
Expand Down
4 changes: 2 additions & 2 deletions frontend/lib/services/products/mutations/create.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { eq } from 'drizzle-orm';

import { db } from '@/db';
import { productPrices,products } from '@/db/schema';
import { productPrices, products } from '@/db/schema';
import { uploadProductImageFromFile } from '@/lib/cloudinary';
import { logError } from '@/lib/logging';
import { toDbMoney } from '@/lib/shop/money';
import type { DbProduct, ProductInput } from '@/lib/types/shop';

import { InvalidPayloadError,SlugConflictError } from '../../errors';
import { InvalidPayloadError, SlugConflictError } from '../../errors';
import { mapRowToProduct } from '../mapping';
import {
enforceSaleBadgeRequiresOriginal,
Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/services/products/mutations/delete.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { sql } from 'drizzle-orm';

import { db } from '@/db';
import { productPrices,products } from '@/db/schema';
import { productPrices, products } from '@/db/schema';
import { destroyProductImage } from '@/lib/cloudinary';
import { ProductNotFoundError } from '@/lib/errors/products';
import { logError } from '@/lib/logging';
Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/services/products/mutations/update.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { eq, sql } from 'drizzle-orm';

import { db } from '@/db';
import { productPrices,products } from '@/db/schema';
import { productPrices, products } from '@/db/schema';
import {
destroyProductImage,
uploadProductImageFromFile,
Expand Down
2 changes: 0 additions & 2 deletions frontend/lib/tests/__mocks__/server-only.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
// Vitest/Node stub for Next.js "server-only" marker import.
// Next handles it at build time, but unit tests need a real module.
export {};
6 changes: 0 additions & 6 deletions frontend/lib/tests/helpers/ip.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
/**
* Derive a deterministic TEST IP from an idempotency key.
* Used only in tests to make rate-limit keys stable per request.
*
* Produces a TEST-NET-3 IPv4 address: 203.0.113.1..250
*/
export function deriveTestIpFromIdemKey(idemKey: string): string {
const hex = idemKey.replace(/[^0-9a-f]/gi, '').slice(0, 2);
const n = hex ? (parseInt(hex, 16) % 250) + 1 : 1;
Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/tests/shop/admin-api-killswitch.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NextRequest } from 'next/server';
import { afterEach,beforeEach, describe, expect, it } from 'vitest';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { vi } from 'vitest';

const BASE_URL = 'http://localhost';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NextRequest } from 'next/server';
import { afterEach,beforeEach, describe, expect, it, vi } from 'vitest';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

const { getCurrentUserMock, parseAdminProductFormMock } = vi.hoisted(() => ({
getCurrentUserMock: vi.fn(async () => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { eq } from 'drizzle-orm';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

import { db } from '@/db';
import { productPrices,products } from '@/db/schema';
import { productPrices, products } from '@/db/schema';
import { rehydrateCartItems } from '@/lib/services/products';

let productId: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,6 @@ describe('P0-8.10.1 checkout concurrency: stock=1, two parallel checkouts', () =
// In CI we fail fast so flakes are visible.
if (process.env.CI) throw err;


console.warn('checkout concurrency cleanup failed', err);
}
}, 30000);
Expand Down
20 changes: 13 additions & 7 deletions frontend/lib/tests/shop/checkout-currency-policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,31 @@ vi.mock('@/lib/env/stripe', () => ({
isPaymentsEnabled: () => false,
}));

const createPaymentIntentMock = vi.fn((..._args: any[]) => {
const createPaymentIntentMock = vi.fn((...args: any[]) => {
void args;
throw new Error(
'Stripe should not be called in this test (payments disabled).'
);
});

vi.mock('@/lib/psp/stripe', () => ({
createPaymentIntent: (...args: any[]) => createPaymentIntentMock(...args),
retrievePaymentIntent: (..._args: any[]) => {
retrievePaymentIntent: (...args: any[]) => {
void args;
throw new Error(
'Stripe should not be called in this test (payments disabled).'
);
},
}));

// checkout-currency-policy.test.ts

const logErrorMock = vi.fn((..._args: any[]) => undefined);
const logWarnMock = vi.fn((..._args: any[]) => undefined);
const logErrorMock = vi.fn((...args: any[]) => {
void args;
return undefined;
});
const logWarnMock = vi.fn((...args: any[]) => {
void args;
return undefined;
});

vi.mock('@/lib/logging', async () => {
const actual =
Expand All @@ -51,7 +57,7 @@ vi.mock('@/lib/logging', async () => {
});

import { db } from '@/db';
import { orders,productPrices, products } from '@/db/schema';
import { orders, productPrices, products } from '@/db/schema';

let POST: (req: NextRequest) => Promise<Response>;

Expand Down
4 changes: 2 additions & 2 deletions frontend/lib/tests/shop/checkout-no-payments.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
import crypto from 'crypto';
import { eq, sql } from 'drizzle-orm';
import { NextRequest } from 'next/server';
import { afterAll,beforeAll, describe, expect, it, vi } from 'vitest';
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';

import { db } from '@/db';
import { orders, productPrices,products } from '@/db/schema';
import { orders, productPrices, products } from '@/db/schema';
import { toDbMoney } from '@/lib/shop/money';
import { deriveTestIpFromIdemKey } from '@/lib/tests/helpers/ip';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect,it } from 'vitest';
import { describe, expect, it } from 'vitest';

import { POST } from '@/app/api/shop/checkout/route';
import { makeCheckoutReq } from '@/lib/tests/helpers/makeCheckoutReq';
Expand Down
4 changes: 2 additions & 2 deletions frontend/lib/tests/shop/order-items-variants.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import crypto from 'crypto';
import { eq, sql } from 'drizzle-orm';
import { describe, expect,it } from 'vitest';
import { describe, expect, it } from 'vitest';

import { db } from '@/db';
import { orderItems,orders, productPrices, products } from '@/db/schema/shop';
import { orderItems, orders, productPrices, products } from '@/db/schema/shop';
import { createOrderWithItems } from '@/lib/services/orders';

describe('order_items variants (selected_size/selected_color)', () => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/tests/shop/orders-access.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NextRequest } from 'next/server';
import { beforeEach,describe, expect, it, vi } from 'vitest';
import { beforeEach, describe, expect, it, vi } from 'vitest';

vi.mock('@/lib/auth', () => ({
getCurrentUser: vi.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fs from 'node:fs';
import path from 'node:path';

import { eq } from 'drizzle-orm';
import { afterEach,describe, expect, it } from 'vitest';
import { afterEach, describe, expect, it } from 'vitest';

import { db } from '@/db';
import { orders } from '@/db/schema/shop';
Expand All @@ -17,7 +17,6 @@ type SeedArgs = {
async function seedOrder(args: SeedArgs): Promise<string> {
const orderId = crypto.randomUUID();
const now = new Date();

const idempotencyKey = `test:${orderId}`;

await db.insert(orders).values({
Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/tests/shop/payment-status-tripwire.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from 'node:fs';
import path from 'node:path';

import { describe, expect,it } from 'vitest';
import { describe, expect, it } from 'vitest';

const REPO_ROOT = process.cwd();

Expand Down
4 changes: 2 additions & 2 deletions frontend/lib/tests/shop/product-sale-invariant.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { eq } from 'drizzle-orm';
import { afterEach,describe, expect, it, vi } from 'vitest';
import { afterEach, describe, expect, it, vi } from 'vitest';

vi.mock('@/lib/cloudinary', () => {
return {
Expand All @@ -12,7 +12,7 @@ vi.mock('@/lib/cloudinary', () => {
});

import { db } from '@/db';
import { productPrices,products } from '@/db/schema';
import { productPrices, products } from '@/db/schema';
import { createProduct, updateProduct } from '@/lib/services/products';
import { toDbMoney } from '@/lib/shop/money';

Expand Down
4 changes: 2 additions & 2 deletions frontend/lib/tests/shop/public-product-visibility.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { randomUUID } from 'node:crypto';

import { eq } from 'drizzle-orm';
import { describe, expect,it } from 'vitest';
import { describe, expect, it } from 'vitest';

import { db } from '@/db';
import { getPublicProductBySlug } from '@/db/queries/shop/products';
import { productPrices,products } from '@/db/schema';
import { productPrices, products } from '@/db/schema';

function logTestCleanupFailed(meta: Record<string, unknown>, error: unknown) {
console.error('[test cleanup failed]', {
Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/tests/shop/rate-limit-subject.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { afterEach,describe, expect, it, vi } from 'vitest';
import { afterEach, describe, expect, it, vi } from 'vitest';
vi.mock('@/db', () => ({ db: { execute: vi.fn() } }));

import { NextRequest } from 'next/server';
Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/tests/shop/restock-order-only-once.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import crypto from 'crypto';
import { eq, sql } from 'drizzle-orm';
import { describe, expect,it } from 'vitest';
import { describe, expect, it } from 'vitest';

import { db } from '@/db';
import { orders, products } from '@/db/schema';
Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/tests/shop/restock-stale-claim-gate.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import crypto from 'crypto';
import { eq } from 'drizzle-orm';
import { describe, expect,it } from 'vitest';
import { describe, expect, it } from 'vitest';

import { db } from '@/db';
import { orders } from '@/db/schema';
Expand Down
Loading