mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
148 lines
3.8 KiB
JavaScript
148 lines
3.8 KiB
JavaScript
require('ts-node/register/transpile-only');
|
|
|
|
const assert = require('node:assert/strict');
|
|
const { test } = require('node:test');
|
|
const {
|
|
PrimaryCancellationSupervisor,
|
|
} = require('../../back/runtime/application/primaryCancellationSupervisor');
|
|
|
|
function summary(overrides = {}) {
|
|
return {
|
|
scanned: 1,
|
|
claimed: 1,
|
|
terminationRequested: 1,
|
|
alreadyExited: 0,
|
|
pending: 0,
|
|
ambiguous: 0,
|
|
blocked: 0,
|
|
deferred: 0,
|
|
alreadyResolved: 0,
|
|
notEligible: 0,
|
|
failed: 0,
|
|
truncated: false,
|
|
unsafeAttemptOverflow: false,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test('paginates a bounded cancellation recovery cycle and aggregates results', async () => {
|
|
const calls = [];
|
|
const pages = [
|
|
summary({
|
|
scanned: 2,
|
|
claimed: 1,
|
|
pending: 1,
|
|
truncated: true,
|
|
nextCursor: { requestedAtMs: 100, runId: 'run-2' },
|
|
}),
|
|
summary({
|
|
scanned: 2,
|
|
claimed: 2,
|
|
terminationRequested: 0,
|
|
alreadyExited: 1,
|
|
blocked: 1,
|
|
}),
|
|
];
|
|
const supervisor = new PrimaryCancellationSupervisor({
|
|
async dispatchBatch(options) {
|
|
calls.push(options);
|
|
return pages.shift();
|
|
},
|
|
});
|
|
|
|
const result = await supervisor.runCycle({ pageSize: 2, maxPages: 4 });
|
|
assert.deepEqual(calls, [
|
|
{ limit: 2 },
|
|
{ cursor: { requestedAtMs: 100, runId: 'run-2' }, limit: 2 },
|
|
]);
|
|
assert.deepEqual(result, {
|
|
pages: 2,
|
|
scanned: 4,
|
|
claimed: 3,
|
|
terminationRequested: 1,
|
|
alreadyExited: 1,
|
|
pending: 1,
|
|
ambiguous: 0,
|
|
blocked: 1,
|
|
deferred: 0,
|
|
alreadyResolved: 0,
|
|
notEligible: 0,
|
|
failed: 0,
|
|
stopReason: 'complete',
|
|
remaining: false,
|
|
});
|
|
});
|
|
|
|
test('stops at the page limit and exposes a resume cursor', async () => {
|
|
let page = 0;
|
|
const supervisor = new PrimaryCancellationSupervisor({
|
|
async dispatchBatch() {
|
|
page += 1;
|
|
return summary({
|
|
truncated: true,
|
|
nextCursor: { requestedAtMs: 100 + page, runId: `run-${page}` },
|
|
});
|
|
},
|
|
});
|
|
|
|
const result = await supervisor.runCycle({ pageSize: 1, maxPages: 2 });
|
|
assert.equal(result.pages, 2);
|
|
assert.equal(result.stopReason, 'page_limit');
|
|
assert.equal(result.remaining, true);
|
|
assert.deepEqual(result.nextCursor, {
|
|
requestedAtMs: 102,
|
|
runId: 'run-2',
|
|
});
|
|
|
|
const resumedCalls = [];
|
|
const resumed = new PrimaryCancellationSupervisor({
|
|
async dispatchBatch(options) {
|
|
resumedCalls.push(options);
|
|
return summary();
|
|
},
|
|
});
|
|
await resumed.runCycle({ cursor: result.nextCursor, pageSize: 1 });
|
|
assert.deepEqual(resumedCalls, [
|
|
{
|
|
cursor: { requestedAtMs: 102, runId: 'run-2' },
|
|
limit: 1,
|
|
},
|
|
]);
|
|
});
|
|
|
|
test('fails closed when attempt rows overflow or pagination cannot advance', async () => {
|
|
const unsafe = new PrimaryCancellationSupervisor({
|
|
async dispatchBatch() {
|
|
return summary({
|
|
scanned: 0,
|
|
claimed: 0,
|
|
terminationRequested: 0,
|
|
truncated: true,
|
|
unsafeAttemptOverflow: true,
|
|
});
|
|
},
|
|
});
|
|
const unsafeResult = await unsafe.runCycle();
|
|
assert.equal(unsafeResult.stopReason, 'unsafe_attempt_overflow');
|
|
assert.equal(unsafeResult.remaining, true);
|
|
|
|
const stalled = new PrimaryCancellationSupervisor({
|
|
async dispatchBatch() {
|
|
return summary({ truncated: true });
|
|
},
|
|
});
|
|
const stalledResult = await stalled.runCycle();
|
|
assert.equal(stalledResult.stopReason, 'cursor_stalled');
|
|
assert.equal(stalledResult.remaining, true);
|
|
});
|
|
|
|
test('rejects unbounded cycle settings', async () => {
|
|
const supervisor = new PrimaryCancellationSupervisor({
|
|
async dispatchBatch() {
|
|
throw new Error('must not run');
|
|
},
|
|
});
|
|
await assert.rejects(supervisor.runCycle({ pageSize: 65 }), RangeError);
|
|
await assert.rejects(supervisor.runCycle({ maxPages: 65 }), RangeError);
|
|
});
|