Files
apex-mcp-server/packages/shared/test/provider-confirmation.test.ts
FilipBl4gojevic ab21f32ca9 fix(builder): prefer Koios tx_status for await_transaction, stop swallowing check errors
vector_await_transaction's confirmation check only ever looked for an
unspent output at index 0 - a heuristic that can read a landed transaction
as unconfirmed, since these tools broadcast CBOR signed elsewhere with no
guarantee of that output existing or staying unspent. The check also wrapped
the lookup in .catch(() => []), collapsing genuine infrastructure failures
(Ogmios down, malformed response) into the same "not confirmed yet" result,
defeating pollUntilConfirmed's documented contract that a check rejection
should propagate.

OgmiosProvider.getKoiosTxStatus + koiosIndicatesConfirmed (shared/provider.ts)
now provide a direct, non-swallowing Koios tx_status check; poll.ts's new
buildConfirmationCheck picks it when VECTOR_KOIOS_URL is configured and
falls back to the outRef heuristic (also no longer swallowing errors)
otherwise - the same preference order OgmiosProvider.awaitTx already used,
without reusing awaitTx itself (still not caller-configurable, still
swallows every failure). awaitTx stays in place only because the Provider
interface requires it; nothing else calls it.

buildConfirmationCheck and koiosIndicatesConfirmed are unit tested via
dependency injection, the same pattern poll.test.ts already uses for
now/sleep, with no network access required.
2026-07-29 08:08:26 +02:00

57 lines
2.5 KiB
TypeScript

import { describe, test } from 'node:test';
import assert from 'node:assert/strict';
import { koiosIndicatesConfirmed } from '../src/provider.ts';
// koiosIndicatesConfirmed only ever sees a body that Koios already answered
// with an OK response - getKoiosTxStatus throws before this runs for a
// network failure or a non-2xx status. So every case here is "reachable and
// answered", and the only question is whether that answer means confirmed.
describe('koiosIndicatesConfirmed', () => {
test('a positive confirmation count means confirmed', () => {
assert.equal(koiosIndicatesConfirmed([{ tx_hash: 'a'.repeat(64), num_confirmations: 1 }]), true);
});
test('a larger confirmation count still means confirmed', () => {
assert.equal(koiosIndicatesConfirmed([{ tx_hash: 'a'.repeat(64), num_confirmations: 42 }]), true);
});
test('an empty array (Koios has not indexed the hash yet) means not confirmed', () => {
assert.equal(koiosIndicatesConfirmed([]), false);
});
test('zero confirmations means not confirmed', () => {
assert.equal(koiosIndicatesConfirmed([{ tx_hash: 'a'.repeat(64), num_confirmations: 0 }]), false);
});
test('a missing num_confirmations field means not confirmed, not an error', () => {
assert.equal(koiosIndicatesConfirmed([{ tx_hash: 'a'.repeat(64) }]), false);
});
test('a non-array body means not confirmed, not an error', () => {
assert.equal(koiosIndicatesConfirmed(null), false);
assert.equal(koiosIndicatesConfirmed(undefined), false);
assert.equal(koiosIndicatesConfirmed({}), false);
assert.equal(koiosIndicatesConfirmed('unexpected'), false);
});
test('a num_confirmations that is not a number means not confirmed, not an error', () => {
// Defensive: guards against a future Koios response shape change (e.g. a
// stringified count) silently being coerced by a loose truthiness check
// instead of being treated as "cannot tell, so say not yet".
assert.equal(koiosIndicatesConfirmed([{ num_confirmations: '3' }]), false);
assert.equal(koiosIndicatesConfirmed([{ num_confirmations: null }]), false);
});
test('a negative confirmation count means not confirmed', () => {
assert.equal(koiosIndicatesConfirmed([{ num_confirmations: -1 }]), false);
});
test('only the first entry is consulted, matching the single-hash query this is always called with', () => {
assert.equal(
koiosIndicatesConfirmed([{ num_confirmations: 0 }, { num_confirmations: 5 }]),
false,
);
});
});