test: add Vitest infrastructure (config, setup, mocks, fixtures)

Adds test harness for the suite:
- vitest.config.ts: happy-dom env, @/* alias, v8 coverage with 70% thresholds
- setup.ts: env vars, global next/headers and next/navigation mocks
- tsconfig.json: IDE alias resolution for test/
- __mocks__/prisma.ts: centralised Prisma mock auto-registered via vi.mock
- fixtures/users.ts, fixtures/orders.ts: typed test data
This commit is contained in:
2026-05-19 14:07:18 +02:00
parent ed7faa3be5
commit b93f5d5bdf
6 changed files with 243 additions and 0 deletions
+81
View File
@@ -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 }))
+53
View File
@@ -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',
},
},
}
+27
View File
@@ -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,
}
+31
View File
@@ -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(),
}))
+15
View File
@@ -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"]
}
+36
View File
@@ -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',
},
})