import { describe, it, expect, beforeEach } from 'vitest'; import { resetDb } from '../helpers/db.js'; import { migrateUp } from '../../lib/db/migrate.js'; import * as spaces from '../../lib/db/repos/spaces.js'; import * as audit from '../../lib/db/repos/audit.js'; const owner = { kind: 'user', id: null }; beforeEach(async () => { await resetDb(); await migrateUp(); }); describe('audit log', () => { it('creates an audit row on space create', async () => { const s = await spaces.create({ slug: 'h', name: 'H' }, owner); const rows = await audit.listForEntity('space', s.id); expect(rows).toHaveLength(1); expect(rows[0].action).toBe('create'); }); it('records the diff of an update', async () => { const s = await spaces.create({ slug: 'h', name: 'H' }, owner); await spaces.update(s.id, { name: 'Hh' }, owner); const rows = await audit.listForEntity('space', s.id); const upd = rows.find(r => r.action === 'update'); expect(upd.diff.changes.name.before).toBe('H'); expect(upd.diff.changes.name.after).toBe('Hh'); }); it('redacts known sensitive keys', async () => { await audit.recordAudit(owner, 'update', 'test', null, { token: 'secret-abc' }, { token: 'secret-def' } ); const rows = await audit.listByActor({}); const r = rows.find(r => r.entity_type === 'test'); expect(r.diff.changes.token.before).toBe('[REDACTED]'); expect(r.diff.changes.token.after).toBe('[REDACTED]'); }); it('redacts sensitive keys in nested objects', async () => { await audit.recordAudit(owner, 'create', 'nested', null, null, { outer: { password: 'hunter2', name: 'ok' } } ); const rows = await audit.listByActor({}); const r = rows.find(r => r.entity_type === 'nested'); expect(r.diff.after.outer.password).toBe('[REDACTED]'); expect(r.diff.after.outer.name).toBe('ok'); }); });