/* Throwaway harness: applies the operations migration to a scratch database and * runs every operations report across several parameter sets. Delete after use. */ import { DataSource } from 'typeorm'; import { OperationsReporting3580000000000 } from '../migrations/3580000000000-OperationsReporting'; import { REPORTS } from '../modules/reports/report.registry'; import { ReportContext } from '../modules/reports/report.types'; const NEW_KEYS = [ 'station-staying-time', 'turnaround-cycle', 'train-delays', 'trainset-performance', 'teu-performance', 'cargo-volume-performance', 'charged-vs-actual-volume', 'cargo-volume-by-station', ]; const PARAM_SETS: Record[] = [ {}, { period: 'month' }, { period: 'quarter', direction: 'IMPORT' }, { period: 'week', country: 'Djibouti' }, { period: 'year', country: 'Ethiopia', categories: ['CONTAINER_IMPORT_MULTIMODAL', 'BULK'] }, { dateFrom: '2020-01-01', dateTo: '2030-01-01', delayedOnly: 'true' }, { trainNumber: 'X', origin: 'NAGAD', destination: 'KALITY' }, ]; async function main(): Promise { const ds = new DataSource({ type: 'postgres', host: 'localhost', port: 5433, username: 'nati', password: 'password', database: 'nati_wt_opsreport', entities: ['src/**/*.entity.ts'], synchronize: false, logging: false, }); await ds.initialize(); // Apply the migration's DDL by hand — this scratch database is not under // migration control and only needs the two new tables and two columns. const runner = ds.createQueryRunner(); await new OperationsReporting3580000000000().up(runner); await runner.release(); console.log('migration applied\n'); let failures = 0; for (const key of NEW_KEYS) { const def = REPORTS.find((r) => r.key === key); if (!def) { console.log(`MISSING ${key}`); failures++; continue; } for (const [i, params] of PARAM_SETS.entries()) { for (const directions of [null, ['IMPORT']] as (string[] | null)[]) { const ctx: ReportContext = { ds, params, directions }; const label = `${key} [set ${i}${directions ? ' scoped' : ''}]`; try { const qb = def.query(ctx); const rows = await qb.limit(5).getRawMany(); // The runner wraps every query for the total count — prove that works too. const [sql, bound] = qb.getQueryAndParameters(); const counted = await ds.query(`SELECT COUNT(*)::int AS n FROM (${sql}) AS sub`, bound); // Sorting: every sortable column must be a legal ORDER BY. for (const col of def.columns.filter((c) => c.sortable)) { await def .query({ ds, params, directions }) .orderBy(col.sortExpr ?? `"${col.key}"`, 'DESC') .limit(1) .getRawMany(); } const kpis = def.summary ? await def.summary(ctx) : []; console.log( `OK ${label.padEnd(50)} rows=${rows.length} total=${counted[0]?.n ?? '?'} kpis=${kpis .map((k) => `${k.label}:${k.value}${k.unit ?? ''}`) .join(' ')}`, ); } catch (err) { failures++; console.log(`FAIL ${label}: ${(err as Error).message.split('\n')[0]}`); } } } } console.log(failures ? `\n${failures} failures` : '\nall checks passed'); await ds.destroy(); process.exit(failures ? 1 : 0); } void main();