diff --git a/test/__mocks__/prisma.ts b/test/__mocks__/prisma.ts new file mode 100644 index 0000000..4dc86fa --- /dev/null +++ b/test/__mocks__/prisma.ts @@ -0,0 +1,81 @@ +import { vi } from 'vitest' + +export const prisma = { + user: { + findUnique: vi.fn(), + findMany: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + count: vi.fn(), + }, + session: { + create: vi.fn(), + findUnique: vi.fn(), + delete: vi.fn(), + deleteMany: vi.fn(), + }, + loginAttempt: { + count: vi.fn(), + create: vi.fn(), + deleteMany: vi.fn(), + }, + order: { + create: vi.fn(), + update: vi.fn(), + findUnique: vi.fn(), + findMany: vi.fn(), + count: vi.fn(), + }, + orderItem: { + create: vi.fn(), + findMany: vi.fn(), + }, + payment: { + create: vi.fn(), + update: vi.fn(), + updateMany: vi.fn(), + findFirst: vi.fn(), + findMany: vi.fn(), + }, + product: { + findUnique: vi.fn(), + findMany: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + count: vi.fn(), + }, + productType: { + findUnique: vi.fn(), + findMany: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + }, + category: { + findUnique: vi.fn(), + findMany: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + }, + review: { + findMany: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + count: vi.fn(), + }, + auditLog: { + create: vi.fn(), + findMany: vi.fn(), + }, + siteSettings: { + findMany: vi.fn(), + upsert: vi.fn(), + }, + $transaction: vi.fn((fn: (tx: unknown) => unknown) => fn(prisma)), +} + +vi.mock('@/lib/prisma', () => ({ prisma })) diff --git a/test/fixtures/orders.ts b/test/fixtures/orders.ts new file mode 100644 index 0000000..a15fd65 --- /dev/null +++ b/test/fixtures/orders.ts @@ -0,0 +1,53 @@ +import { mockUser } from './users' + +export const mockOrder = { + id: 'order-1', + userId: mockUser.id, + status: 'PENDING' as const, + grandTotal: 2999, + currency: 'EUR', + createdAt: new Date('2024-01-01'), + updatedAt: new Date('2024-01-01'), + user: mockUser, +} + +export const mockPayment = { + id: 'payment-1', + orderId: mockOrder.id, + provider: 'stripe', + providerPaymentId: 'pi_test_123', + status: 'pending', + amount: 2999, + currency: 'EUR', + rawPayload: {}, + createdAt: new Date('2024-01-01'), + updatedAt: new Date('2024-01-01'), +} + +export const mockStripeCheckoutEvent = { + type: 'checkout.session.completed', + data: { + object: { + metadata: { orderId: mockOrder.id }, + payment_intent: 'pi_test_123', + }, + }, +} + +export const mockStripePaymentSucceededEvent = { + type: 'payment_intent.succeeded', + data: { + object: { + id: 'pi_test_123', + }, + }, +} + +export const mockStripePaymentFailedEvent = { + type: 'payment_intent.payment_failed', + data: { + object: { + id: 'pi_test_123', + }, + }, +} diff --git a/test/fixtures/users.ts b/test/fixtures/users.ts new file mode 100644 index 0000000..a17aa27 --- /dev/null +++ b/test/fixtures/users.ts @@ -0,0 +1,27 @@ +export const mockUser = { + id: 'user-1', + email: 'test@example.com', + name: 'Test User', + passwordHash: '$2a$12$hashedpassword', + role: 'CUSTOMER' as const, + mustChangePassword: false, + createdAt: new Date('2024-01-01'), + updatedAt: new Date('2024-01-01'), +} + +export const mockAdmin = { + ...mockUser, + id: 'admin-1', + email: 'admin@example.com', + name: 'Admin User', + role: 'ADMIN' as const, +} + +export const mockSession = { + id: 'session-1', + userId: mockUser.id, + tokenHash: 'abc123hash', + expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), + createdAt: new Date('2024-01-01'), + user: mockUser, +} diff --git a/test/setup.ts b/test/setup.ts new file mode 100644 index 0000000..3b802d3 --- /dev/null +++ b/test/setup.ts @@ -0,0 +1,31 @@ +import '@testing-library/jest-dom' +import { vi } from 'vitest' + +// Env vars necessarie per i test +process.env.STRIPE_SECRET_KEY = 'sk_test_mock' +process.env.STRIPE_WEBHOOK_SECRET = 'whsec_test_mock' +process.env.SMTP_HOST = 'localhost' +process.env.SMTP_PORT = '1025' +process.env.APP_URL = 'http://localhost' +process.env.DATABASE_URL = 'postgresql://test:test@localhost:5432/test' +process.env.NODE_ENV = 'test' +process.env.AUTH_SECRET = 'test-secret-32-chars-minimum-ok!' + +// Mock di next/headers (cookies() lancia fuori dal runtime Next.js) +vi.mock('next/headers', () => { + const mockCookieStore = { + get: vi.fn(), + set: vi.fn(), + delete: vi.fn(), + } + return { + cookies: vi.fn(() => mockCookieStore), + } +}) + +// Mock di next/navigation +vi.mock('next/navigation', () => ({ + useRouter: vi.fn(() => ({ push: vi.fn(), replace: vi.fn() })), + usePathname: vi.fn(() => '/'), + redirect: vi.fn(), +})) diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 0000000..91a3352 --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../app/tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["../app/src/*"] + }, + "types": ["vitest/globals", "@testing-library/jest-dom"] + }, + "include": [ + "./**/*.ts", + "./**/*.tsx" + ], + "exclude": ["node_modules"] +} diff --git a/test/vitest.config.ts b/test/vitest.config.ts new file mode 100644 index 0000000..c0f7d1d --- /dev/null +++ b/test/vitest.config.ts @@ -0,0 +1,36 @@ +import { defineConfig } from 'vitest/config' +import path from 'path' + +export default defineConfig({ + test: { + environment: 'happy-dom', + globals: true, + setupFiles: [path.resolve(__dirname, 'setup.ts')], + include: [ + '../test/unit/**/*.{test,spec}.{ts,tsx}', + '../test/integration/**/*.{test,spec}.{ts,tsx}', + '../test/components/**/*.{test,spec}.{ts,tsx}', + ], + exclude: ['**/node_modules/**'], + coverage: { + provider: 'v8', + reporter: ['text', 'html', 'lcov'], + include: ['src/**/*.ts', 'src/**/*.tsx'], + exclude: [ + 'src/app/layout.tsx', + 'src/lib/prisma.ts', + 'src/**/*.d.ts', + ], + thresholds: { lines: 70, functions: 70 }, + }, + }, + resolve: { + alias: { + '@': path.resolve(__dirname, '../app/src'), + }, + }, + esbuild: { + jsx: 'automatic', + jsxImportSource: 'react', + }, +})