diff --git a/tests/api/incidents/rca-draft.test.ts b/tests/api/incidents/rca-draft.test.ts new file mode 100644 index 0000000..484e03a --- /dev/null +++ b/tests/api/incidents/rca-draft.test.ts @@ -0,0 +1,78 @@ +import { describe, it, expect, vi } from 'vitest' + +const mockIncident = { + id: 'inc-1', + incident_type: 'injury', + description: 'Forklift operator collided with racking in zone B, injuring left arm.', + severity: 3, + injury_involved: true, + medical_status: 'medical_treatment', + is_fatality: false, + is_serious_bodily_injury: false, + triage_notes: null, + sites: { name: 'Warehouse A' }, + zones: { name: 'Zone B' }, +} + +vi.mock('@/lib/supabase/server', () => ({ + createClient: vi.fn().mockResolvedValue({ + auth: { + getUser: vi.fn().mockResolvedValue({ data: { user: { id: 'user-1' } }, error: null }), + }, + from: vi.fn().mockReturnValue({ + select: vi.fn().mockReturnThis(), + eq: vi.fn().mockReturnThis(), + single: vi.fn() + .mockResolvedValueOnce({ data: { role: 'hse' } }) // profile + .mockResolvedValueOnce({ data: mockIncident }), // incident + }), + rpc: vi.fn().mockResolvedValue({ error: null }), + }), +})) + +vi.mock('@/lib/claude/client', () => ({ + createAnthropicClient: vi.fn().mockReturnValue({ + messages: { + create: vi.fn().mockResolvedValue({ + content: [{ + type: 'tool_use', + name: 'draft_rca', + input: { + five_why_steps: [ + { why: 'Why did the incident happen?', answer: 'Forklift entered zone B without checking for pedestrians.' }, + { why: 'Why was there no check?', answer: 'No pedestrian exclusion zone marked at zone B entrance.' }, + { why: 'Why was it not marked?', answer: 'Site hazard assessment did not include zone B forklift route.' }, + ], + root_cause_summary: 'Absence of pedestrian exclusion zone and forklift route hazard assessment in zone B.', + capa_suggestions: [ + 'Mark pedestrian exclusion zones at all forklift routes in zone B within 7 days.', + 'Update site hazard assessment to include forklift routes in all zones.', + 'Conduct forklift safety refresher for all operators within 30 days.', + ], + }, + }], + }), + }, + }), +})) + +vi.mock('@/lib/settings', () => ({ + getApiKey: vi.fn().mockResolvedValue('sk-test-key'), +})) + +describe('POST /api/incidents/[id]/ai/rca-draft', () => { + it('returns five_why_steps, root_cause_summary, and capa_suggestions', async () => { + const { POST } = await import('@/app/api/incidents/[id]/ai/rca-draft/route') + const req = new Request('http://localhost/api/incidents/inc-1/ai/rca-draft', { method: 'POST' }) + const res = await POST(req as never, { params: Promise.resolve({ id: 'inc-1' }) }) + expect(res.status).toBe(200) + const body = await res.json() + expect(body).toHaveProperty('five_why_steps') + expect(Array.isArray(body.five_why_steps)).toBe(true) + expect(body.five_why_steps.length).toBeGreaterThanOrEqual(1) + expect(body).toHaveProperty('root_cause_summary') + expect(typeof body.root_cause_summary).toBe('string') + expect(body).toHaveProperty('capa_suggestions') + expect(Array.isArray(body.capa_suggestions)).toBe(true) + }) +})