feat: implement incident reporting and management system
- Add incident reporting routes and logic to handle creation, updating, and retrieval of incidents. - Implement visibility rules for users based on roles (reporters, supervisors). - Create tests for incident reporting functionality to ensure correctness and reliability. - Introduce site and area management routes, including validation for area hierarchy. - Establish user management routes with role-based access control for creating and updating users. - Add setup routes for initializing the application with an organization and admin user. - Implement shared enums and schemas for consistent validation and data handling across the application. - Create a test helper for setting up an in-memory database and API client for testing. - Add TypeScript configuration for server-side code with strict type checking and module resolution.
This commit is contained in:
1 parent
a382fa904a
commit
c69eccb5c1
47 files changed
+6297
-45
No files matched your search
@@ -0,0 +1,61 @@
|
||||
import { createApp } from '../app.ts'
|
||||
import { loadConfig } from '../config.ts'
|
||||
import { openDatabase } from '../db/client.ts'
|
||||
|
||||
type App = ReturnType<typeof createApp>
|
||||
|
||||
/** Minimal API client that keeps its session cookie between requests, like a browser. */
|
||||
export class Client {
|
||||
cookie: string | undefined
|
||||
private readonly app: App
|
||||
|
||||
constructor(app: App) {
|
||||
this.app = app
|
||||
}
|
||||
|
||||
async request(method: string, path: string, body?: unknown) {
|
||||
const headers: Record<string, string> = {}
|
||||
if (body !== undefined) headers['content-type'] = 'application/json'
|
||||
if (this.cookie) headers.cookie = this.cookie
|
||||
const res = await this.app.request(path, { method, headers, body: body === undefined ? undefined : JSON.stringify(body) })
|
||||
const setCookie = res.headers.get('set-cookie')
|
||||
if (setCookie) this.cookie = /max-age=0|expires=thu, 01 jan 1970/i.test(setCookie) ? undefined : setCookie.split(';')[0]
|
||||
const text = await res.text()
|
||||
// oxlint-disable-next-line typescript/no-explicit-any -- tests read arbitrary JSON shapes
|
||||
const json: any = text ? JSON.parse(text) : null
|
||||
return { status: res.status, body: json, headers: res.headers }
|
||||
}
|
||||
|
||||
get = (path: string) => this.request('GET', path)
|
||||
post = (path: string, body?: unknown) => this.request('POST', path, body ?? {})
|
||||
patch = (path: string, body: unknown) => this.request('PATCH', path, body)
|
||||
}
|
||||
|
||||
export const ADMIN = { organizationName: 'Northstar Co.', name: 'Avery Admin', email: 'admin@example.com', password: 'correct horse battery' }
|
||||
|
||||
/** Fresh in-memory database and app, with the first-run setup already done and the admin signed in. */
|
||||
export async function createTestContext() {
|
||||
const database = await openDatabase('memory://')
|
||||
const app = createApp({ db: database.db, config: { ...loadConfig({}), databaseUrl: 'memory://' } })
|
||||
const admin = new Client(app)
|
||||
const setup = await admin.post('/api/setup', ADMIN)
|
||||
if (setup.status !== 201) throw new Error(`setup failed: ${JSON.stringify(setup.body)}`)
|
||||
|
||||
async function createUser(role: 'reporter' | 'supervisor' | 'safety_manager' | 'admin', email = `${role}@example.com`) {
|
||||
const password = 'a sufficiently long password'
|
||||
const created = await admin.post('/api/users', { name: `${role} user`, email, role, password })
|
||||
if (created.status !== 201) throw new Error(`createUser failed: ${JSON.stringify(created.body)}`)
|
||||
const client = new Client(app)
|
||||
await client.post('/api/auth/login', { email, password })
|
||||
return Object.assign(client, { user: created.body.user as { id: string; email: string } })
|
||||
}
|
||||
|
||||
async function createSite(name = 'North Campus') {
|
||||
const res = await admin.post('/api/sites', { name })
|
||||
return res.body.site as { id: string; name: string }
|
||||
}
|
||||
|
||||
return { app, db: database.db, admin, anonymous: () => new Client(app), createUser, createSite, close: database.close }
|
||||
}
|
||||
|
||||
export const hoursAgo = (hours: number) => new Date(Date.now() - hours * 3_600_000).toISOString()
|
||||
Reference in new issue
Block a user