import { strict as assert } from 'node:assert'; import { describe, it } from 'node:test'; import { z } from 'zod'; import { AuthError } from '../src/lib/auth'; import { apiError, executeMutation, MutationError } from '../src/lib/mutation'; import { fakeDatabase, onTeam, principal as makePrincipal } from './helpers/principal'; const principal = makePrincipal(); function db(events: string[], inserted: unknown[] = []) { return fakeDatabase({ events, inserted }); } describe('mutation convention', () => { it('checks capability before reading attacker-controlled input', async () => { const events: string[] = []; const forbidden = makePrincipal(onTeam('supply', 'admin')); await assert.rejects( executeMutation(db(events), forbidden, async () => { events.push('body'); return {}; }, { schema: z.object({ name: z.string() }), permission: { capability: 'deal:write', team: 'demand' }, invalidMessage: 'Invalid deal.', async mutate() { events.push('mutate'); return { data: {}, activity: { type: 'note', subject: 'Changed' } }; }, }), (error: unknown) => error instanceof AuthError && error.code === 'insufficient_permission', ); assert.deepEqual(events, []); }); it('rejects invalid ontology input before opening a transaction', async () => { const events: string[] = []; const stages = ['qualification', 'legal'] as const; await assert.rejects( executeMutation(db(events), principal, async () => ({ stage: 'invented' }), { schema: z.object({ stage: z.enum(stages) }), permission: { capability: 'deal:write', team: 'demand' }, invalidMessage: 'Invalid transition.', async mutate() { events.push('mutate'); return { data: {}, activity: { type: 'stage_change', subject: 'Changed' } }; }, }), (error: unknown) => error instanceof MutationError && error.code === 'invalid_request' && apiError(error.code, error.message, error.issues).issues?.length === 1, ); assert.deepEqual(events, []); }); it('writes mutation evidence in the same transaction with framework attribution', async () => { const events: string[] = []; const rows: unknown[] = []; const result = await executeMutation( db(events, rows), principal, async () => ({ stage: 'legal' }), { schema: z.object({ stage: z.literal('legal') }), permission: { capability: 'deal:write', team: 'demand' }, invalidMessage: 'Invalid transition.', async mutate() { events.push('mutate'); const data = { id: 'deal-1' }; return { data, activity: { type: 'stage_change', subject: 'Moved to legal', demandDealId: data.id, }, }; }, }, ); assert.deepEqual(result, { id: 'deal-1' }); assert.deepEqual(events, ['transaction', 'mutate', 'insert']); assert.deepEqual(rows, [ { type: 'stage_change', subject: 'Moved to legal', demandDealId: 'deal-1', actorUserId: principal.userId, actorAgent: null, source: 'manual', occurredAt: (rows[0] as { occurredAt: Date }).occurredAt, }, ]); assert.ok((rows[0] as { occurredAt: unknown }).occurredAt instanceof Date); }); });