/** * I-008: Decision Audit — Override Applied with Immutable Timeline */ import { describe, it, expect, beforeAll, afterAll } from 'vitest' import { prisma, createTestContext } from '../setup' import { createTestUser, createTestProgram, createTestPipeline, createTestTrack, createTestStage, createTestProject, createTestPSS, cleanupTestData, } from '../helpers' import { decisionRouter } from '@/server/routers/decision' let programId: string let userIds: string[] = [] beforeAll(async () => { const program = await createTestProgram({ name: 'Decision Audit Test' }) programId = program.id }) afterAll(async () => { await cleanupTestData(programId, userIds) }) describe('I-008: Decision Audit — Override with Immutable Timeline', () => { it('creates OverrideAction and DecisionAuditLog preserving original state', async () => { const admin = await createTestUser('SUPER_ADMIN') userIds.push(admin.id) const pipeline = await createTestPipeline(programId) const track = await createTestTrack(pipeline.id) const stage = await createTestStage(track.id, { status: 'STAGE_ACTIVE' }) const project = await createTestProject(programId, { title: 'Audit Project' }) const pss = await createTestPSS(project.id, track.id, stage.id, { state: 'PENDING' }) const ctx = createTestContext(admin) const caller = decisionRouter.createCaller(ctx) // Apply override: PENDING → PASSED await caller.override({ entityType: 'ProjectStageState', entityId: pss.id, newValue: { state: 'PASSED' }, reasonCode: 'POLICY_EXCEPTION', reasonText: 'Special committee decision', }) // 1. Verify OverrideAction preserves the original state const overrideAction = await prisma.overrideAction.findFirst({ where: { entityType: 'ProjectStageState', entityId: pss.id }, }) expect(overrideAction).not.toBeNull() const prevValue = overrideAction!.previousValue as Record expect(prevValue.state).toBe('PENDING') expect(overrideAction!.reasonCode).toBe('POLICY_EXCEPTION') expect(overrideAction!.reasonText).toBe('Special committee decision') expect(overrideAction!.actorId).toBe(admin.id) // 2. Verify DecisionAuditLog was created const auditLog = await prisma.decisionAuditLog.findFirst({ where: { entityType: 'ProjectStageState', entityId: pss.id, eventType: 'override.applied' }, }) expect(auditLog).not.toBeNull() expect(auditLog!.actorId).toBe(admin.id) const details = auditLog!.detailsJson as Record expect(details.reasonCode).toBe('POLICY_EXCEPTION') // 3. Verify the actual state was updated const updatedPSS = await prisma.projectStageState.findUnique({ where: { id: pss.id } }) expect(updatedPSS!.state).toBe('PASSED') // 4. Verify immutable timeline via the auditTimeline procedure const timeline = await caller.auditTimeline({ entityType: 'ProjectStageState', entityId: pss.id, }) expect(timeline.timeline.length).toBeGreaterThanOrEqual(1) const overrideEntry = timeline.timeline.find(t => t.type === 'override') expect(overrideEntry).toBeDefined() expect((overrideEntry!.details as any).reasonCode).toBe('POLICY_EXCEPTION') // 5. Apply a second override: PASSED → REJECTED await caller.override({ entityType: 'ProjectStageState', entityId: pss.id, newValue: { state: 'REJECTED' }, reasonCode: 'DATA_CORRECTION', reasonText: 'Correcting previous override', }) // 6. Verify both overrides exist in the timeline const fullTimeline = await caller.auditTimeline({ entityType: 'ProjectStageState', entityId: pss.id, }) const overrides = fullTimeline.timeline.filter(t => t.type === 'override') expect(overrides.length).toBe(2) // 7. Verify second override preserved PASSED as previous state const secondOverride = await prisma.overrideAction.findFirst({ where: { entityType: 'ProjectStageState', entityId: pss.id, reasonCode: 'DATA_CORRECTION' }, }) expect(secondOverride).not.toBeNull() const secondPrevValue = secondOverride!.previousValue as Record expect(secondPrevValue.state).toBe('PASSED') }) })