diff --git a/packages/backend-test-utils/src/services/MockSchedulerService.test.ts b/packages/backend-test-utils/src/services/MockSchedulerService.test.ts index 8e26fd770e..87390520b0 100644 --- a/packages/backend-test-utils/src/services/MockSchedulerService.test.ts +++ b/packages/backend-test-utils/src/services/MockSchedulerService.test.ts @@ -205,4 +205,39 @@ describe('MockSchedulerService', () => { expect(taskFn).toHaveBeenCalled(); await expect(isDone()).resolves.toBe(true); }); + + it('should abort tasks when shutting down', async () => { + let taskSignal: AbortSignal | undefined; + + const backend = await startTestBackend({ + features: [ + mockServices.scheduler.factory({ + includeManualTasksOnStartup: true, + includeInitialDelayedTasksOnStartup: true, + }), + createBackendPlugin({ + pluginId: 'tester', + register(reg) { + reg.registerInit({ + deps: { scheduler: coreServices.scheduler }, + async init({ scheduler }) { + scheduler.scheduleTask({ + ...baseOpts, + id: 'test-plain', + fn: async signal => { + taskSignal = signal; + }, + }); + }, + }); + }, + }), + ], + }); + + expect(taskSignal).toBeDefined(); + expect(taskSignal?.aborted).toBe(false); + await backend.stop(); + expect(taskSignal?.aborted).toBe(true); + }); }); diff --git a/packages/backend-test-utils/src/services/MockSchedulerService.ts b/packages/backend-test-utils/src/services/MockSchedulerService.ts index 7648770eb5..ec02748e79 100644 --- a/packages/backend-test-utils/src/services/MockSchedulerService.ts +++ b/packages/backend-test-utils/src/services/MockSchedulerService.ts @@ -31,6 +31,7 @@ export class MockSchedulerService implements SchedulerService { SchedulerServiceTaskInvocationDefinition & SchedulerServiceTaskScheduleDefinition & { descriptor: SchedulerServiceTaskDescriptor; + abortControllers: AbortController[]; } >(); readonly #runningTasks = new Set(); @@ -57,6 +58,9 @@ export class MockSchedulerService implements SchedulerService { }); }); } + lifecycle.addShutdownHook(async () => { + await this.#shutdownAllTasks(); + }); return this; }, }); @@ -87,6 +91,7 @@ export class MockSchedulerService implements SchedulerService { scope: task.scope ?? 'global', settings: { version: 1 }, }, + abortControllers: [], }); } @@ -100,7 +105,9 @@ export class MockSchedulerService implements SchedulerService { } this.#runningTasks.add(id); try { - await task.fn(new AbortController().signal); + const abortController = new AbortController(); + task.abortControllers.push(abortController); + await task.fn(abortController.signal); this.#deferredTaskCompletions.get(id)?.resolve(); } catch (error) { this.#deferredTaskCompletions.get(id)?.reject(error); @@ -144,6 +151,14 @@ export class MockSchedulerService implements SchedulerService { await Promise.all(selectedTaskIds.map(id => this.triggerTask(id))); } + async #shutdownAllTasks() { + for (const task of this.#tasks.values()) { + for (const abortController of task.abortControllers) { + abortController.abort(); + } + } + } + /** * Wait for the task with the given ID to complete. *