import test from 'node:test'; import assert from 'node:assert/strict'; import { buildInventorySnapshot } from '../src/core/inventory.mjs'; import { routeHistoryRecord } from '../src/core/history-records.mjs'; import { insertHistoryEvents, pruneNonSuccessfulQuoteLifecycleHistory, pruneRawNearIntentsQuoteHistory, } from '../src/lib/postgres.mjs'; test('inventory snapshot keeps pending funding out of spendable balances', () => { const snapshot = buildInventorySnapshot({ accountId: 'solver.near', balances: { 'nep141:btc.omft.near': '1000', 'nep141:eure.omft.near': '2000', }, recentDeposits: [ { asset_id: 'nep141:eure.omft.near', amount: '300', status: 'PENDING', }, ], trackedWithdrawals: [ { withdrawal_hash: 'wd-1', asset_id: 'nep141:btc.omft.near', amount: '25', status: 'PENDING', }, ], assetRegistry: new Map([ ['nep141:btc.omft.near', { decimals: 8 }], ['nep141:eure.omft.near', { decimals: 18 }], ]), observedAt: '2026-04-02T10:00:00.000Z', }); assert.equal(snapshot.spendable['nep141:eure.omft.near'], '2000'); assert.equal(snapshot.pending_inbound['nep141:eure.omft.near'], '300'); assert.equal(snapshot.pending_outbound['nep141:btc.omft.near'], '25'); }); test('history writer routes decision events into the decision table family', () => { const routed = routeHistoryRecord({ topic: 'decision.trade_decision', event: { event_id: 'evt-1', event_type: 'trade_decision', venue: 'near-intents', schema_version: 1, ingested_at: '2026-04-02T10:00:00.000Z', payload: { decision_id: 'decision-1', quote_id: 'quote-1', pair: 'a->b', direction: 'btc_to_eure', decision: 'rejected', decision_reason: 'strategy_disarmed', }, }, }); assert.equal(routed.table, 'trade_decisions'); assert.equal(routed.record.decision_key, 'decision-1'); assert.equal(routed.record.quote_id, 'quote-1'); }); test('bulk history insert writes multiple routed events in one table statement', async () => { const queries = []; const pool = { async query(sql, params) { queries.push({ sql, params }); return { rows: [ { event_id: params[0] }, { event_id: params[12] }, ], rowCount: 2, }; }, }; const result = await insertHistoryEvents(pool, [ { table: 'trade_decisions', topic: 'decision.trade_decision', event: historyEvent('evt-1', { decision_id: 'decision-1', quote_id: 'quote-1' }), record: { quote_id: 'quote-1', pair: 'a->b', decision_key: 'decision-1' }, }, { table: 'trade_decisions', topic: 'decision.trade_decision', event: historyEvent('evt-2', { decision_id: 'decision-2', quote_id: 'quote-2' }), record: { quote_id: 'quote-2', pair: 'a->b', decision_key: 'decision-2' }, }, ]); assert.equal(queries.length, 1); assert.match(queries[0].sql, /INSERT INTO trade_decisions/); assert.match(queries[0].sql, /RETURNING event_id/); assert.equal(queries[0].params.length, 24); assert.deepEqual([...result.insertedEventIds].sort(), ['evt-1', 'evt-2']); }); test('raw quote retention preserves rows linked to maker lifecycle evidence', async () => { const queries = []; const pool = { async query(sql, params) { queries.push({ sql, params }); return { rows: [], rowCount: 42 }; }, }; const result = await pruneRawNearIntentsQuoteHistory(pool, { now: '2026-05-21T12:00:00.000Z', retainRecentMs: 6 * 60 * 60 * 1000, batchSize: 123, }); assert.equal(result.deletedCount, 42); assert.equal(result.cutoff, '2026-05-21T06:00:00.000Z'); assert.equal(result.batchSize, 123); assert.equal(queries.length, 1); assert.match(queries[0].sql, /DELETE FROM raw_near_intents_quotes/); assert.match(queries[0].sql, /trade_decisions/); assert.match(queries[0].sql, /execute_trade_commands/); assert.match(queries[0].sql, /trade_execution_results/); assert.match(queries[0].sql, /quote_outcome_attributions/); assert.deepEqual(queries[0].params, ['2026-05-21T06:00:00.000Z', 123]); }); test('raw quote retention drains multiple bounded batches when firehose backlog exceeds one batch', async () => { const rowCounts = [500_000, 500_000, 12]; const queries = []; const pool = { async query(sql, params) { queries.push({ sql, params }); return { rows: [], rowCount: rowCounts.shift() ?? 0 }; }, }; const result = await pruneRawNearIntentsQuoteHistory(pool, { now: '2026-05-21T12:00:00.000Z', retainRecentMs: 30 * 60 * 1000, batchSize: 500_000, maxBatches: 20, }); assert.equal(result.deletedCount, 1_000_012); assert.equal(result.cutoff, '2026-05-21T11:30:00.000Z'); assert.equal(result.batchSize, 500_000); assert.equal(result.maxBatches, 20); assert.equal(result.batches, 3); assert.equal(queries.length, 3); for (const query of queries) { assert.match(query.sql, /DELETE FROM raw_near_intents_quotes/); assert.deepEqual(query.params, ['2026-05-21T11:30:00.000Z', 500_000]); } }); test('quote lifecycle retention preserves completed trades and drains old non-success rows', async () => { const rowCounts = [100_000, 12, 7, 4, 3, 2, 1]; const queries = []; const pool = { async query(sql, params) { queries.push({ sql, params }); return { rows: [], rowCount: rowCounts.shift() ?? 0 }; }, }; const result = await pruneNonSuccessfulQuoteLifecycleHistory(pool, { now: '2026-06-05T12:00:00.000Z', retainRecentMs: 30 * 60 * 1000, batchSize: 100_000, maxBatches: 10, }); assert.equal(result.deletedCount, 100_029); assert.equal(result.cutoff, '2026-06-05T11:30:00.000Z'); assert.equal(result.batchSize, 100_000); assert.equal(result.maxBatches, 10); assert.deepEqual(result.tables.map((entry) => entry.table), [ 'raw_near_intents_quotes', 'swap_demand_events', 'trade_decisions', 'execute_trade_commands', 'trade_execution_results', 'quote_outcome_attributions', ]); assert.equal(queries.length, 7); for (const query of queries) { assert.match(query.sql, /DELETE FROM/); assert.match(query.sql, /outcome_status = 'completed'/); assert.match(query.sql, /attribution_status IN \('heuristic_match', 'linked_settlement', 'exact_match', 'attributed'\)/); assert.deepEqual(query.params, ['2026-06-05T11:30:00.000Z', 100_000]); } assert.match(queries.at(-1).sql, /DELETE FROM quote_outcome_attributions/); }); function historyEvent(eventId, payload) { return { event_id: eventId, event_type: 'trade_decision', venue: 'near-intents', source: 'strategy-engine', observed_at: '2026-04-02T10:00:00.000Z', ingested_at: '2026-04-02T10:00:00.001Z', payload, }; }