All checks were successful
deploy / deploy (push) Successful in 58s
Proof: quote-response execution now uses verifierSaltCache.getCachedFreshSalt() only, rejects verifier_salt_unavailable before signing or relay when no fresh cached salt exists, preserves stale command expiry before salt lookup, exposes salt cache and executor queue-delay state in health/sentinel/dashboard surfaces, and is covered by targeted regression tests plus full npm test and dashboard build. Assumptions: the existing verifier salt freshness policy remains safe for cached signing use; fixed in-code queue-delay warning thresholds are sufficient for operator alerts without new latency env vars; no strategy selection, pricing, edge, notional, inventory, arming, signer, or pair enablement behavior changed. Still fake: venue-native terminal fill ids and fee-complete realized PnL remain unavailable; executor concurrency remains single-consumer; live post-deploy evidence still needs collection after repo-workflow deployment.
151 lines
4.4 KiB
JavaScript
151 lines
4.4 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
|
|
import { createVerifierSaltCache } from '../src/core/verifier-salt-cache.mjs';
|
|
|
|
test('verifier salt cache cached fresh read returns without calling the verifier loader', async () => {
|
|
let nowMs = 1_000;
|
|
let calls = 0;
|
|
const cache = createVerifierSaltCache({
|
|
loadSalt: async () => {
|
|
calls += 1;
|
|
return '252812b3';
|
|
},
|
|
maxAgeMs: 500,
|
|
now: () => nowMs,
|
|
});
|
|
|
|
const refreshed = await cache.refreshNow();
|
|
nowMs += 100;
|
|
const cached = cache.getCachedFreshSalt();
|
|
|
|
assert.equal(calls, 1);
|
|
assert.equal(refreshed.source, 'refresh');
|
|
assert.equal(cached.source, 'cache');
|
|
assert.equal(cached.available, true);
|
|
assert.equal(cached.currentSaltHex, '252812b3');
|
|
assert.equal(cached.ageMs, 100);
|
|
assert.equal(cache.getState().salt_fresh, true);
|
|
});
|
|
|
|
test('verifier salt cache missing or stale cached read returns unavailable without loader call', async () => {
|
|
let nowMs = 1_000;
|
|
let calls = 0;
|
|
const cache = createVerifierSaltCache({
|
|
loadSalt: async () => {
|
|
calls += 1;
|
|
return '252812b3';
|
|
},
|
|
maxAgeMs: 500,
|
|
now: () => nowMs,
|
|
});
|
|
|
|
const missing = cache.getCachedFreshSalt();
|
|
assert.equal(missing.available, false);
|
|
assert.equal(missing.reason, 'empty_cache');
|
|
assert.equal(calls, 0);
|
|
|
|
await cache.refreshNow();
|
|
nowMs += 501;
|
|
const stale = cache.getCachedFreshSalt();
|
|
|
|
assert.equal(stale.available, false);
|
|
assert.equal(stale.reason, 'stale_cache');
|
|
assert.equal(stale.ageMs, 501);
|
|
assert.equal(calls, 1);
|
|
assert.equal(cache.getState().salt_fresh, false);
|
|
});
|
|
|
|
test('verifier salt cache cached read does not wait for slow background current_salt refresh', async () => {
|
|
let calls = 0;
|
|
let resolveSalt;
|
|
const slowSalt = new Promise((resolve) => {
|
|
resolveSalt = resolve;
|
|
});
|
|
const cache = createVerifierSaltCache({
|
|
loadSalt: async () => {
|
|
calls += 1;
|
|
return slowSalt;
|
|
},
|
|
setIntervalFn: () => ({ unref() {} }),
|
|
clearIntervalFn: () => {},
|
|
});
|
|
|
|
cache.start();
|
|
await Promise.resolve();
|
|
|
|
const cached = cache.getCachedFreshSalt();
|
|
assert.equal(cached.available, false);
|
|
assert.equal(cached.reason, 'empty_cache');
|
|
assert.equal(cached.state.refresh_in_flight, true);
|
|
assert.equal(calls, 1);
|
|
|
|
resolveSalt('252812b3');
|
|
await cache.refreshNow();
|
|
assert.equal(cache.getCachedFreshSalt().available, true);
|
|
});
|
|
|
|
test('verifier salt cache background refresh records success duration and state', async () => {
|
|
let nowMs = 1_000;
|
|
const cache = createVerifierSaltCache({
|
|
loadSalt: async () => {
|
|
nowMs += 37;
|
|
return '252812b3';
|
|
},
|
|
maxAgeMs: 500,
|
|
now: () => nowMs,
|
|
});
|
|
|
|
const refreshed = await cache.refreshNow({ reason: 'unit_test' });
|
|
const state = cache.getState();
|
|
|
|
assert.equal(refreshed.currentSaltHex, '252812b3');
|
|
assert.equal(state.salt_fresh, true);
|
|
assert.equal(state.last_refresh_started_at, '1970-01-01T00:00:01.000Z');
|
|
assert.equal(state.last_refresh_completed_at, '1970-01-01T00:00:01.037Z');
|
|
assert.equal(state.last_refresh_duration_ms, 37);
|
|
assert.equal(state.last_refresh_reason, 'unit_test');
|
|
assert.equal(state.refresh_in_flight, false);
|
|
assert.equal(state.last_refresh_error, null);
|
|
});
|
|
|
|
test('verifier salt cache refresh failure preserves still-fresh cached salt', async () => {
|
|
let nowMs = 1_000;
|
|
let fail = false;
|
|
const cache = createVerifierSaltCache({
|
|
loadSalt: async () => {
|
|
if (fail) throw new Error('rpc unavailable');
|
|
return '252812b3';
|
|
},
|
|
maxAgeMs: 500,
|
|
now: () => nowMs,
|
|
});
|
|
|
|
await cache.refreshNow();
|
|
nowMs += 100;
|
|
fail = true;
|
|
|
|
await assert.rejects(
|
|
() => cache.refreshNow({ reason: 'prefetch' }),
|
|
/rpc unavailable/,
|
|
);
|
|
const cached = cache.getCachedFreshSalt();
|
|
assert.equal(cached.available, true);
|
|
assert.equal(cached.currentSaltHex, '252812b3');
|
|
assert.equal(cached.ageMs, 100);
|
|
assert.equal(cache.getState().fresh, true);
|
|
assert.equal(cache.getState().last_refresh_error.message, 'rpc unavailable');
|
|
});
|
|
|
|
test('verifier salt cache rejects malformed salts before signing can use them', async () => {
|
|
const cache = createVerifierSaltCache({
|
|
loadSalt: async () => 'not-a-salt',
|
|
});
|
|
|
|
await assert.rejects(
|
|
() => cache.refreshNow(),
|
|
/current_salt must be 4 bytes in hex/,
|
|
);
|
|
assert.equal(cache.getState().has_cached_salt, false);
|
|
assert.equal(cache.getCachedFreshSalt().available, false);
|
|
});
|