import { describe, expect, it, vi } from 'vitest'; import { RegulatoryFindingRetrievalService, mergeFindingQueryRankings, reciprocalRankFuseByControl } from '../../server/regulatory/regulatory-finding-retrieval'; import type { RegulatoryRetrievalCandidate, RegulatoryRetrievalRequest } from '../../src/domain/regulatory-rag'; import type { UnifiedResultProjection } from '../../src/domain/unified-result'; import { buildRegulatoryFindingQuery } from '../../src/profiles/public-search/regulatory-query'; // Result-table / occurrence data and raw addresses that must never reach the query. const resultTableAsset = 'host.customer.invalid'; const reporterEmail = 'ops@acme.invalid'; const ownerEmail = 'analyst@acme.invalid'; function finding(overrides: Partial = {}): UnifiedResultProjection { return { resultProjectionId: 'projection-1', sourceKind: 'scanner-finding', sourceResultId: 'source-1', projectId: 'tenant-1', assessmentId: 'assessment-1', collectionRunId: 'run-1', sourceTool: 'scanner-one', sourceRuleId: 'RULE-1', title: 'Unsigned protocol sessions are permitted', description: `The service permits unsigned sessions. Reported by ${reporterEmail}.`, recommendation: 'Require signed sessions through the platform policy.', affectedAsset: resultTableAsset, severityOriginal: 'high', severityNormalized: 'high', outcomeOriginal: 'fail', outcomeNormalized: 'fail', technicalExecutionState: 'completed', resultType: 'compliance_check', isIoe: false, isDeviation: true, lifecycleState: 'new', mappingConfidence: 1, reviewStatus: 'open', reviewOwner: ownerEmail, importedAt: '2026-09-15T10:00:00.000Z', createdAt: '2026-09-15T10:00:00.000Z', categories: [], ...overrides }; } function hit( controlId: string, chunkId: string, score: number, sectionKind?: RegulatoryRetrievalCandidate['chunk']['sectionKind'] ): RegulatoryRetrievalCandidate { return { score, chunk: { schemaVersion: 1, corpusId: 'synthetic', corpusVersion: '1', documentId: `${controlId}-document`, framework: 'Synthetic Framework', frameworkVersion: '1', controlId, chunkId, chunkIndex: 0, language: 'de', title: 'Synthetic control', text: 'Synthetic regulatory text.', textSha256: 'a'.repeat(64), ...(sectionKind ? { sectionKind } : {}) } }; } describe('regulatory finding retrieval boundary', () => { it('builds problem and remediation queries only from privacy-safe finding text', () => { const query = buildRegulatoryFindingQuery(finding()); expect(query).toEqual({ schemaVersion: 4, problemQuery: 'Security check: Unsigned protocol sessions are permitted. Check detail: The service permits unsigned sessions. Reported by [redacted-email].', remediationQuery: 'Security check: Unsigned protocol sessions are permitted. Recommended action: Require signed sessions through the platform policy.', retrievalQuery: 'Security check: Unsigned protocol sessions are permitted. Check detail: The service permits unsigned sessions. Reported by [redacted-email]. Recommended action: Require signed sessions through the platform policy.', query: 'Retrieve regulatory controls whose normative requirements directly address this security finding. Security check: Unsigned protocol sessions are permitted. Check detail: The service permits unsigned sessions. Reported by [redacted-email]. Recommended action: Require signed sessions through the platform policy. Prefer controls with direct requirement text over merely related guidance. Do not infer customer scope, assets, configuration values, or compliance status.' }); for (const marker of [resultTableAsset, reporterEmail, ownerEmail, 'scanner-one', 'RULE-1']) { expect(JSON.stringify(query)).not.toContain(marker); } }); it('produces identical retrieval input for identical findings from unknown or known scanners', () => { const first = buildRegulatoryFindingQuery(finding({ sourceTool: 'scanner-one', sourceRuleId: 'RULE-1' })); const second = buildRegulatoryFindingQuery(finding({ sourceTool: 'brand-new-scanner', sourceRuleId: 'NEW-999' })); expect(second).toEqual(first); }); it('uses one problem query when no recommendation is available', () => { const query = buildRegulatoryFindingQuery(finding({ title: 'Excessive service privileges', description: undefined, recommendation: undefined })); expect(query).toMatchObject({ schemaVersion: 4, problemQuery: 'Security check: Excessive service privileges.', retrievalQuery: 'Security check: Excessive service privileges.' }); expect(query?.remediationQuery).toBeUndefined(); }); it('fails closed only for non-scanner records or findings without a check title', () => { expect(buildRegulatoryFindingQuery(finding({ sourceKind: 'environment-observation' }))).toBeUndefined(); expect(buildRegulatoryFindingQuery(finding({ title: ' ' }))).toBeUndefined(); }); it('runs problem, remediation and section-specific full-context queries', async () => { const retrieve = vi.fn(async (request: RegulatoryRetrievalRequest) => [hit( 'C-1', `chunk-${request.filters?.sectionKind ?? (request.query.includes('Recommended action') ? 'remediation' : 'problem')}`, 0.8, request.filters?.sectionKind )]); const service = new RegulatoryFindingRetrievalService({ retrieve }); const result = await service.retrieve(finding(), 12, { framework: 'Synthetic Framework' }); expect(result).toMatchObject({ sourceResultId: 'source-1', status: 'candidates' }); expect(retrieve.mock.calls.map(call => call[0])).toEqual([ { query: result.preparedQuery?.problemQuery, limit: 64, filters: { framework: 'Synthetic Framework' } }, { query: result.preparedQuery?.remediationQuery, limit: 64, filters: { framework: 'Synthetic Framework' } }, ...(['control', 'purpose', 'guidance'] as const).map(sectionKind => ({ query: result.preparedQuery?.retrievalQuery, limit: 32, filters: { framework: 'Synthetic Framework', sectionKind } })) ]); expect(result.candidates.map(item => item.chunk.sectionKind).filter(Boolean)) .toEqual(['control', 'purpose', 'guidance']); for (const marker of [resultTableAsset, reporterEmail, ownerEmail, 'scanner-one', 'RULE-1']) { expect(JSON.stringify(retrieve.mock.calls)).not.toContain(marker); } }); it('does not duplicate the broad search when no remediation text is present', async () => { const retrieve = vi.fn(async () => [hit('C-1', 'chunk-1', 0.8)]); const service = new RegulatoryFindingRetrievalService({ retrieve }); await service.retrieve(finding({ recommendation: undefined }), 12); expect(retrieve).toHaveBeenCalledTimes(4); }); it('fuses each control once per independent finding query', () => { const fused = reciprocalRankFuseByControl([ [hit('C-1', 'problem-c1-a', 0.9), hit('C-1', 'problem-c1-b', 0.89), hit('C-2', 'problem-c2', 0.8)], [hit('C-2', 'remediation-c2', 0.85), hit('C-3', 'remediation-c3', 0.75), hit('C-1', 'remediation-c1', 0.65)] ], 2); expect(fused.map(item => item.chunk.chunkId)).toEqual([ 'problem-c2', 'remediation-c2', 'problem-c1-a', 'remediation-c1' ]); expect(new Set(fused.map(item => item.chunk.controlId))).toEqual(new Set(['C-1', 'C-2'])); expect(fused[0].score).toBeGreaterThan(fused[2].score); }); it('lets the collapsed full-context section family contribute a shortlist control', () => { const selected = mergeFindingQueryRankings( [ [hit('C-1', 'problem-c1', 0.9), hit('C-2', 'problem-c2', 0.8)], [hit('C-1', 'remediation-c1', 0.9)] ], [ [hit('C-3', 'control-c3', 1, 'control'), hit('C-1', 'control-c1', 0.7, 'control')], [hit('C-3', 'purpose-c3', 1, 'purpose'), hit('C-1', 'purpose-c1', 0.7, 'purpose')], [hit('C-3', 'guidance-c3', 1, 'guidance'), hit('C-1', 'guidance-c1', 0.7, 'guidance')] ], 2 ); expect(new Set(selected.map(item => item.chunk.controlId))).toEqual(new Set(['C-1', 'C-3'])); expect(selected.some(item => item.chunk.controlId === 'C-2')).toBe(false); expect(selected.filter(item => item.chunk.controlId === 'C-1').map(item => item.chunk.chunkId)) .toEqual(['control-c1', 'purpose-c1', 'guidance-c1', 'problem-c1', 'remediation-c1']); }); it('counts the three full-context sections as one final ranking vote', () => { const selected = mergeFindingQueryRankings( [ [hit('C-1', 'problem-c1', 0.9), hit('C-2', 'problem-c2', 0.8)], [hit('C-2', 'remediation-c2', 0.9)] ], [ [hit('C-3', 'control-c3', 1, 'control')], [hit('C-3', 'purpose-c3', 1, 'purpose')], [hit('C-3', 'guidance-c3', 1, 'guidance')] ], 2 ); expect(new Set(selected.map(item => item.chunk.controlId))).toEqual(new Set(['C-1', 'C-2'])); expect(selected.some(item => item.chunk.controlId === 'C-3')).toBe(false); }); it('does not call retrieval for a finding without a check title', async () => { const retrieve = vi.fn(); const service = new RegulatoryFindingRetrievalService({ retrieve }); await expect(service.retrieve(finding({ title: '' }), 5)) .resolves.toEqual({ sourceResultId: 'source-1', status: 'insufficient-public-context', candidates: [] }); expect(retrieve).not.toHaveBeenCalled(); }); });