import assert from 'node:assert/strict'; import test from 'node:test'; import type { ParseRestClient } from '../src/db/parse-rest.client.js'; import type { Queryable } from '../src/db/types.js'; import { ApiError } from '../src/http/api-error.js'; import type { ActionItem } from '../src/modules/saas-platform/domain.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-06T08:00:00.000Z'; const actionInput: Omit & { sourceDecisionId: string; sourceKind: 'insight'; creationKey: string; } = { id: 'action-1', workspaceId: 'workspace-1', sourceAnalysisId: 'analysis-1', sourceInsightId: 'insight-1', sourceDecisionId: 'decision-1', sourceKind: 'insight', creationKey: 'workspace-1|analysis-1|insight-1|decision-1', evidenceIds: ['evidence-1', 'evidence-1', 'evidence-2'], validationMetric: 'Evidence coverage >= 80%', actionType: 'experience', title: 'Improve evidence traceability', description: 'Keep insight evidence attached to delivery.', priority: 'high', status: 'open', productKey: null, assigneeUserId: null, dueAt: null, createdBy: 'user-1', }; const analysisRow = { public_id: 'analysis-1', workspace_public_id: 'workspace-1', analysis_type: 'voc_insight', target_kind: 'workspace', target_key: '', status: 'completed', input: {}, result: { insights: [{ id: 'insight-1', evidenceIds: ['evidence-1', 'evidence-2'] }] }, evidence_count: 2, requested_by_external_id: 'user-1', error_summary: null, requested_at: timestamp, started_at: timestamp, completed_at: timestamp, }; const decisionRow = { public_id: 'decision-1', 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.', decided_by_external_id: 'user-1', decided_at: timestamp, version: 1, supersedes_public_id: null, is_current: true, created_at: timestamp, updated_at: timestamp, }; test('Parse REST action repository writes and maps insight provenance', async () => { let createdBody: Record | null = null; const client = { async findOne(className: string, where: Record) { if (className === 'VocActionItem') { assert.equal(where.workspaceId, 'workspace-1'); return null; } if (className === 'VocInsightDecision') { assert.deepEqual(where, { workspaceId: 'workspace-1', publicId: 'decision-1' }); return { objectId: 'parse-decision-1', publicId: 'decision-1', workspaceId: 'workspace-1', sourceAnalysisId: 'analysis-1', sourceInsightId: 'insight-1', decision: 'confirmed', reviewedEvidenceIds: ['evidence-1', 'evidence-2'], comment: 'Evidence reviewed.', decidedBy: 'user-1', decidedAt: timestamp, version: 1, supersedesId: null, isCurrent: true, createdAt: timestamp, updatedAt: timestamp, }; } assert.equal(className, 'VocAnalysisRun'); assert.deepEqual(where, { workspaceId: 'workspace-1', publicId: 'analysis-1' }); return { objectId: 'parse-analysis-1', createdAt: timestamp, publicId: 'analysis-1', workspaceId: 'workspace-1', analysisType: 'voc_insight', targetKind: 'workspace', targetKey: '', status: 'completed', input: {}, result: { insights: [{ id: 'insight-1', evidenceIds: ['evidence-1', 'evidence-2'] }] }, evidenceCount: 2, requestedBy: 'user-1', requestedAt: timestamp, startedAt: timestamp, completedAt: timestamp, }; }, async create(className: string, body: Record) { assert.equal(className, 'VocActionItem'); createdBody = body; return { objectId: 'parse-action-1', createdAt: timestamp }; }, } as unknown as ParseRestClient; const repository = new ParseRestVocRepository(client); const action = await repository.createAction(actionInput); assert.deepEqual((createdBody as unknown as { evidenceIds: string[] }).evidenceIds, ['evidence-1', 'evidence-2']); assert.equal((createdBody as unknown as { sourceAnalysisId: string }).sourceAnalysisId, 'analysis-1'); assert.equal((createdBody as unknown as { sourceDecisionId: string }).sourceDecisionId, 'decision-1'); assert.equal((createdBody as unknown as { sourceKind: string }).sourceKind, 'insight'); assert.equal( (createdBody as unknown as { creationKey: string }).creationKey, 'workspace-1|analysis-1|insight-1|decision-1', ); assert.equal(action.sourceInsightId, 'insight-1'); assert.equal(action.sourceDecisionId, 'decision-1'); assert.deepEqual(action.evidenceIds, ['evidence-1', 'evidence-2']); assert.equal(action.validationMetric, 'Evidence coverage >= 80%'); await assert.rejects( repository.createAction({ ...actionInput, sourceInsightId: 'insight-unknown' }), (error) => error instanceof ApiError && error.code === 'source_insight_not_found', ); await assert.rejects( repository.createAction({ ...actionInput, sourceAnalysisId: null }), (error) => error instanceof ApiError && error.code === 'source_insight_orphan', ); }); test('Postgres action repository converts the source public ID and maps provenance', async () => { const calls: Array<{ text: string; values: readonly unknown[] }> = []; const database = { async query(text: string, values: readonly unknown[] = []) { calls.push({ text, values }); if (text.includes('action.creation_key = $2')) { return { rows: [], rowCount: 0 }; } if (text.includes('FROM voc.analysis_run run')) { return { rows: [analysisRow], rowCount: 1 }; } if (text.includes('FROM voc.insight_decision decision')) { return { rows: [decisionRow], rowCount: 1 }; } assert.match(text, /INSERT INTO voc\.action_item/); assert.match(text, /source_analysis_id/); assert.match(text, /source_insight_id/); assert.match(text, /evidence_ids/); assert.match(text, /validation_metric/); return { rows: [{ public_id: 'action-1', workspace_public_id: 'workspace-1', source_analysis_public_id: 'analysis-1', source_insight_id: 'insight-1', source_decision_id: 12, source_decision_public_id: 'decision-1', source_kind: 'insight', creation_key: 'workspace-1|analysis-1|insight-1|decision-1', evidence_ids: ['evidence-1', 'evidence-2'], validation_metric: 'Evidence coverage >= 80%', action_type: 'experience', title: 'Improve evidence traceability', description: 'Keep insight evidence attached to delivery.', priority: 'high', status: 'open', product_key: null, assignee_external_id: null, due_at: null, created_by_external_id: 'user-1', completed_at: null, created_at: timestamp, updated_at: timestamp, }], rowCount: 1, }; }, } as unknown as Queryable; const repository = new PostgresPlatformRepository(database); const action = await repository.createAction(actionInput); assert.equal(calls.length, 4); assert.equal(calls[3]!.values[11], 'analysis-1'); assert.equal(calls[3]!.values[12], 'insight-1'); assert.equal(calls[3]!.values[13], JSON.stringify(['evidence-1', 'evidence-2'])); assert.equal(calls[3]!.values[14], 'Evidence coverage >= 80%'); assert.equal(calls[3]!.values[15], 'decision-1'); assert.equal(calls[3]!.values[16], 'insight'); assert.equal(calls[3]!.values[17], 'workspace-1|analysis-1|insight-1|decision-1'); assert.equal(action.sourceAnalysisId, 'analysis-1'); assert.equal(action.sourceDecisionId, 'decision-1'); assert.equal(action.sourceKind, 'insight'); assert.equal(action.creationKey, 'workspace-1|analysis-1|insight-1|decision-1'); assert.deepEqual(action.evidenceIds, ['evidence-1', 'evidence-2']); await assert.rejects( repository.createAction({ ...actionInput, evidenceIds: ['evidence-from-other-insight'] }), (error) => error instanceof ApiError && error.code === 'source_evidence_not_in_insight', ); await assert.rejects( repository.createAction({ ...actionInput, sourceInsightId: null, evidenceIds: [] }), (error) => error instanceof ApiError && error.code === 'source_insight_required', ); }); test('action repositories return the existing creation key without creating another row', async () => { let parseCreates = 0; const parseClient = { async findOne(className: string, where: Record) { if (className === 'VocAnalysisRun') { assert.deepEqual(where, { workspaceId: 'workspace-1', publicId: 'analysis-1' }); return { objectId: 'parse-analysis-1', createdAt: timestamp, publicId: 'analysis-1', workspaceId: 'workspace-1', analysisType: 'voc_insight', targetKind: 'workspace', targetKey: '', status: 'completed', input: {}, result: { insights: [{ id: 'insight-1', evidenceIds: ['evidence-1', 'evidence-2'] }] }, evidenceCount: 2, requestedBy: 'user-1', requestedAt: timestamp, startedAt: timestamp, completedAt: timestamp, }; } if (className === 'VocInsightDecision') { assert.deepEqual(where, { workspaceId: 'workspace-1', publicId: 'decision-1' }); return { objectId: 'parse-decision-1', publicId: 'decision-1', workspaceId: 'workspace-1', sourceAnalysisId: 'analysis-1', sourceInsightId: 'insight-1', decision: 'confirmed', reviewedEvidenceIds: ['evidence-1', 'evidence-2'], comment: 'Evidence reviewed.', decidedBy: 'user-1', decidedAt: timestamp, version: 1, supersedesId: null, isCurrent: true, createdAt: timestamp, updatedAt: timestamp, }; } assert.equal(className, 'VocActionItem'); assert.deepEqual(where, { workspaceId: 'workspace-1', creationKey: 'workspace-1|analysis-1|insight-1|decision-1', }); return { objectId: 'parse-action-1', publicId: 'action-existing', workspaceId: 'workspace-1', sourceAnalysisId: 'analysis-1', sourceInsightId: 'insight-1', sourceDecisionId: 'decision-1', sourceKind: 'insight', creationKey: 'workspace-1|analysis-1|insight-1|decision-1', evidenceIds: ['evidence-1', 'evidence-2'], validationMetric: 'Evidence coverage >= 80%', actionType: 'experience', title: 'Existing action', description: '', priority: 'high', status: 'open', createdBy: 'user-1', createdAt: timestamp, updatedAt: timestamp, }; }, async create() { parseCreates += 1; throw new Error('Parse create should not run for an existing creation key'); }, } as unknown as ParseRestClient; const parseAction = await new ParseRestVocRepository(parseClient).createAction(actionInput); assert.equal(parseAction.id, 'action-existing'); assert.equal(parseCreates, 0); let postgresQueries = 0; const database = { async query(text: string, values: readonly unknown[] = []) { postgresQueries += 1; if (text.includes('FROM voc.analysis_run run')) { return { rows: [analysisRow], rowCount: 1 }; } if (text.includes('FROM voc.insight_decision decision')) { return { rows: [decisionRow], rowCount: 1 }; } assert.match(text, /action\.creation_key = \$2/); assert.deepEqual(values, ['workspace-1', 'workspace-1|analysis-1|insight-1|decision-1']); return { rows: [{ public_id: 'action-existing', workspace_public_id: 'workspace-1', source_analysis_id: 10, source_analysis_public_id: 'analysis-1', source_insight_id: 'insight-1', source_decision_id: 11, source_decision_public_id: 'decision-1', source_kind: 'insight', creation_key: 'workspace-1|analysis-1|insight-1|decision-1', evidence_ids: ['evidence-1', 'evidence-2'], validation_metric: 'Evidence coverage >= 80%', action_type: 'experience', title: 'Existing action', description: '', priority: 'high', status: 'open', product_key: null, assignee_external_id: null, due_at: null, created_by_external_id: 'user-1', completed_at: null, created_at: timestamp, updated_at: timestamp, }], rowCount: 1, }; }, } as unknown as Queryable; const postgresAction = await new PostgresPlatformRepository(database).createAction(actionInput); assert.equal(postgresAction.id, 'action-existing'); assert.equal(postgresQueries, 3); });