feat(pdf): add incident report PDF API route
GET /api/incidents/[id]/report-pdf — hse/admin only, closed incidents only. Joins incidents + sites + zones + users + investigations + capaActions + addenda, then delegates to buildIncidentReportPdf() and returns application/pdf download. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HEYxFQiCyxJvnBCoZeYzB9
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
// Mock auth
|
||||
vi.mock('@/lib/auth/get-session', () => ({
|
||||
getSession: vi.fn(),
|
||||
}))
|
||||
|
||||
// Mock db
|
||||
vi.mock('@/lib/db/with-user', () => ({
|
||||
withUser: vi.fn(),
|
||||
}))
|
||||
|
||||
// Mock pdf builder
|
||||
vi.mock('@/lib/pdf/incident-report', () => ({
|
||||
buildIncidentReportPdf: vi.fn().mockResolvedValue(new Uint8Array([37, 80, 68, 70])), // %PDF
|
||||
}))
|
||||
|
||||
import { GET } from '@/app/api/incidents/[id]/report-pdf/route'
|
||||
import { getSession } from '@/lib/auth/get-session'
|
||||
import { withUser } from '@/lib/db/with-user'
|
||||
import { buildIncidentReportPdf } from '@/lib/pdf/incident-report'
|
||||
import { NextRequest } from 'next/server'
|
||||
|
||||
const mockSession = { sub: 'user-1', role: 'hse' as const }
|
||||
|
||||
const MOCK_INCIDENT = {
|
||||
id: 'inc-1',
|
||||
referenceNo: 'SC1-202607-0001',
|
||||
incidentType: 'injury',
|
||||
reportedAt: new Date('2026-07-01T08:00:00Z'),
|
||||
status: 'closed',
|
||||
siteName: 'Warehouse KL',
|
||||
zoneName: 'Bay A',
|
||||
reporterName: 'Ahmad',
|
||||
severity: 3,
|
||||
medicalStatus: 'first_aid',
|
||||
description: 'Slip and fall',
|
||||
injuryInvolved: true,
|
||||
assetInvolved: false,
|
||||
isFatality: false,
|
||||
isSeriousBodilyInjury: false,
|
||||
isDangerousOccurrence: false,
|
||||
isOccupationalDisease: false,
|
||||
lostDays: null,
|
||||
triagedBy: 'Supervisor',
|
||||
triagedAt: new Date('2026-07-01T10:00:00Z'),
|
||||
triageNotes: 'Reviewed',
|
||||
closedAt: new Date('2026-07-20T15:00:00Z'),
|
||||
}
|
||||
|
||||
function makeRequest(id: string) {
|
||||
return new NextRequest(`http://localhost/api/incidents/${id}/report-pdf`)
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('GET /api/incidents/[id]/report-pdf', () => {
|
||||
it('returns 401 when not authenticated', async () => {
|
||||
vi.mocked(getSession).mockResolvedValue(null)
|
||||
const res = await GET(makeRequest('inc-1'), { params: Promise.resolve({ id: 'inc-1' }) })
|
||||
expect(res.status).toBe(401)
|
||||
})
|
||||
|
||||
it('returns 403 for non-hse/admin roles', async () => {
|
||||
vi.mocked(getSession).mockResolvedValue({ ...mockSession, role: 'reporter' as never })
|
||||
const res = await GET(makeRequest('inc-1'), { params: Promise.resolve({ id: 'inc-1' }) })
|
||||
expect(res.status).toBe(403)
|
||||
})
|
||||
|
||||
it('returns 404 when incident not found', async () => {
|
||||
vi.mocked(getSession).mockResolvedValue(mockSession)
|
||||
vi.mocked(withUser).mockResolvedValue([[], [], [], []])
|
||||
const res = await GET(makeRequest('inc-1'), { params: Promise.resolve({ id: 'inc-1' }) })
|
||||
expect(res.status).toBe(404)
|
||||
})
|
||||
|
||||
it('returns 400 when incident is not closed', async () => {
|
||||
vi.mocked(getSession).mockResolvedValue(mockSession)
|
||||
vi.mocked(withUser).mockResolvedValue([[{ ...MOCK_INCIDENT, status: 'investigating' }], [], [], []])
|
||||
const res = await GET(makeRequest('inc-1'), { params: Promise.resolve({ id: 'inc-1' }) })
|
||||
expect(res.status).toBe(400)
|
||||
})
|
||||
|
||||
it('returns PDF with correct headers for a closed incident', async () => {
|
||||
vi.mocked(getSession).mockResolvedValue(mockSession)
|
||||
vi.mocked(withUser).mockResolvedValue([[MOCK_INCIDENT], [], [], []])
|
||||
const res = await GET(makeRequest('inc-1'), { params: Promise.resolve({ id: 'inc-1' }) })
|
||||
expect(res.status).toBe(200)
|
||||
expect(res.headers.get('Content-Type')).toBe('application/pdf')
|
||||
expect(res.headers.get('Content-Disposition')).toContain('SC1-202607-0001')
|
||||
expect(buildIncidentReportPdf).toHaveBeenCalledOnce()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user