import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; import test from 'node:test'; import type { ParseRestClient } from '../src/db/parse-rest.client.js'; import { VOC_PARSE_CLASSES, VOC_PARSE_SCHEMAS } from '../src/db/parse-rest.schema.js'; import type { Queryable } from '../src/db/types.js'; import { ParseRestVocRepository } from '../src/modules/saas-platform/parse-rest-voc.repository.js'; import { PostgresPlatformRepository } from '../src/modules/saas-platform/postgres-platform.repository.js'; const timestamp = '2026-08-07T08:00:00.000Z'; const decisionInput = { id: 'decision-2', workspaceId: 'workspace-1', sourceAnalysisId: 'analysis-1', sourceInsightId: 'insight-1', decision: 'confirmed' as const, reviewedEvidenceIds: ['evidence-1', 'evidence-1', 'evidence-2'], comment: 'Evidence reviewed by the product owner.', decidedBy: 'user-1', }; const previousParseDecision = { objectId: 'parse-decision-1', publicId: 'decision-1', workspaceId: 'workspace-1', sourceAnalysisId: 'analysis-1', sourceInsightId: 'insight-1', decision: 'needs_more_evidence' as const, reviewedEvidenceIds: ['evidence-1'], comment: 'Review another sample.', decidedBy: 'user-1', decidedAt: { __type: 'Date', iso: timestamp }, version: 1, supersedesId: null, isCurrent: true, createdAt: timestamp, updatedAt: timestamp, }; test('Parse REST insight decisions append a version and retire the prior current record', async () => { let createdBody: Record | null = null; const updates: Array<{ objectId: string; patch: Record }> = []; const client = { async findOne(className: string) { assert.equal(className, VOC_PARSE_CLASSES.insightDecision); return null; }, async findAll(className: string, where: Record) { assert.equal(className, VOC_PARSE_CLASSES.insightDecision); assert.deepEqual(where, { workspaceId: 'workspace-1', sourceAnalysisId: 'analysis-1', sourceInsightId: 'insight-1', }); return [previousParseDecision]; }, async create(className: string, body: Record) { assert.equal(className, VOC_PARSE_CLASSES.insightDecision); createdBody = body; return { objectId: 'parse-decision-2', createdAt: timestamp, updatedAt: timestamp }; }, async update(className: string, objectId: string, patch: Record) { assert.equal(className, VOC_PARSE_CLASSES.insightDecision); updates.push({ objectId, patch }); return { updatedAt: timestamp }; }, } as unknown as ParseRestClient; const decision = await new ParseRestVocRepository(client).createInsightDecision(decisionInput); assert.equal((createdBody as unknown as { version: number }).version, 2); assert.equal((createdBody as unknown as { supersedesId: string }).supersedesId, 'decision-1'); assert.deepEqual( (createdBody as unknown as { reviewedEvidenceIds: string[] }).reviewedEvidenceIds, ['evidence-1', 'evidence-2'], ); assert.deepEqual(updates, [{ objectId: 'parse-decision-1', patch: { isCurrent: false } }]); assert.equal(decision.version, 2); assert.equal(decision.supersedesId, 'decision-1'); assert.equal(decision.isCurrent, true); }); test('Parse REST insight decision create returns an existing public ID without appending', async () => { let writes = 0; const client = { async findOne() { return previousParseDecision; }, async findAll() { throw new Error('findAll should not run for an existing decision ID'); }, async create() { writes += 1; throw new Error('create should not run for an existing decision ID'); }, } as unknown as ParseRestClient; const decision = await new ParseRestVocRepository(client).createInsightDecision({ ...decisionInput, id: 'decision-1', }); assert.equal(decision.id, 'decision-1'); assert.equal(writes, 0); }); test('Parse REST insight decisions support filtered list and workspace-scoped get', async () => { const client = { async findAll(className: string, where: Record) { assert.equal(className, VOC_PARSE_CLASSES.insightDecision); assert.deepEqual(where, { workspaceId: 'workspace-1' }); return [previousParseDecision, { ...previousParseDecision, publicId: 'decision-retired', isCurrent: false }]; }, async findOne(className: string, where: Record) { assert.equal(className, VOC_PARSE_CLASSES.insightDecision); assert.deepEqual(where, { workspaceId: 'workspace-1', publicId: 'decision-1' }); return previousParseDecision; }, } as unknown as ParseRestClient; const repository = new ParseRestVocRepository(client); const page = await repository.listInsightDecisions({ workspaceId: 'workspace-1', limit: 10, cursor: null, sourceAnalysisId: 'analysis-1', sourceInsightId: 'insight-1', currentOnly: true, }); const decision = await repository.getInsightDecision('workspace-1', 'decision-1'); assert.deepEqual(page.items.map((item) => item.id), ['decision-1']); assert.equal(page.nextCursor, null); assert.equal(decision?.decision, 'needs_more_evidence'); }); test('Postgres insight decision create uses one append-and-retire statement and maps the version chain', async () => { const calls: Array<{ text: string; values: readonly unknown[] }> = []; const database = { async query(text: string, values: readonly unknown[] = []) { calls.push({ text, values }); assert.match(text, /WITH target AS/); assert.match(text, /FOR UPDATE OF analysis/); assert.match(text, /SET is_current = false/); assert.match(text, /CROSS JOIN \(SELECT count\(\*\) FROM retired\)/); return { rows: [{ public_id: 'decision-2', workspace_public_id: 'workspace-1', source_analysis_public_id: 'analysis-1', source_insight_id: 'insight-1', decision: 'confirmed', reviewed_evidence_ids: ['evidence-1', 'evidence-2'], comment: 'Evidence reviewed by the product owner.', decided_by_external_id: 'user-1', decided_at: timestamp, version: 2, supersedes_public_id: 'decision-1', is_current: true, created_at: timestamp, updated_at: timestamp, }], rowCount: 1, }; }, } as unknown as Queryable; const decision = await new PostgresPlatformRepository(database).createInsightDecision(decisionInput); assert.equal(calls.length, 1); assert.deepEqual(calls[0]!.values, [ 'workspace-1', 'decision-2', 'analysis-1', 'insight-1', 'confirmed', JSON.stringify(['evidence-1', 'evidence-2']), 'Evidence reviewed by the product owner.', 'user-1', ]); assert.equal(decision.version, 2); assert.equal(decision.supersedesId, 'decision-1'); assert.equal(decision.decidedAt, timestamp); }); test('Postgres insight decisions support filtered list and workspace-scoped get', async () => { const calls: Array<{ text: string; values: readonly unknown[] }> = []; const row = { cursor_id: '42', public_id: 'decision-1', workspace_public_id: 'workspace-1', source_analysis_public_id: 'analysis-1', source_insight_id: 'insight-1', decision: 'needs_more_evidence', reviewed_evidence_ids: ['evidence-1'], comment: 'Review another sample.', decided_by_external_id: 'user-1', decided_at: timestamp, version: 1, supersedes_public_id: null, is_current: true, created_at: timestamp, updated_at: timestamp, }; const database = { async query(text: string, values: readonly unknown[] = []) { calls.push({ text, values }); return { rows: [row], rowCount: 1 }; }, } as unknown as Queryable; const repository = new PostgresPlatformRepository(database); const page = await repository.listInsightDecisions({ workspaceId: 'workspace-1', limit: 10, cursor: null, sourceAnalysisId: 'analysis-1', sourceInsightId: 'insight-1', currentOnly: true, }); const decision = await repository.getInsightDecision('workspace-1', 'decision-1'); assert.deepEqual(calls[0]!.values, [ 'workspace-1', '9223372036854775807', 'analysis-1', 'insight-1', true, 11, ]); assert.deepEqual(calls[1]!.values, ['workspace-1', 'decision-1']); assert.equal(page.items[0]?.id, 'decision-1'); assert.equal(decision?.sourceAnalysisId, 'analysis-1'); }); test('Parse schema and PostgreSQL migrations declare decision, idempotency, and source guards', async () => { const decisionSchema = VOC_PARSE_SCHEMAS.find((schema) => schema.className === VOC_PARSE_CLASSES.insightDecision); const actionSchema = VOC_PARSE_SCHEMAS.find((schema) => schema.className === VOC_PARSE_CLASSES.actionItem); assert.ok(decisionSchema); assert.equal(decisionSchema.fields.isCurrent?.type, 'Boolean'); assert.deepEqual(decisionSchema.indexes?.voc_insight_decision_source_current_idx, { workspaceId: 1, sourceAnalysisId: 1, sourceInsightId: 1, isCurrent: 1, }); assert.equal(actionSchema?.fields.creationKey?.type, 'String'); assert.deepEqual(actionSchema?.indexes?.voc_action_workspace_creation_key_idx, { workspaceId: 1, creationKey: 1, }); const migration = await readFile( new URL('../migrations/005_insight_decision_action_idempotency.sql', import.meta.url), 'utf8', ); assert.match(migration, /CREATE UNIQUE INDEX IF NOT EXISTS insight_decision_source_current_unique/); assert.match(migration, /CREATE UNIQUE INDEX IF NOT EXISTS action_item_workspace_creation_key_unique/); assert.match(migration, /CREATE TRIGGER insight_decision_append_only_guard/); assert.match(migration, /CREATE TRIGGER action_item_source_decision_guard/); const hardeningMigration = await readFile( new URL('../migrations/006_insight_action_guard_hardening.sql', import.meta.url), 'utf8', ); assert.match(hardeningMigration, /reviewed evidence must cover every evidence ID/); assert.match(hardeningMigration, /non-insight actions cannot carry insight analysis, insight, or decision sources/); assert.match(hardeningMigration, /insight actions require analysis, insight, and decision sources/); assert.match(hardeningMigration, /UPDATE OF[\s\S]*action_type,[\s\S]*validation_metric,[\s\S]*evidence_ids/); assert.match(hardeningMigration, /ADD CONSTRAINT action_item_source_decision_kind_check[\s\S]*NOT VALID/); });