diff --git a/src/lib/postgres.mjs b/src/lib/postgres.mjs index 71b3235..5db1f73 100644 --- a/src/lib/postgres.mjs +++ b/src/lib/postgres.mjs @@ -5932,7 +5932,7 @@ async function loadCreditedDepositRowsSince(pool, since) { AND COALESCE(payload->'details'->>'tx_hash', '') <> '' ORDER BY COALESCE(observed_at, ingested_at) DESC `, - [...CREDITED_STATUSES], + [[...CREDITED_STATUSES]], ); return normalizeDepositStatusRows(result.rows) .filter((row) => timestampValue(row.observed_at || row.ingested_at) > timestampValue(since)); diff --git a/test/postgres-funding.test.mjs b/test/postgres-funding.test.mjs index 45100b6..0aa4402 100644 --- a/test/postgres-funding.test.mjs +++ b/test/postgres-funding.test.mjs @@ -1,7 +1,7 @@ import test from 'node:test'; import assert from 'node:assert/strict'; -import { loadRecentDepositStatuses } from '../src/lib/postgres.mjs'; +import { loadPortfolioMetricInputs, loadRecentDepositStatuses } from '../src/lib/postgres.mjs'; test('recent deposit status loader keeps old bridge deposits at original effective time', async () => { const txHash = '0xe8755a069e10a5963953c847f48f2db857c7a2dfdf8d21579c4057366126dd3f'; @@ -59,6 +59,96 @@ test('recent deposit status loader uses bridge created_at when present', async ( assert.equal(deposit.payload.details.created_at, '2026-04-08T15:57:14.877Z'); }); +test('portfolio metric load binds credited deposit statuses as one pg array parameter', async () => { + const btcAsset = { + assetId: 'nep141:btc.omft.near', + symbol: 'BTC', + decimals: 8, + }; + const eureAsset = { + assetId: 'nep141:eure.omft.near', + symbol: 'EURe', + decimals: 18, + }; + + const pool = { + async query(sql, params) { + if (sql.includes('FROM liquidity_actions') && sql.includes('deposit_status_observed') && sql.includes('ANY($1)')) { + assert.equal(params.length, 1); + assert.deepEqual(params[0], ['CREDITED', 'COMPLETED', 'FINALIZED', 'SETTLED']); + return { rows: [] }; + } + + if (sql.includes('FROM liquidity_actions') && sql.includes('ORDER BY COALESCE(observed_at, ingested_at) DESC LIMIT $1')) { + assert.equal(params[0], 500); + return { rows: [] }; + } + + if (sql.includes('FROM liquidity_actions') && sql.includes('withdrawal_status_changed')) { + return { rows: [] }; + } + + if (sql.includes('FROM execute_trade_commands')) { + return { rows: [{ first_command_at: '2026-06-01T12:00:00.000Z', command_count: 1 }] }; + } + + if (sql.includes('FROM trade_execution_results')) { + return { rows: [{ result_count: 0 }] }; + } + + if (sql.includes('FROM intent_inventory_snapshots')) { + return { + rows: [{ + ingested_at: '2026-06-01T12:00:00.000Z', + payload: { + inventory_id: 'inv-1', + synced_at: '2026-06-01T12:00:00.000Z', + spendable: {}, + }, + }], + }; + } + + if (sql.includes('FROM market_price_events') && sql.includes("COALESCE(payload->>'eure_per_btc', '') <> ''")) { + return { + rows: [{ + ingested_at: '2026-06-01T12:00:00.000Z', + payload: { + price_id: 'current-price', + eure_per_btc: '60000.00', + }, + }], + }; + } + + if (sql.includes('SELECT DISTINCT ON (payload->>\'price_route_id\')')) { + return { rows: [] }; + } + + if (sql.includes('FROM market_price_events') && sql.includes('WHERE ingested_at <= $1')) { + return { + rows: [{ + ingested_at: '2026-06-01T11:00:00.000Z', + payload: { + price_id: 'baseline-price', + eure_per_btc: '60000.00', + }, + }], + }; + } + + assert.fail(`unexpected query: ${sql}`); + }, + }; + + const inputs = await loadPortfolioMetricInputs(pool, { + btcAsset, + eureAsset, + }); + + assert.equal(inputs.baseline.anchor, 'latest_inventory_before_first_command'); +}); + function depositRow({ txHash, status,