import { test } from 'node:test'; import assert from 'node:assert/strict'; import { existsSync, mkdtempSync, readFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { ForbiddenException } from '@nestjs/common'; import { SettingsService } from '../src/settings/settings.service.js'; const viewer = { actor: 'viewer-1', role: 'viewer', project: 'default', tenant: 'default' }; const admin = { actor: 'admin-1', role: 'org-admin', project: 'default', tenant: 'default' }; function withTempStore(fn: (storeFile: string) => void) { const prev = process.env.CASAN_CP_STORE_FILE; const storeFile = join(mkdtempSync(join(tmpdir(), 'cp-settings-')), 'settings.json'); process.env.CASAN_CP_STORE_FILE = storeFile; try { fn(storeFile); } finally { if (prev === undefined) delete process.env.CASAN_CP_STORE_FILE; else process.env.CASAN_CP_STORE_FILE = prev; } } test('settings list is readable by viewer and exposes policy/capabilities', () => { withTempStore(() => { const svc = new SettingsService(); const res = svc.list(viewer) as any; assert.ok(res.policy['compression.enabled']); assert.equal(res.capabilities.can_write_standard, false); assert.equal(res.capabilities.can_write_sensitive, false); }); }); test('viewer cannot write settings', () => { withTempStore(() => { const svc = new SettingsService(); assert.throws( () => svc.set({ key: 'compression.enabled', value: true, reason: 'test viewer deny' }, viewer), ForbiddenException, ); }); }); test('org-admin writes and rolls back through governed store with audit', () => { withTempStore((storeFile) => { const svc = new SettingsService(); const first = svc.set({ key: 'compression.enabled', value: true, reason: 'enable compression' }, admin) as any; const second = svc.set({ key: 'compression.enabled', value: false, reason: 'disable compression' }, admin) as any; const rolled = svc.rollback({ key: 'compression.enabled', reason: 'rollback compression' }, admin) as any; assert.equal(first.setting.version, 1); assert.equal(second.setting.version, 2); assert.equal(rolled.setting.version, 3); assert.equal(rolled.setting.value, true); assert.equal(rolled.audit_verify.ok, true); assert.ok(existsSync(storeFile)); const raw = JSON.parse(readFileSync(storeFile, 'utf8')); assert.equal(raw.audit.length, 3); assert.equal(raw.audit[0].action, 'set'); assert.equal(raw.audit[2].action, 'rollback'); }); }); test('sensitive setting requires approval even for org-admin in dev gate', () => { withTempStore(() => { const svc = new SettingsService(); assert.throws( () => svc.set({ key: 'security.strict', value: false, reason: 'loosen strict mode' }, admin), ForbiddenException, ); const res = svc.set({ key: 'security.strict', value: true, reason: 'tighten strict mode with approval token', approval: 'approved-in-dev', }, admin) as any; assert.equal(res.setting.value, true); }); });