UNPKG

@backstage/backend-test-utils

Version:

Test helpers library for Backstage backends

124 lines (120 loc) 3.8 kB
'use strict'; var backendPluginApi = require('@backstage/backend-plugin-api'); var types = require('@backstage/types'); class MockSchedulerService { #tasks = /* @__PURE__ */ new Map(); #runningTasks = /* @__PURE__ */ new Set(); #deferredTaskCompletions = /* @__PURE__ */ new Map(); /** * Creates a service factory for this mock scheduler instance, which can be installed in a test backend */ factory(options) { return backendPluginApi.createServiceFactory({ service: backendPluginApi.coreServices.scheduler, deps: { lifecycle: backendPluginApi.coreServices.lifecycle }, factory: async ({ lifecycle }) => { if (!options?.skipTaskRunOnStartup) { lifecycle.addStartupHook(async () => { await this.#triggerAllTasks({ includeManualTasks: options?.includeManualTasksOnStartup, includeInitialDelayedTasks: options?.includeInitialDelayedTasksOnStartup }); }); } lifecycle.addShutdownHook(async () => { await this.#shutdownAllTasks(); }); return this; } }); } createScheduledTaskRunner(schedule) { return { run: async (task) => { await this.scheduleTask({ ...task, ...schedule }); } }; } async getScheduledTasks() { return Array.from(this.#tasks.values()).map(({ descriptor }) => descriptor); } async scheduleTask(task) { this.#tasks.set(task.id, { ...task, descriptor: { id: task.id, scope: task.scope ?? "global", settings: { version: 1 } }, abortControllers: new AbortController() }); } async triggerTask(id) { const task = this.#tasks.get(id); if (!task) { throw new Error(`Task ${id} not found`); } if (this.#runningTasks.has(id)) { return; } this.#runningTasks.add(id); try { await task.fn(task.abortControllers.signal); this.#deferredTaskCompletions.get(id)?.resolve(); } catch (error) { this.#deferredTaskCompletions.get(id)?.reject(error); } finally { this.#deferredTaskCompletions.delete(id); this.#runningTasks.delete(id); } } /** * Trigger all tasks that match the given options, and wait for them to complete. * * @param options - The options to filter the tasks to trigger */ async #triggerAllTasks(options) { const { scope = "all", includeManualTasks = false, includeInitialDelayedTasks = false } = options ?? {}; const selectedTaskIds = new Array(); for (const task of this.#tasks.values()) { if (task.initialDelay && !includeInitialDelayedTasks) { continue; } if ("trigger" in task.frequency && task.frequency.trigger === "manual" && !includeManualTasks) { continue; } if (scope === "all" || scope === task.scope) { selectedTaskIds.push(task.id); } } await Promise.all(selectedTaskIds.map((id) => this.triggerTask(id))); } async #shutdownAllTasks() { for (const task of this.#tasks.values()) { task.abortControllers.abort(); } } /** * Wait for the task with the given ID to complete. * * If the task has not yet been scheduled or started, this will wait for it to be scheduled, started, and completed * * @param id - The task ID to wait for * @returns A promise that resolves when the task is completed */ async waitForTask(id) { const existing = this.#deferredTaskCompletions.get(id); if (existing) { return existing; } const deferred = types.createDeferred(); this.#deferredTaskCompletions.set(id, deferred); return deferred; } } exports.MockSchedulerService = MockSchedulerService; //# sourceMappingURL=MockSchedulerService.cjs.js.map