import { describe, expect, it, vi } from 'vitest'; import { RegulatoryFindingRetrievalService } from '../../server/compliance/regulatory-finding-retrieval'; import { RegulatoryMappingService } from '../../server/compliance/regulatory-mapping-service'; import { complianceReadPortFrom } from '../../src/compliance/compliance-store'; import { LocalComplianceStore } from '../../src/compliance/local-compliance-store'; import type { RegulatoryChunk, RegulatoryRetrievalRequest } from '../../src/compliance/regulatory-rag'; import type { ScanRun } from '../../src/domain/canonical-model'; import { LocalAssessorRepository } from '../../src/persistence/local-assessor-repository'; // Permanent on/off invariance of the Compliance Engine (AGENTS.md, "Core Engine and engines"): the Core // works unchanged with the engine switched off, and running the engine - a single mapping, a decision and // a filtered batch - leaves every stored ASSESSOR value and the repository revision exactly as they were. // The engine writes only its own store. const stamp = '2026-10-05T08:00:00.000Z'; const confidence = { level: 'high' as const, score: 1, reasons: ['synthetic'] }; const provenance = { decisionSource: 'source-native' as const, confidence, mappingVersion: '1.0.0' }; function chunk(sourceStandard: 'ISO/IEC 27001' | 'ISO/IEC 27002'): RegulatoryChunk { const reference = sourceStandard === 'ISO/IEC 27001'; return { schemaVersion: 2, corpusId: 'iso-test', corpusVersion: 'v2', documentId: reference ? '27001-5.1' : '27002-5.1-purpose', framework: 'ISO/IEC 27002', frameworkVersion: '2022', controlId: '5.1', annexAControlId: 'A.5.1', controlTheme: 'organizational', sourceStandard, sourceEdition: '2022', nationalEdition: 'DIN 2024', sectionKind: reference ? 'control' : 'purpose', sourcePageStart: 1, sourcePageEnd: 1, retrievalRole: reference ? 'reference' : 'semantic', parserVersion: 'test', sourceSha256: reference ? '1'.repeat(64) : '2'.repeat(64), chunkId: reference ? 'reference-5.1' : 'semantic-5.1', chunkIndex: 0, language: 'de', title: 'Synthetic control', text: reference ? 'Synthetic normative statement.' : 'Synthetic semantic evidence.', textSha256: 'a'.repeat(64) }; } async function syntheticRepository(): Promise { const repository = new LocalAssessorRepository(); await repository.saveProject({ id: 'dummy-project', name: 'Dummy Customer', createdAt: stamp, updatedAt: stamp, attributes: {} }); await repository.saveAssessment({ id: 'dummy-assessment', projectId: 'dummy-project', name: 'Dummy', createdAt: stamp, updatedAt: stamp }); const scanRun: ScanRun = { id: 'dummy-run', assessmentId: 'dummy-assessment', sequence: 1, label: 'Dummy', tool: { id: 'purple-knight', name: 'Purple Knight' }, importedAt: stamp }; const session = await repository.beginImport({ sessionId: 'dummy-session', projectId: 'dummy-project', assessmentId: 'dummy-assessment', scanRun }); await session.stage({ importFiles: [{ id: 'dummy-file', scanRunId: scanRun.id, fileName: 'dummy.csv', format: 'CSV', sizeBytes: 10, importedAt: stamp, detectedTool: scanRun.tool, detection: confidence, processedWith: { parserId: 'csv', parserVersion: '1.0.0', profileId: 'purple-knight', profileVersion: '1.0.0', mappingVersion: '1.0.0' }, sourceArtifact: { kind: 'not-retained', reason: 'synthetic' } }], findingDefinitions: [{ id: 'dummy-definition', canonicalKey: 'purple-knight:A-SmbSigning', identity: { version: 1, strategy: 'tool-rule-id', durable: true, evidence: ['synthetic'] }, title: 'SMB signing is not required on domain controllers', category: 'Synthetic', frameworkReferences: [], createdAt: stamp, updatedAt: stamp }], findingOccurrences: [{ id: 'dummy-occurrence', definitionId: 'dummy-definition', scanRunId: scanRun.id, importFileId: 'dummy-file', sourceIdentity: { tool: scanRun.tool, originalRuleId: 'A-SmbSigning', canonicalRuleId: 'A-SmbSigning', originalTitle: 'SMB signing is not required on domain controllers' }, sourcePosition: { rowIndex: 1 }, originalFields: [{ key: 'Status', label: 'Status', value: 'Fail', columnIndex: 0 }], fieldMappings: [], severity: { originalValue: 'High', normalizedValue: 'high', provenance }, status: { originalValue: 'Fail', normalizedValue: 'failed', provenance } }] }); await session.commit(); return repository; } describe('Compliance Engine on/off invariance', () => { it('leaves every stored ASSESSOR value and the repository revision unchanged', async () => { const repository = await syntheticRepository(); // Switched off: the Core alone, read before any engine work. const off = { revision: repository.revision, data: JSON.stringify(await repository.exportProject('dummy-project')) }; const resultDbOff = JSON.stringify(await repository.queryUnifiedResults({ audience: 'result-db', projectId: 'dummy-project' })); const store = new LocalComplianceStore(); const service = new RegulatoryMappingService(complianceReadPortFrom(repository), store, { findingRetrieval: new RegulatoryFindingRetrievalService({ retrieve: vi.fn(async (_request: RegulatoryRetrievalRequest) => [{ score: 0.9, chunk: chunk('ISO/IEC 27002') }]) }), index: { ensureCollection: vi.fn(), replaceDocuments: vi.fn(), searchDense: vi.fn(), list: vi.fn(async () => [chunk('ISO/IEC 27001')]) } as never, client: { model: 'work-model', complete: vi.fn(async () => ({ value: { schemaVersion: 1, outcome: 'mapped', mappings: [{ controlId: '5.1', relationship: 'primary', confidence: 'high', rationale: 'Synthetic rationale.', evidenceChunkIds: ['semantic-5.1', 'reference-5.1'] }] } })) } as never, corpusId: 'iso-test', corpusVersion: 'v2', embeddingModel: 'bge-m3', now: () => new Date(stamp) }); const resultProjectionId = 'scanner-finding:dummy-occurrence'; await service.start('dummy-project', { resultProjectionId }); await service.close(); // The first proposal is accepted automatically; a manual decision then replaces it. const automatic = (await service.get('dummy-project', resultProjectionId)).decisions[0]; await service.decide('dummy-project', { resultProjectionId, controlId: '5.1', decision: 'rejected', expectedLatestDecisionId: automatic.id }); await service.startBatch('dummy-project', { assessmentIds: ['dummy-assessment'], scanRunIds: [], filters: {}, idempotencyKey: 'dummy-batch-1' }); await service.close(); // The engine really ran and kept its results in its own store. const engine = await store.exportProject('dummy-project'); expect(engine.regulatoryMappingRuns.map(run => run.status)).toEqual(['succeeded']); expect(engine.regulatoryMappingDecisions.map(decision => decision.decision)).toEqual(['accepted', 'rejected']); expect(engine.regulatoryMappingBatchRuns.map(batch => batch.status)).toEqual(['completed']); // Switched on: the Core is untouched. expect(repository.revision).toBe(off.revision); expect(JSON.stringify(await repository.exportProject('dummy-project'))).toBe(off.data); expect(JSON.stringify(await repository.queryUnifiedResults({ audience: 'result-db', projectId: 'dummy-project' }))).toBe(resultDbOff); }); it('gives the engine no Core write method', () => { const port = complianceReadPortFrom(new LocalAssessorRepository()); expect(Object.keys(port).sort()).toEqual(['getProject', 'getUnifiedResultById', 'listAssessments', 'listProjectScanRuns', 'listProjects', 'queryUnifiedResults']); }); });