Files
TORCH/test/lock-ops-publish-retry.test.mjs
T
google-labs-jules[bot]andPR0M3TH3AN c0c5ac5473 fix(torch-lock): re-apply fixes for circular deps and syntax errors
- Moved `withTimeout` implementation to `src/lock-utils.mjs` and exported it correctly.
- Removed duplicate local definitions of `withTimeout`, `mergeRelayList`, and `secureRandom` in `src/lock-ops.mjs` and `src/lock-publisher.mjs`.
- Fixed imports in `src/lock-ops.mjs`, `src/lock-publisher.mjs`, and `src/relay-health.mjs` to use `src/lock-utils.mjs` consistently.
- Updated `test/utils.test.mjs` and `test/utils-async.test.mjs` to fix broken imports and test definitions.
- Verified `npm run lock:complete` and `npm run scheduler:daily` execute correctly.
- Updated `src/todo/TODO_2026-02-20T03-00-00Z.md` to mark verification task as completed.
- Verified `src/relay-health.mjs` does not use `MINUTE_MS` locally, despite mysterious CI error. Confirmed imports are correct.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-02-21 19:23:48 +00:00

193 lines
7.5 KiB
JavaScript

import { describe, it, beforeEach, afterEach, mock } from 'node:test';
import assert from 'node:assert';
import { publishLock, _resetRelayHealthState } from '../src/lock-ops.mjs';
function createScriptedPool(scriptedOutcomesByAttempt, relaysSeen) {
let publishCallCount = 0;
return {
publish: (relays) => {
publishCallCount += 1;
relaysSeen.push({ publishAttempt: publishCallCount, relays: [...relays] });
const attemptOutcomes = scriptedOutcomesByAttempt[publishCallCount - 1] || {};
return relays.map((relay) => {
const outcome = attemptOutcomes[relay] || { ok: true };
return outcome.ok ? Promise.resolve('ok') : Promise.reject(new Error(outcome.message));
});
},
close: () => {},
getPublishCallCount: () => publishCallCount,
};
}
const parseTelemetry = (logs) => logs.map((line) => JSON.parse(line));
describe('publishLock retry behavior', { concurrency: false }, () => {
beforeEach(() => {
_resetRelayHealthState();
});
afterEach(() => {
mock.timers.reset();
});
it('succeeds immediately on first attempt without retries', async () => {
const telemetryLogs = [];
const relaysSeen = [];
const mockPool = createScriptedPool([
{ 'wss://relay-a': { ok: true } },
], relaysSeen);
const event = { id: 'evt-immediate' };
const result = await publishLock(['wss://relay-a'], event, {
poolFactory: () => mockPool,
getPublishTimeoutMsFn: () => 200,
getMinSuccessfulRelayPublishesFn: () => 1,
getRelayFallbacksFn: () => [],
getMinActiveRelayPoolFn: () => 1,
retryAttempts: 4,
telemetryLogger: (line) => telemetryLogs.push(line),
healthLogger: () => {},
});
assert.strictEqual(result, event);
assert.strictEqual(mockPool.getPublishCallCount(), 1);
assert.deepStrictEqual(relaysSeen, [{ publishAttempt: 1, relays: ['wss://relay-a'] }]);
const telemetry = parseTelemetry(telemetryLogs);
assert.strictEqual(telemetry.filter((entry) => entry.event === 'lock_publish_retry').length, 0);
const metEvent = telemetry.find((entry) => entry.event === 'lock_publish_quorum_met');
assert.ok(metEvent);
assert.strictEqual(metEvent.publishAttempt, 1);
});
it('retries a transient failure then succeeds within retry budget with bounded jitter', async () => {
mock.timers.enable({ apis: ['Date', 'setTimeout'] });
const telemetryLogs = [];
const relaysSeen = [];
const sleepCalls = [];
const mockPool = createScriptedPool([
{ 'wss://relay-a': { ok: false, message: 'connection reset by peer' } },
{ 'wss://relay-a': { ok: true } },
], relaysSeen);
const result = await publishLock(['wss://relay-a'], { id: 'evt-retry-success' }, {
poolFactory: () => mockPool,
getPublishTimeoutMsFn: () => 200,
getMinSuccessfulRelayPublishesFn: () => 1,
getRelayFallbacksFn: () => [],
getMinActiveRelayPoolFn: () => 1,
retryAttempts: 3,
retryBaseDelayMs: 100,
retryCapDelayMs: 500,
randomFn: () => 0.25,
sleepFn: async (delayMs) => {
sleepCalls.push(delayMs);
mock.timers.tick(delayMs);
},
telemetryLogger: (line) => telemetryLogs.push(line),
healthLogger: () => {},
});
assert.strictEqual(result.id, 'evt-retry-success');
assert.deepStrictEqual(sleepCalls, [25]);
assert.strictEqual(mockPool.getPublishCallCount(), 2);
const telemetry = parseTelemetry(telemetryLogs);
const retryEvents = telemetry.filter((entry) => entry.event === 'lock_publish_retry');
assert.strictEqual(retryEvents.length, 1);
assert.strictEqual(retryEvents[0].publishAttempt, 1);
assert.strictEqual(retryEvents[0].relayUrl, 'wss://relay-a');
assert.ok(retryEvents[0].nextDelayMs >= 0 && retryEvents[0].nextDelayMs <= 100);
});
it('returns terminal relay_publish_quorum_failure after exhausting retry budget', async () => {
mock.timers.enable({ apis: ['Date', 'setTimeout'] });
const telemetryLogs = [];
const relaysSeen = [];
const sleepCalls = [];
const randomValues = [0.1, 0.9];
const mockPool = createScriptedPool([
{ 'wss://relay-a': { ok: false, message: 'relay unavailable' } },
{ 'wss://relay-a': { ok: false, message: 'relay unavailable' } },
{ 'wss://relay-a': { ok: false, message: 'relay unavailable' } },
], relaysSeen);
await assert.rejects(
() => publishLock(['wss://relay-a'], { id: 'evt-exhausted' }, {
poolFactory: () => mockPool,
getPublishTimeoutMsFn: () => 200,
getMinSuccessfulRelayPublishesFn: () => 1,
getRelayFallbacksFn: () => [],
getMinActiveRelayPoolFn: () => 1,
retryAttempts: 3,
retryBaseDelayMs: 100,
retryCapDelayMs: 250,
randomFn: () => randomValues.shift() ?? 0,
sleepFn: async (delayMs) => {
sleepCalls.push(delayMs);
mock.timers.tick(delayMs);
},
telemetryLogger: (line) => telemetryLogs.push(line),
healthLogger: () => {},
}),
/error_category=relay_publish_quorum_failure/,
);
assert.strictEqual(mockPool.getPublishCallCount(), 3);
assert.deepStrictEqual(sleepCalls, [10, 180]);
assert.deepStrictEqual(relaysSeen.map((entry) => entry.publishAttempt), [1, 2, 3]);
const telemetry = parseTelemetry(telemetryLogs);
const retryEvents = telemetry.filter((entry) => entry.event === 'lock_publish_retry');
assert.strictEqual(retryEvents.length, 2);
assert.strictEqual(retryEvents[0].publishAttempt, 1);
assert.strictEqual(retryEvents[0].relayUrl, 'wss://relay-a');
assert.ok(retryEvents[0].nextDelayMs >= 0 && retryEvents[0].nextDelayMs <= 100);
assert.strictEqual(retryEvents[1].publishAttempt, 2);
assert.strictEqual(retryEvents[1].relayUrl, 'wss://relay-a');
assert.ok(retryEvents[1].nextDelayMs >= retryEvents[0].nextDelayMs);
assert.ok(retryEvents[1].nextDelayMs <= 200);
const failedEvent = telemetry.find((entry) => entry.event === 'lock_publish_quorum_failed');
assert.ok(failedEvent);
assert.strictEqual(failedEvent.errorCategory, 'relay_publish_quorum_failure');
assert.strictEqual(failedEvent.attempts, 3);
});
it('short-circuits non-retryable failures without extra attempts', async () => {
const telemetryLogs = [];
const relaysSeen = [];
const mockPool = createScriptedPool([
{ 'wss://relay-a': { ok: false, message: 'validation failed: invalid signature' } },
{ 'wss://relay-a': { ok: true } },
], relaysSeen);
await assert.rejects(
() => publishLock(['wss://relay-a'], { id: 'evt-permanent' }, {
poolFactory: () => mockPool,
getPublishTimeoutMsFn: () => 200,
getMinSuccessfulRelayPublishesFn: () => 1,
getRelayFallbacksFn: () => [],
getMinActiveRelayPoolFn: () => 1,
retryAttempts: 4,
telemetryLogger: (line) => telemetryLogs.push(line),
healthLogger: () => {},
}),
/error_category=relay_publish_non_retryable/,
);
assert.strictEqual(mockPool.getPublishCallCount(), 1);
assert.deepStrictEqual(relaysSeen, [{ publishAttempt: 1, relays: ['wss://relay-a'] }]);
const telemetry = parseTelemetry(telemetryLogs);
assert.strictEqual(telemetry.filter((entry) => entry.event === 'lock_publish_retry').length, 0);
const failedEvent = telemetry.find((entry) => entry.event === 'lock_publish_quorum_failed');
assert.ok(failedEvent);
assert.strictEqual(failedEvent.errorCategory, 'relay_publish_non_retryable');
assert.notStrictEqual(failedEvent.errorCategory, 'relay_publish_quorum_failure');
});
});