All checks were successful
deploy / deploy (push) Successful in 59s
Proof: added persisted quote_lifecycle_stat_subjects snapshots, refreshed statistics from retained per-quote subjects, preserved completed/attributed execution evidence during lifecycle retention, and wired successful lifecycle evidence into the dashboard trade funnel; verified with targeted lifecycle/dashboard tests, full npm test, and operator dashboard bundle build. Assumptions: detailed successful trade rows already deleted by prior retention cannot be reconstructed from aggregate rollups without faking quote-level settlement evidence; the new subject table starts preserving surviving and future quote evidence after deployment. Still fake: fee-aware realized PnL and venue-native fill truth beyond retained settlement/inventory evidence remain out of scope; already-pruned detailed success rows remain unrecoverable.
293 lines
9.9 KiB
JavaScript
293 lines
9.9 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
|
|
import {
|
|
loadQuoteLifecycleStatistics,
|
|
refreshQuoteLifecycleStatistics,
|
|
} from '../src/lib/postgres.mjs';
|
|
|
|
test('postgres quote lifecycle statistics refresh idempotently upserts state buckets by original quote window', async () => {
|
|
const upserts = [];
|
|
const subjects = new Map();
|
|
let includeFailure = false;
|
|
const pool = {
|
|
async query(sql, params = []) {
|
|
if (sql.includes('FROM swap_demand_events') && sql.includes('ORDER BY COALESCE')) {
|
|
return {
|
|
rows: [{
|
|
observed_at: '2026-06-12T17:31:01.000Z',
|
|
ingested_at: '2026-06-12T17:31:01.000Z',
|
|
payload: {
|
|
quote_id: 'quote-stat',
|
|
pair: 'btc->eure',
|
|
asset_in: 'btc',
|
|
asset_out: 'eure',
|
|
amount_in: '1',
|
|
amount_out: '2',
|
|
},
|
|
}],
|
|
};
|
|
}
|
|
if (sql.includes('FROM trade_decisions') && sql.includes('ORDER BY COALESCE')) {
|
|
return {
|
|
rows: [{
|
|
observed_at: '2026-06-12T17:31:02.000Z',
|
|
ingested_at: '2026-06-12T17:31:02.000Z',
|
|
payload: {
|
|
quote_id: 'quote-stat',
|
|
decision_id: 'decision-stat',
|
|
pair: 'btc->eure',
|
|
decision: 'approved',
|
|
decision_reason: 'strategy_approved',
|
|
},
|
|
}],
|
|
};
|
|
}
|
|
if (sql.includes('FROM execute_trade_commands') && sql.includes('ORDER BY COALESCE')) {
|
|
return {
|
|
rows: [{
|
|
observed_at: '2026-06-12T17:31:03.000Z',
|
|
ingested_at: '2026-06-12T17:31:03.000Z',
|
|
payload: {
|
|
quote_id: 'quote-stat',
|
|
decision_id: 'decision-stat',
|
|
command_id: 'cmd-stat',
|
|
pair: 'btc->eure',
|
|
},
|
|
}],
|
|
};
|
|
}
|
|
if (sql.includes('FROM trade_execution_results r')) {
|
|
return {
|
|
rows: includeFailure ? [{
|
|
result_observed_at: '2026-06-12T17:36:10.000Z',
|
|
result_ingested_at: '2026-06-12T17:36:10.000Z',
|
|
result_payload: {
|
|
quote_id: 'quote-stat',
|
|
decision_id: 'decision-stat',
|
|
command_id: 'cmd-stat',
|
|
status: 'failed',
|
|
result_code: 'submission_failed',
|
|
failure_category: 'quote_not_found_or_finished',
|
|
},
|
|
command_ingested_at: '2026-06-12T17:31:03.000Z',
|
|
command_payload: {
|
|
quote_id: 'quote-stat',
|
|
decision_id: 'decision-stat',
|
|
command_id: 'cmd-stat',
|
|
},
|
|
decision_payload: {
|
|
quote_id: 'quote-stat',
|
|
decision_id: 'decision-stat',
|
|
decision: 'approved',
|
|
},
|
|
outcome_payload: null,
|
|
}] : [],
|
|
};
|
|
}
|
|
if (sql.includes('FROM quote_outcome_attributions') && sql.includes('ORDER BY COALESCE')) {
|
|
return { rows: [] };
|
|
}
|
|
if (sql.includes('INSERT INTO quote_lifecycle_stat_subjects')) {
|
|
const existing = subjects.get(params[0]);
|
|
subjects.set(params[0], {
|
|
quote_id: params[0],
|
|
first_activity_at: existing?.first_activity_at || params[1],
|
|
latest_stage_at: params[2] || existing?.latest_stage_at || null,
|
|
bucket: params[3],
|
|
lifecycle_state: params[4],
|
|
reason_code: params[5],
|
|
updated_at: params[6],
|
|
payload: JSON.parse(params[7]),
|
|
});
|
|
return { rows: [], rowCount: 1 };
|
|
}
|
|
if (sql.includes('FROM quote_lifecycle_stat_subjects')) {
|
|
return { rows: [...subjects.values()] };
|
|
}
|
|
if (sql.includes('INSERT INTO quote_lifecycle_statistics')) {
|
|
upserts.push({ sql, params });
|
|
return { rows: [], rowCount: 1 };
|
|
}
|
|
throw new Error(`unexpected query: ${sql}`);
|
|
},
|
|
};
|
|
|
|
await refreshQuoteLifecycleStatistics(pool, {
|
|
now: '2026-06-12T17:32:00.000Z',
|
|
});
|
|
includeFailure = true;
|
|
await refreshQuoteLifecycleStatistics(pool, {
|
|
now: '2026-06-12T17:37:00.000Z',
|
|
});
|
|
|
|
const fiveMinuteUpserts = upserts.filter((query) => (
|
|
query.params[1] === 'five_minute'
|
|
&& query.params[2] === '2026-06-12T17:30:00.000Z'
|
|
));
|
|
assert.equal(fiveMinuteUpserts.length, 2);
|
|
assert.equal(fiveMinuteUpserts.at(-1).params[4], 1);
|
|
const latestBuckets = JSON.parse(fiveMinuteUpserts.at(-1).params[7]);
|
|
assert.equal(latestBuckets.submission_failed, 1);
|
|
assert.equal(latestBuckets.awaiting_executor, 0);
|
|
assert.equal(
|
|
upserts.some((query) => (
|
|
query.params[1] === 'five_minute'
|
|
&& query.params[2] === '2026-06-12T17:35:00.000Z'
|
|
)),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test('postgres quote lifecycle statistics retain per-quote buckets after detail pruning', async () => {
|
|
const subjects = new Map();
|
|
const allTimeUpserts = [];
|
|
let sourceAvailable = true;
|
|
const pool = {
|
|
async query(sql, params = []) {
|
|
if (sql.includes('FROM swap_demand_events') && sql.includes('ORDER BY COALESCE')) {
|
|
return {
|
|
rows: sourceAvailable ? [{
|
|
observed_at: '2026-06-12T14:11:01.000Z',
|
|
ingested_at: '2026-06-12T14:11:01.000Z',
|
|
payload: {
|
|
quote_id: 'quote-completed',
|
|
pair: 'btc->usdc',
|
|
asset_in: 'btc',
|
|
asset_out: 'usdc',
|
|
amount_in: '1',
|
|
amount_out: '2',
|
|
},
|
|
}] : [],
|
|
};
|
|
}
|
|
if (sql.includes('FROM trade_decisions') && sql.includes('ORDER BY COALESCE')) {
|
|
return {
|
|
rows: sourceAvailable ? [{
|
|
observed_at: '2026-06-12T14:11:02.000Z',
|
|
ingested_at: '2026-06-12T14:11:02.000Z',
|
|
payload: {
|
|
quote_id: 'quote-completed',
|
|
decision_id: 'decision-completed',
|
|
pair: 'btc->usdc',
|
|
decision: 'approved',
|
|
decision_reason: 'strategy_approved',
|
|
},
|
|
}] : [],
|
|
};
|
|
}
|
|
if (sql.includes('FROM execute_trade_commands') && sql.includes('ORDER BY COALESCE')) {
|
|
return {
|
|
rows: sourceAvailable ? [{
|
|
observed_at: '2026-06-12T14:11:03.000Z',
|
|
ingested_at: '2026-06-12T14:11:03.000Z',
|
|
payload: {
|
|
quote_id: 'quote-completed',
|
|
decision_id: 'decision-completed',
|
|
command_id: 'cmd-completed',
|
|
pair: 'btc->usdc',
|
|
},
|
|
}] : [],
|
|
};
|
|
}
|
|
if (sql.includes('FROM trade_execution_results r')) {
|
|
return {
|
|
rows: sourceAvailable ? [{
|
|
result_observed_at: '2026-06-12T14:11:10.000Z',
|
|
result_ingested_at: '2026-06-12T14:11:10.000Z',
|
|
result_payload: {
|
|
quote_id: 'quote-completed',
|
|
decision_id: 'decision-completed',
|
|
command_id: 'cmd-completed',
|
|
status: 'submitted',
|
|
result_code: 'quote_response_ok',
|
|
outcome_status: 'completed',
|
|
},
|
|
command_payload: {
|
|
quote_id: 'quote-completed',
|
|
decision_id: 'decision-completed',
|
|
command_id: 'cmd-completed',
|
|
},
|
|
decision_payload: {
|
|
quote_id: 'quote-completed',
|
|
decision_id: 'decision-completed',
|
|
decision: 'approved',
|
|
},
|
|
outcome_payload: null,
|
|
}] : [],
|
|
};
|
|
}
|
|
if (sql.includes('FROM quote_outcome_attributions') && sql.includes('ORDER BY COALESCE')) {
|
|
return { rows: [] };
|
|
}
|
|
if (sql.includes('INSERT INTO quote_lifecycle_stat_subjects')) {
|
|
const existing = subjects.get(params[0]);
|
|
subjects.set(params[0], {
|
|
quote_id: params[0],
|
|
first_activity_at: existing?.first_activity_at || params[1],
|
|
latest_stage_at: params[2] || existing?.latest_stage_at || null,
|
|
bucket: params[3],
|
|
lifecycle_state: params[4],
|
|
reason_code: params[5],
|
|
updated_at: params[6],
|
|
payload: JSON.parse(params[7]),
|
|
});
|
|
return { rows: [], rowCount: 1 };
|
|
}
|
|
if (sql.includes('FROM quote_lifecycle_stat_subjects')) {
|
|
return { rows: [...subjects.values()] };
|
|
}
|
|
if (sql.includes('INSERT INTO quote_lifecycle_statistics')) {
|
|
if (params[1] === 'all_time') allTimeUpserts.push({ params });
|
|
return { rows: [], rowCount: 1 };
|
|
}
|
|
throw new Error(`unexpected query: ${sql}`);
|
|
},
|
|
};
|
|
|
|
await refreshQuoteLifecycleStatistics(pool, {
|
|
now: '2026-06-12T14:12:00.000Z',
|
|
});
|
|
sourceAvailable = false;
|
|
await refreshQuoteLifecycleStatistics(pool, {
|
|
now: '2026-06-12T15:12:00.000Z',
|
|
});
|
|
|
|
assert.equal(allTimeUpserts.length, 2);
|
|
assert.equal(allTimeUpserts[0].params[4], 1);
|
|
assert.equal(JSON.parse(allTimeUpserts[0].params[7]).success, 1);
|
|
assert.equal(allTimeUpserts[1].params[4], 1);
|
|
assert.equal(JSON.parse(allTimeUpserts[1].params[7]).success, 1);
|
|
});
|
|
|
|
test('postgres quote lifecycle statistics loader returns all-time and clamped grain rows', async () => {
|
|
const pool = {
|
|
async query(sql, params = []) {
|
|
assert.match(sql, /FROM quote_lifecycle_statistics/);
|
|
assert.deepEqual(params, [['all_time', 'five_minute'], 2]);
|
|
return {
|
|
rows: [{
|
|
stat_id: 'quote-lifecycle-stat:all_time:all',
|
|
grain: 'all_time',
|
|
window_start: null,
|
|
window_end: null,
|
|
quote_count: 3,
|
|
missing_identifier_count: 1,
|
|
missing_timestamp_count: 0,
|
|
bucket_counts: { success: 1, submission_failed: 2 },
|
|
computed_at: '2026-06-12T17:40:00.000Z',
|
|
}],
|
|
};
|
|
},
|
|
};
|
|
|
|
const rows = await loadQuoteLifecycleStatistics(pool, {
|
|
grains: ['all_time', 'five_minute'],
|
|
limit: 2,
|
|
});
|
|
|
|
assert.equal(rows.length, 1);
|
|
assert.equal(rows[0].quote_count, 3);
|
|
assert.equal(rows[0].bucket_counts.success, 1);
|
|
assert.equal(rows[0].bucket_counts.submitted_no_reply, 0);
|
|
});
|