@backstage/backend-test-utils
Version:
Test helpers library for Backstage backends
275 lines (269 loc) • 9.27 kB
JavaScript
;
var backendAppApi = require('@backstage/backend-app-api');
var backendPluginApi = require('@backstage/backend-plugin-api');
var mockServices = require('../services/mockServices.cjs.js');
require('../services/mockCredentials.cjs.js');
var config = require('@backstage/config');
var express = require('express');
var rootHttpRouter = require('@backstage/backend-defaults/rootHttpRouter');
var discovery = require('@backstage/backend-defaults/discovery');
var ActionsRegistryServiceMock = require('../alpha/services/ActionsRegistryServiceMock.cjs.js');
require('@backstage/errors');
require('zod');
require('zod-to-json-schema');
var ActionsServiceMock = require('../alpha/services/ActionsServiceMock.cjs.js');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
var express__default = /*#__PURE__*/_interopDefaultCompat(express);
const defaultServiceFactories = [
mockServices.mockServices.auth.factory(),
mockServices.mockServices.auditor.factory(),
mockServices.mockServices.cache.factory(),
mockServices.mockServices.rootConfig.factory(),
mockServices.mockServices.database.factory(),
mockServices.mockServices.httpAuth.factory(),
mockServices.mockServices.httpRouter.factory(),
mockServices.mockServices.lifecycle.factory(),
mockServices.mockServices.logger.factory(),
mockServices.mockServices.permissions.factory(),
mockServices.mockServices.permissionsRegistry.factory(),
mockServices.mockServices.rootHealth.factory(),
mockServices.mockServices.rootLifecycle.factory(),
mockServices.mockServices.rootLogger.factory(),
mockServices.mockServices.scheduler.factory(),
mockServices.mockServices.userInfo.factory(),
mockServices.mockServices.urlReader.factory(),
mockServices.mockServices.events.factory(),
// Alpha services
ActionsRegistryServiceMock.actionsRegistryServiceMock.factory(),
ActionsServiceMock.actionsServiceMock.factory()
];
function createPluginsForOrphanModules(features) {
const pluginIds = /* @__PURE__ */ new Set();
const modulePluginIds = /* @__PURE__ */ new Set();
for (const feature of features) {
if (isInternalBackendRegistrations(feature)) {
const registrations = feature.getRegistrations();
for (const registration of registrations) {
if (registration.type === "plugin") {
pluginIds.add(registration.pluginId);
} else if (registration.type === "module") {
modulePluginIds.add(registration.pluginId);
}
}
}
}
for (const pluginId of pluginIds) {
modulePluginIds.delete(pluginId);
}
return Array.from(modulePluginIds).map(
(pluginId) => backendPluginApi.createBackendPlugin({
pluginId,
register(reg) {
reg.registerInit({ deps: {}, async init() {
} });
}
})
);
}
function createExtensionPointTestModules(features, extensionPointTuples) {
if (!extensionPointTuples) {
return [];
}
const registrations = features.flatMap((feature) => {
if (isInternalBackendRegistrations(feature)) {
return feature.getRegistrations();
}
return [];
});
const extensionPointMap = new Map(
extensionPointTuples.map((ep) => [ep[0].id, ep])
);
const extensionPointsToSort = new Set(extensionPointMap.keys());
const extensionPointsByPlugin = /* @__PURE__ */ new Map();
for (const registration of registrations) {
if (registration.type === "module") {
const testDep = Object.values(registration.init.deps).filter(
(dep) => extensionPointsToSort.has(dep.id)
);
if (testDep.length > 0) {
let points = extensionPointsByPlugin.get(registration.pluginId);
if (!points) {
points = [];
extensionPointsByPlugin.set(registration.pluginId, points);
}
for (const { id } of testDep) {
points.push(id);
extensionPointsToSort.delete(id);
}
}
}
}
if (extensionPointsToSort.size > 0) {
const list = Array.from(extensionPointsToSort).map((id) => `'${id}'`).join(", ");
throw new Error(
`Unable to determine the plugin ID of extension point(s) ${list}. Tested extension points must be depended on by one or more tested modules.`
);
}
const modules = [];
for (const [pluginId, pluginExtensionPointIds] of extensionPointsByPlugin) {
modules.push(
backendPluginApi.createBackendModule({
pluginId,
moduleId: "test-extension-point-registration",
register(reg) {
for (const id of pluginExtensionPointIds) {
const tuple = extensionPointMap.get(id);
reg.registerExtensionPoint(...tuple);
}
reg.registerInit({ deps: {}, async init() {
} });
}
})
);
}
return modules;
}
function isPromise(value) {
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
}
function unwrapFeature(feature) {
if ("$$type" in feature) {
return feature;
}
if ("default" in feature) {
return feature.default;
}
return feature;
}
const backendInstancesToCleanUp = new Array();
async function startTestBackend(options) {
const { extensionPoints, ...otherOptions } = options;
const features = await Promise.all(
options.features?.map(async (val) => {
if (isPromise(val)) {
const { default: feature } = await val;
return unwrapFeature(feature);
}
return unwrapFeature(val);
}) ?? []
);
let server;
const rootHttpRouterFactory = backendPluginApi.createServiceFactory({
service: backendPluginApi.coreServices.rootHttpRouter,
deps: {
config: backendPluginApi.coreServices.rootConfig,
lifecycle: backendPluginApi.coreServices.rootLifecycle,
rootLogger: backendPluginApi.coreServices.rootLogger,
health: backendPluginApi.coreServices.rootHealth
},
async factory({ config, lifecycle, rootLogger, health }) {
const router = rootHttpRouter.DefaultRootHttpRouter.create();
const logger = rootLogger.child({ service: "rootHttpRouter" });
const app = express__default.default();
const middleware = rootHttpRouter.MiddlewareFactory.create({ config, logger });
const healthRouter = rootHttpRouter.createHealthRouter({ config, health });
app.use(healthRouter);
app.use(router.handler());
app.use(middleware.notFound());
app.use(middleware.error());
server = await rootHttpRouter.createHttpServer(
app,
{ listen: { host: "", port: 0 } },
{ logger }
);
lifecycle.addShutdownHook(() => server.stop(), { logger });
await server.start();
return router;
}
});
const discoveryFactory = backendPluginApi.createServiceFactory({
service: backendPluginApi.coreServices.discovery,
deps: {
rootHttpRouter: backendPluginApi.coreServices.rootHttpRouter
},
async factory() {
if (!server) {
throw new Error("Test server not started yet");
}
const port = server.port();
const discovery$1 = discovery.HostDiscovery.fromConfig(
new config.ConfigReader({
backend: { baseUrl: `http://localhost:${port}`, listen: { port } }
})
);
return discovery$1;
}
});
const backend = backendAppApi.createSpecializedBackend({
...otherOptions,
defaultServiceFactories: [
...defaultServiceFactories,
rootHttpRouterFactory,
discoveryFactory
]
});
backendInstancesToCleanUp.push(backend);
for (const m of createExtensionPointTestModules(features, extensionPoints)) {
backend.add(m);
}
for (const p of createPluginsForOrphanModules(features)) {
backend.add(p);
}
for (const feature of features) {
backend.add(feature);
}
await backend.start();
return Object.assign(backend, {
get server() {
if (!server) {
throw new Error("TestBackend server is not available");
}
return server;
}
});
}
let registered = false;
function registerTestHooks() {
if (typeof afterAll !== "function") {
return;
}
if (registered) {
return;
}
registered = true;
afterAll(async () => {
await Promise.all(
backendInstancesToCleanUp.map(async (backend) => {
try {
await backend.stop();
} catch (error) {
console.error(`Failed to stop backend after tests, ${error}`);
}
})
);
backendInstancesToCleanUp.length = 0;
});
}
registerTestHooks();
function toInternalBackendFeature(feature) {
if (feature.$$type !== "@backstage/BackendFeature") {
throw new Error(`Invalid BackendFeature, bad type '${feature.$$type}'`);
}
const internal = feature;
if (internal.version !== "v1") {
throw new Error(
`Invalid BackendFeature, bad version '${internal.version}'`
);
}
return internal;
}
function isInternalBackendRegistrations(feature) {
const internal = toInternalBackendFeature(feature);
if (internal.featureType === "registrations") {
return true;
}
return "getRegistrations" in internal;
}
exports.defaultServiceFactories = defaultServiceFactories;
exports.startTestBackend = startTestBackend;
//# sourceMappingURL=TestBackend.cjs.js.map