f2b9239d82
Adds the initial testing baseline for the project: Unit coverage: - Zod schemas for items, assignments, movements, categories, auth, recipients, users, and assets - password hashing and verification helpers - auth role helper functions Integration coverage with PostgreSQL Testcontainers: - item use-cases: create, duplicate names, delete constraints - assignment use-cases: create, insufficient stock, return, double return - asset use-cases: available/assigned creation and lifecycle transitions - user use-cases: create/update, uniqueness, admin safeguards, password reset - category use-cases: create/update/delete constraints - recipient use-cases: create/update and uniqueness constraints E2E smoke coverage with Playwright: - unauthenticated redirect to login - seeded admin login - dashboard load - admin users page - inventory items page - assignments page Also configures: - Vitest - Playwright - PostgreSQL Testcontainers helpers - deterministic E2E admin bootstrap - test artifact ignores Validation: - bun run test: 9 files / 37 tests passed - bun run test:e2e: 3 passed - bunx tsc --noEmit: passed - bunx prisma validate: passed
73 lines
1.7 KiB
TypeScript
73 lines
1.7 KiB
TypeScript
import { execFileSync } from "node:child_process"
|
|
import {
|
|
PostgreSqlContainer,
|
|
type StartedPostgreSqlContainer,
|
|
} from "@testcontainers/postgresql"
|
|
import type { PrismaClient } from "@/generated/prisma/client"
|
|
|
|
type TestDatabaseState = {
|
|
container?: StartedPostgreSqlContainer
|
|
databaseUrl?: string
|
|
}
|
|
|
|
const state: TestDatabaseState = {}
|
|
|
|
const TABLES_TO_TRUNCATE = [
|
|
"Movement",
|
|
"Assignment",
|
|
"Asset",
|
|
"Item",
|
|
"Category",
|
|
"Recipient",
|
|
"User",
|
|
]
|
|
|
|
export async function startIntegrationTestDatabase() {
|
|
if (state.container && state.databaseUrl) {
|
|
return state.databaseUrl
|
|
}
|
|
|
|
const container = await new PostgreSqlContainer("postgres:18-alpine")
|
|
.withDatabase("stock_manager_test")
|
|
.withUsername("test")
|
|
.withPassword("test")
|
|
.start()
|
|
|
|
const databaseUrl = container.getConnectionUri()
|
|
|
|
state.container = container
|
|
state.databaseUrl = databaseUrl
|
|
|
|
process.env.DATABASE_URL = databaseUrl
|
|
process.env.AUTH_SECRET = process.env.AUTH_SECRET || "test-auth-secret"
|
|
|
|
try {
|
|
execFileSync("bunx", ["prisma", "migrate", "deploy"], {
|
|
env: {
|
|
...process.env,
|
|
DATABASE_URL: databaseUrl,
|
|
},
|
|
stdio: "inherit",
|
|
})
|
|
} catch (error) {
|
|
await stopIntegrationTestDatabase()
|
|
throw error
|
|
}
|
|
|
|
return databaseUrl
|
|
}
|
|
|
|
export async function resetIntegrationTestDatabase(prisma: PrismaClient) {
|
|
const quotedTables = TABLES_TO_TRUNCATE.map((table) => `"${table}"`).join(
|
|
", ",
|
|
)
|
|
|
|
await prisma.$executeRawUnsafe(`TRUNCATE TABLE ${quotedTables} CASCADE`)
|
|
}
|
|
|
|
export async function stopIntegrationTestDatabase() {
|
|
await state.container?.stop()
|
|
state.container = undefined
|
|
state.databaseUrl = undefined
|
|
}
|