Add user auth with JWT, switch to bun test
- User table with company_id FK, unique email, role enum - Register/login routes with bcrypt + JWT token generation - Auth plugin with authenticate decorator and role guards - Login uses globally unique email (no company header needed) - Dev-auth plugin kept as fallback when JWT_SECRET not set - Switched from vitest to bun:test (vitest had ESM resolution issues with zod in Bun's module structure) - Upgraded to zod 4 - Added Dockerfile.dev and API service to docker-compose - 8 tests passing (health + auth)
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import { buildApp } from '../main.js'
|
||||
import { sql } from 'drizzle-orm'
|
||||
import { companies, locations } from '../db/schema/stores.js'
|
||||
|
||||
export const TEST_COMPANY_ID = '00000000-0000-0000-0000-000000000099'
|
||||
export const TEST_LOCATION_ID = '00000000-0000-0000-0000-000000000099'
|
||||
|
||||
/**
|
||||
* Build a fresh Fastify app instance for testing.
|
||||
* Each test gets its own app — no shared state.
|
||||
*/
|
||||
export async function createTestApp(): Promise<FastifyInstance> {
|
||||
const app = await buildApp()
|
||||
@@ -14,7 +17,6 @@ export async function createTestApp(): Promise<FastifyInstance> {
|
||||
|
||||
/**
|
||||
* Truncate all tables in the test database.
|
||||
* Call this in beforeEach to guarantee a clean slate per test.
|
||||
*/
|
||||
export async function cleanDb(app: FastifyInstance): Promise<void> {
|
||||
await app.db.execute(sql`
|
||||
@@ -27,3 +29,49 @@ export async function cleanDb(app: FastifyInstance): Promise<void> {
|
||||
END $$
|
||||
`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Seed a test company and location. Call after cleanDb.
|
||||
*/
|
||||
export async function seedTestCompany(app: FastifyInstance): Promise<void> {
|
||||
await app.db.insert(companies).values({
|
||||
id: TEST_COMPANY_ID,
|
||||
name: 'Test Music Co.',
|
||||
timezone: 'America/Chicago',
|
||||
})
|
||||
await app.db.insert(locations).values({
|
||||
id: TEST_LOCATION_ID,
|
||||
companyId: TEST_COMPANY_ID,
|
||||
name: 'Test Location',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a user and return the JWT token.
|
||||
*/
|
||||
export async function registerAndLogin(
|
||||
app: FastifyInstance,
|
||||
overrides: {
|
||||
email?: string
|
||||
password?: string
|
||||
firstName?: string
|
||||
lastName?: string
|
||||
role?: string
|
||||
} = {},
|
||||
): Promise<{ token: string; user: Record<string, unknown> }> {
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/v1/auth/register',
|
||||
headers: { 'x-company-id': TEST_COMPANY_ID },
|
||||
payload: {
|
||||
email: overrides.email ?? 'test@forte.dev',
|
||||
password: overrides.password ?? 'testpassword123',
|
||||
firstName: overrides.firstName ?? 'Test',
|
||||
lastName: overrides.lastName ?? 'User',
|
||||
role: overrides.role ?? 'admin',
|
||||
},
|
||||
})
|
||||
|
||||
const body = response.json()
|
||||
return { token: body.token, user: body.user }
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ const TEST_DB_URL =
|
||||
process.env.DATABASE_URL = TEST_DB_URL
|
||||
process.env.NODE_ENV = 'test'
|
||||
process.env.LOG_LEVEL = 'silent'
|
||||
process.env.JWT_SECRET = 'test-secret-for-jwt-signing'
|
||||
|
||||
/**
|
||||
* Ensure the forte_test database exists before tests run.
|
||||
|
||||
Reference in New Issue
Block a user