test: add initial unit integration and e2e coverage

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
This commit is contained in:
2026-06-07 04:14:01 +02:00
parent cb01f4f8ef
commit f2b9239d82
18 changed files with 2372 additions and 9 deletions
+227
View File
@@ -0,0 +1,227 @@
import { describe, expect, it } from "vitest"
import { createAssetSchema, updateAssetSchema } from "@/schemas/asset.schema"
import { createAssignmentSchema } from "@/schemas/assignment.schema"
import { signInSchema } from "@/schemas/auth.schema"
import { createCategorySchema } from "@/schemas/category.schema"
import { createItemSchema, updateItemSchema } from "@/schemas/item.schema"
import { createMovementSchema } from "@/schemas/movement.schema"
import { createRecipientSchema } from "@/schemas/recipient.schema"
import { createUserSchema, updateUserSchema } from "@/schemas/user.schema"
describe("core schemas", () => {
it("coerces valid numeric fields", () => {
expect(
createItemSchema.safeParse({
name: "Laptop",
categoryId: "category-id",
stock: "3",
}),
).toMatchObject({ success: true, data: { stock: 3 } })
expect(
createAssignmentSchema.safeParse({
itemId: "item-id",
recipientId: "recipient-id",
quantity: "2",
}),
).toMatchObject({ success: true, data: { quantity: 2 } })
expect(
createMovementSchema.safeParse({
type: "IN",
quantity: "1",
itemId: "item-id",
}),
).toMatchObject({ success: true, data: { quantity: 1 } })
})
it("rejects invalid numeric fields", () => {
expect(
createItemSchema.safeParse({
name: "Laptop",
categoryId: "category-id",
stock: -1,
}).success,
).toBe(false)
expect(
createAssignmentSchema.safeParse({
itemId: "item-id",
recipientId: "recipient-id",
quantity: 0,
}).success,
).toBe(false)
expect(
createMovementSchema.safeParse({
type: "IN",
quantity: 0,
itemId: "item-id",
}).success,
).toBe(false)
})
it("validates required identifiers for update schemas", () => {
expect(
updateItemSchema.safeParse({
id: "item-id",
name: "Laptop",
categoryId: "category-id",
stock: 1,
}).success,
).toBe(true)
expect(
updateItemSchema.safeParse({
id: "",
name: "Laptop",
categoryId: "category-id",
stock: 1,
}).success,
).toBe(false)
expect(
updateUserSchema.safeParse({
id: "user-id",
username: "user",
name: "User",
email: "user@example.test",
role: "ADMIN",
isActive: true,
}).success,
).toBe(true)
expect(
updateUserSchema.safeParse({
id: "",
username: "user",
name: "User",
email: "user@example.test",
role: "ADMIN",
isActive: true,
}).success,
).toBe(false)
})
it("validates category and auth requirements", () => {
expect(createCategorySchema.safeParse({ name: "IT" }).success).toBe(false)
expect(createCategorySchema.safeParse({ name: "Hardware" }).success).toBe(
true,
)
expect(
signInSchema.safeParse({ username: "admin", password: "abc" }).success,
).toBe(true)
expect(
signInSchema.safeParse({ username: "", password: "abc" }).success,
).toBe(false)
expect(
signInSchema.safeParse({ username: "admin", password: "ab" }).success,
).toBe(false)
})
it("validates recipient email only when present", () => {
expect(
createRecipientSchema.safeParse({
username: "recipient",
firstName: "Rec",
lastName: "Ipient",
department: "IT",
email: "recipient@example.test",
}).success,
).toBe(true)
expect(
createRecipientSchema.safeParse({
username: "recipient",
firstName: "Rec",
lastName: "Ipient",
department: "IT",
email: "not-an-email",
}).success,
).toBe(false)
expect(
createRecipientSchema.safeParse({
username: "recipient",
firstName: "Rec",
lastName: "Ipient",
department: "IT",
email: "",
}).success,
).toBe(true)
})
it("validates user password, email, and role", () => {
expect(
createUserSchema.safeParse({
username: "user",
name: "User",
email: "user@example.test",
password: "password1",
role: "STAFF",
isActive: true,
}).success,
).toBe(true)
expect(
createUserSchema.safeParse({
username: "user",
name: "User",
email: "bad-email",
password: "password1",
role: "STAFF",
isActive: true,
}).success,
).toBe(false)
expect(
createUserSchema.safeParse({
username: "user",
name: "User",
email: "user@example.test",
password: "short",
role: "STAFF",
isActive: true,
}).success,
).toBe(false)
expect(
createUserSchema.safeParse({
username: "user",
name: "User",
email: "user@example.test",
password: "password1",
role: "OWNER",
isActive: true,
}).success,
).toBe(false)
})
it("validates asset status options", () => {
expect(
createAssetSchema.safeParse({
itemId: "item-id",
serialNumber: "SERIAL-001",
status: "AVAILABLE",
}).success,
).toBe(true)
expect(
createAssetSchema.safeParse({
itemId: "item-id",
serialNumber: "SERIAL-001",
status: "BROKEN",
}).success,
).toBe(false)
expect(
updateAssetSchema.safeParse({
id: "asset-id",
itemId: "item-id",
serialNumber: "SERIAL-001",
status: "BROKEN",
}).success,
).toBe(true)
})
})