@backstage/backend-test-utils
Version:
Test helpers library for Backstage backends
124 lines (120 loc) • 3.8 kB
JavaScript
;
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