31 lines
2.7 KiB
TypeScript
31 lines
2.7 KiB
TypeScript
import { decryptText } from '../../src/crypto.js';
|
|
import type { AppConfig } from '../../src/config.js';
|
|
import type pg from 'pg';
|
|
|
|
// Stateful test database; scope, unique keys, orphan joins and backfill are all exercised.
|
|
export function progressMemory(config: AppConfig) {
|
|
const owners = new Map<string, boolean>();
|
|
const records: Record<string, any>[] = [], tasks: Record<string, any>[] = [];
|
|
const answer = (rows: Record<string, any>[] = []) => ({ rowCount: rows.length, rows });
|
|
const query = async (sql: string, p: any[] = []) => {
|
|
const owner = `${p[0]}/${p[1]}`, scoped = records.filter(r => r.organization_id === p[0] && r.owner_user_id === p[1]);
|
|
if (sql.includes('INSERT INTO team_progress_owners')) { if (!owners.has(owner)) owners.set(owner,false); return answer(); }
|
|
if (sql.includes('SELECT backfilled_at FROM team_progress_owners')) return answer([{backfilled_at:owners.get(owner)?'done':null}]);
|
|
if (sql.includes('UPDATE team_progress_owners')) { owners.set(owner,true); return answer(); }
|
|
if (sql.includes('FROM tasks t JOIN task_attempts') && sql.includes('COALESCE')) return answer(tasks.filter(t => t.organization_id === p[0] && t.assigned_user_id === p[1]
|
|
&& ['completed','blocked','failed','reconciliation_pending'].includes(t.status) && t.task_id > p[2] && t.execution_id === t.execution_result.execution_id).sort((a,b) => a.task_id.localeCompare(b.task_id)).slice(0,200));
|
|
if (sql.includes('INSERT INTO team_progress_events')) {
|
|
if (!scoped.some(r => r.execution_id === p[7] && r.event_key === p[5])) records.push({organization_id:p[0],owner_user_id:p[1],group_key:p[2],subject_key:p[3],
|
|
subject_kind:p[4],event_key:p[5],source_task_id:p[6],execution_id:p[7],effective_at:p[8],recorded_at:p[9],detail_ciphertext:p[10],source_task_deleted_at:null});
|
|
return answer();
|
|
}
|
|
if (sql.includes('SELECT detail_ciphertext FROM team_progress_events')) return answer(scoped.filter(r => r.subject_key === p[2] && r.subject_kind === 'child' && r.group_key));
|
|
if (sql.includes('SELECT e.detail_ciphertext')) return answer(scoped.filter(r => r.group_key === p[2] || (!r.group_key && r.subject_kind === 'child'
|
|
&& scoped.some(link => link.subject_key === r.subject_key && link.group_key === p[2])
|
|
&& !scoped.some(link => link.subject_key === r.subject_key && link.group_key && link.group_key !== p[2]))));
|
|
if (sql.includes('UPDATE team_progress_events')) { scoped.filter(r => p[2].includes(r.source_task_id)).forEach(r => r.source_task_deleted_at = new Date()); return answer(); }
|
|
throw new Error(`Unexpected progress SQL: ${sql}`);
|
|
};
|
|
return {query: query as unknown as pg.PoolClient['query'],records,tasks,owners,decode:() => records.map(r => JSON.parse(decryptText(config,r.detail_ciphertext)))};
|
|
}
|