Fix credited status array binding in portfolio metric loader
All checks were successful
deploy / deploy (push) Successful in 1m13s
All checks were successful
deploy / deploy (push) Successful in 1m13s
Proof: stop history-writer portfolio metrics refresh from failing SQL ANY() binding errors so command/result persistence path is unblocked and trading can proceed. Assumptions: live topic flow is healthy, and the blocker is the SQL binding mismatch, not strategy guardrails. Still fake: no live DB proof or regression harness with real market/run data; no strategy behavior changes, only portfolio-metrics refresh plumbing.
This commit is contained in:
parent
cb7cb77a23
commit
68b86353eb
2 changed files with 92 additions and 2 deletions
|
|
@ -5932,7 +5932,7 @@ async function loadCreditedDepositRowsSince(pool, since) {
|
||||||
AND COALESCE(payload->'details'->>'tx_hash', '') <> ''
|
AND COALESCE(payload->'details'->>'tx_hash', '') <> ''
|
||||||
ORDER BY COALESCE(observed_at, ingested_at) DESC
|
ORDER BY COALESCE(observed_at, ingested_at) DESC
|
||||||
`,
|
`,
|
||||||
[...CREDITED_STATUSES],
|
[[...CREDITED_STATUSES]],
|
||||||
);
|
);
|
||||||
return normalizeDepositStatusRows(result.rows)
|
return normalizeDepositStatusRows(result.rows)
|
||||||
.filter((row) => timestampValue(row.observed_at || row.ingested_at) > timestampValue(since));
|
.filter((row) => timestampValue(row.observed_at || row.ingested_at) > timestampValue(since));
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import test from 'node:test';
|
import test from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
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 () => {
|
test('recent deposit status loader keeps old bridge deposits at original effective time', async () => {
|
||||||
const txHash = '0xe8755a069e10a5963953c847f48f2db857c7a2dfdf8d21579c4057366126dd3f';
|
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');
|
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({
|
function depositRow({
|
||||||
txHash,
|
txHash,
|
||||||
status,
|
status,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue