fix(queue): await real process exit before cleanup

Start process-exit waits only when pause or cancellation begins, escalate stubborn children without resolving before close, latch pause across late registrations and fast resume, and retry concat only after a complete resume. Add real child-process and filesystem integration coverage for pause, shutdown cleanup, and persistence order.
This commit is contained in:
Sucukdeluxe
2026-08-11 22:28:35 +02:00
parent 1140c8526e
commit de05539422
4 changed files with 235 additions and 67 deletions
+32 -1
View File
@@ -12,7 +12,7 @@ function deferred(): { promise: Promise<void>; resolve: () => void } {
function createResource(wait: Promise<void> = Promise.resolve()): QueueProcessResource {
return {
kill: vi.fn(),
wait,
wait: () => wait,
pause: vi.fn(),
resume: vi.fn(),
cancel: vi.fn(async () => undefined),
@@ -70,6 +70,37 @@ describe('QueueProcessRegistry', () => {
expect(registry.isPaused('item-a')).toBe(false);
});
it('keeps pause latched until existing and newly registered resources finish pausing', async () => {
const registry = new QueueProcessRegistry();
const firstPaused = deferred();
const latePaused = deferred();
const first = createResource();
const late = createResource();
first.pause = vi.fn(() => firstPaused.promise);
late.pause = vi.fn(() => latePaused.promise);
registry.register('item-a', 'merge', first);
const pausing = registry.pauseItem('item-a');
registry.register('item-a', 'post-processing', late);
const resuming = registry.resumeItem('item-a');
await Promise.resolve();
expect(registry.isPaused('item-a')).toBe(true);
expect(first.resume).not.toHaveBeenCalled();
expect(late.pause).toHaveBeenCalledOnce();
firstPaused.resolve();
await Promise.resolve();
expect(registry.isPaused('item-a')).toBe(true);
latePaused.resolve();
await Promise.all([pausing, resuming]);
expect(first.resume).toHaveBeenCalledOnce();
expect(late.resume).toHaveBeenCalledOnce();
expect(registry.isPaused('item-a')).toBe(false);
});
it.each(['merge', 'split'] as const)('waits for %s termination before removing partial output', async (phase) => {
const registry = new QueueProcessRegistry();
const closed = deferred();