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.
57 lines
2.5 KiB
TypeScript
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,
|
|
);
|
|
});
|
|
});
|