Files
stack/packages/tasks/tests/processing.test.mjs
rocko 4bdc7e0e86 processing: inspectable per-run stop seam for lifecycle fire
Unify halt and fire cancellation by verified run identity with durable first-owner receipts and shared deadlines. Integrate the instance control with the current host, library and tester-socket paths. Focused nine-file evidence is 62 passing tests; broad suites, mutants and review remain pending.
2026-10-21 22:42:12 -05:00

54 lines
3.6 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { world } from './world.mjs';
import { tasksAdapter } from '../src/adapter.mjs';
import { createProcessing } from '../../processing-control/src/coordinator.mjs';
import { transition } from '../../processing-control/src/state.mjs';
const actor = { business: 'demo', human: 'jason', halted: true };
test('halted tracker startup makes no fetch and a later clear permits its ordinary startup', async t => {
const w = await world(t), processing = createProcessing({ dataRoot: w.root });
let calls = 0;
const adapter = await tasksAdapter({ trackers: { demo: { baseUrl: 'http://vikunja.test', project: w.project } },
autostart: false, fetch: (...args) => { calls++; return w.fake.fetch(...args); }, log: () => {} })
({ broker: w.broker, credentials: w.credentials, businesses: w.businesses, processing });
t.after(async () => { await adapter.close(); await processing.close(); });
const halted = transition(w.root, actor);
await assert.rejects(adapter.start('demo'), e => e.code === 'processing-halted');
assert.equal(calls, 0);
transition(w.root, { ...actor, halted: false, generation: halted.generation });
await adapter.start('demo'); assert.ok(calls > 0); assert.equal(adapter.status()[0].state, 'ready');
transition(w.root, actor); calls = 0;
await assert.rejects(adapter.tick('demo'), e => e.code === 'processing-halted');
await assert.rejects(adapter.reconcile('demo'), e => e.code === 'processing-halted');
assert.equal(calls, 0);
});
test('halt aborts the tracker HTTP request already in progress', async t => {
const w = await world(t), processing = createProcessing({ dataRoot: w.root });
let ready, aborted = false;
const started = new Promise(resolve => { ready = resolve; });
const adapter = await tasksAdapter({ trackers: { demo: { baseUrl: 'http://vikunja.test', project: w.project } },
autostart: false, log: () => {}, fetch: (_url, init) => new Promise((_resolve, reject) => {
init.signal.addEventListener('abort', () => { aborted = true; reject(Error('fixture abort')); }, { once: true }); ready();
}) })({ broker: w.broker, credentials: w.credentials, businesses: w.businesses, processing });
t.after(async () => { await adapter.close(); await processing.close(); });
const run = adapter.start('demo'); await started; const began = Date.now(); transition(w.root, actor);
await run.catch(() => {}); assert.equal(aborted, true);
assert.ok(Date.now() - began < 2000, 'halt must abort before the ordinary 15-second HTTP deadline');
});
test('an initially halted background startup remains retryable after clear', async t => {
const w = await world(t), processing = createProcessing({ dataRoot: w.root }), ticks = [];
const halted = transition(w.root, actor); let calls = 0;
const adapter = await tasksAdapter({ trackers: { demo: { baseUrl: 'http://vikunja.test', project: w.project } },
log: () => {}, timers: { setInterval: fn => { ticks.push(fn); return { unref() {} }; }, clearInterval() {} },
fetch: (...args) => { calls++; return w.fake.fetch(...args); } })
({ broker: w.broker, credentials: w.credentials, businesses: w.businesses, processing });
t.after(async () => { await adapter.close(); await processing.close(); });
await adapter.ready; assert.equal(calls, 0); assert.equal(adapter.status()[0].refused, 'processing-halted');
transition(w.root, { ...actor, halted: false, generation: halted.generation });
ticks[0]();
for (let i = 0; i < 100 && adapter.status()[0].state !== 'ready'; i++)
await new Promise(resolve => setTimeout(resolve, 10));
assert.equal(adapter.status()[0].state, 'ready'); assert.ok(calls > 0);
});