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,56 @@
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
||||
import { createTestContext } from '../test/helpers.ts'
|
||||
|
||||
let ctx: Awaited<ReturnType<typeof createTestContext>>
|
||||
beforeAll(async () => {
|
||||
ctx = await createTestContext()
|
||||
})
|
||||
afterAll(() => ctx.close())
|
||||
|
||||
describe('users', () => {
|
||||
it('lets admins create users and rejects duplicate emails', async () => {
|
||||
const body = { name: 'Sam Rivera', email: 'Sam@Example.com', role: 'supervisor', password: 'a sufficiently long password' }
|
||||
const created = await ctx.admin.post('/api/users', body)
|
||||
expect(created.status).toBe(201)
|
||||
expect(created.body.user).toMatchObject({ email: 'sam@example.com', role: 'supervisor', active: true })
|
||||
expect(created.body.user.passwordHash).toBeUndefined()
|
||||
|
||||
expect((await ctx.admin.post('/api/users', body)).status).toBe(409)
|
||||
})
|
||||
|
||||
it('validates input', async () => {
|
||||
const res = await ctx.admin.post('/api/users', { name: '', email: 'not-an-email', role: 'boss', password: 'short' })
|
||||
expect(res.status).toBe(400)
|
||||
expect(res.body.issues.map((issue: { path: string }) => issue.path).sort()).toEqual(['email', 'name', 'password', 'role'])
|
||||
})
|
||||
|
||||
it('restricts listing to safety managers and creating to admins', async () => {
|
||||
const supervisor = await ctx.createUser('supervisor')
|
||||
const manager = await ctx.createUser('safety_manager')
|
||||
expect((await supervisor.get('/api/users')).status).toBe(403)
|
||||
expect((await manager.get('/api/users')).status).toBe(200)
|
||||
expect((await manager.post('/api/users', { name: 'X', email: 'x@example.com', role: 'admin', password: 'a sufficiently long password' })).status).toBe(403)
|
||||
expect((await ctx.anonymous().get('/api/users')).status).toBe(401)
|
||||
})
|
||||
|
||||
it('returns 404 for unknown or malformed ids', async () => {
|
||||
expect((await ctx.admin.patch('/api/users/00000000-0000-4000-8000-000000000000', { name: 'Nobody' })).status).toBe(404)
|
||||
expect((await ctx.admin.patch('/api/users/not-a-uuid', { name: 'Nobody' })).status).toBe(400)
|
||||
})
|
||||
|
||||
it('keeps at least one active admin', async () => {
|
||||
const me = await ctx.admin.get('/api/auth/me')
|
||||
const adminId = me.body.user.id
|
||||
expect((await ctx.admin.patch(`/api/users/${adminId}`, { role: 'reporter' })).status).toBe(409)
|
||||
expect((await ctx.admin.patch(`/api/users/${adminId}`, { active: false })).status).toBe(400)
|
||||
|
||||
const second = await ctx.createUser('admin', 'second-admin@example.com')
|
||||
const demoted = await second.patch(`/api/users/${adminId}`, { role: 'safety_manager' })
|
||||
expect(demoted.status).toBe(200)
|
||||
expect(demoted.body.user.role).toBe('safety_manager')
|
||||
// The demoted user stays signed in but loses admin rights immediately
|
||||
expect((await ctx.admin.get('/api/auth/me')).status).toBe(200)
|
||||
expect((await ctx.admin.post('/api/users', { name: 'Y', email: 'y@example.com', role: 'reporter', password: 'a sufficiently long password' })).status).toBe(403)
|
||||
})
|
||||
|
||||
})
|
||||
Reference in new issue
Block a user