@backstage/backend-test-utils
Version:
Test helpers library for Backstage backends
784 lines (770 loc) • 30.2 kB
TypeScript
import Keyv from 'keyv';
import { Knex } from 'knex';
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
import { ServiceFactory, RootConfigService, LoggerService, AuthService, DiscoveryService, BackstageCredentials, HttpAuthService, BackstageUserInfo, UserInfoService, DatabaseService, PermissionsService, SchedulerService, BackstageNonePrincipal, BackstageUserPrincipal, BackstagePrincipalAccessRestrictions, BackstageServicePrincipal, ServiceRef, ExtensionPoint, BackendFeature } from '@backstage/backend-plugin-api';
import { EventsService } from '@backstage/plugin-events-node';
import { AuthorizeResult } from '@backstage/plugin-permission-common';
import { JsonObject } from '@backstage/types';
import { Backend } from '@backstage/backend-app-api';
import { ExtendedHttpServer } from '@backstage/backend-defaults/rootHttpRouter';
import * as express from 'express';
import * as qs from 'qs';
import * as express_serve_static_core from 'express-serve-static-core';
/**
* The possible caches to test against.
*
* @public
*/
type TestCacheId = 'MEMORY' | 'REDIS_7' | 'VALKEY_8' | 'MEMCACHED_1';
/**
* Encapsulates the creation of ephemeral test cache instances for use inside
* unit or integration tests.
*
* @public
*/
declare class TestCaches {
private readonly instanceById;
private readonly supportedIds;
private static defaultIds?;
/**
* Creates an empty `TestCaches` instance, and sets up Jest to clean up all of
* its acquired resources after all tests finish.
*
* You typically want to create just a single instance like this at the top of
* your test file or `describe` block, and then call `init` many times on that
* instance inside the individual tests. Spinning up a "physical" cache
* instance takes a considerable amount of time, slowing down tests. But
* wiping the contents of an instance using `init` is very fast.
*/
static create(options?: {
ids?: TestCacheId[];
disableDocker?: boolean;
}): TestCaches;
static setDefaults(options: {
ids?: TestCacheId[];
}): void;
private constructor();
supports(id: TestCacheId): boolean;
eachSupportedId(): [TestCacheId][];
/**
* Returns a fresh, empty cache for the given driver.
*
* @param id - The ID of the cache to use, e.g. 'REDIS_7'
* @returns Cache connection properties
*/
init(id: TestCacheId): Promise<{
store: string;
connection: string;
keyv: Keyv;
}>;
private initAny;
private initMemcached;
private initRedis;
private initValkey;
private shutdown;
}
/**
* The possible databases to test against.
*
* @public
*/
type TestDatabaseId = 'POSTGRES_17' | 'POSTGRES_16' | 'POSTGRES_15' | 'POSTGRES_14' | 'POSTGRES_13' | 'POSTGRES_12' | 'POSTGRES_11' | 'POSTGRES_9' | 'MYSQL_8' | 'SQLITE_3';
/**
* Encapsulates the creation of ephemeral test database instances for use
* inside unit or integration tests.
*
* @public
*/
declare class TestDatabases {
private readonly engineFactoryByDriver;
private readonly engineByTestDatabaseId;
private readonly supportedIds;
private static defaultIds?;
/**
* Creates an empty `TestDatabases` instance, and sets up Jest to clean up
* all of its acquired resources after all tests finish.
*
* You typically want to create just a single instance like this at the top
* of your test file or `describe` block, and then call `init` many times on
* that instance inside the individual tests. Spinning up a "physical"
* database instance takes a considerable amount of time, slowing down tests.
* But initializing a new logical database inside that instance using `init`
* is very fast.
*/
static create(options?: {
ids?: TestDatabaseId[];
disableDocker?: boolean;
}): TestDatabases;
static setDefaults(options: {
ids?: TestDatabaseId[];
}): void;
private constructor();
supports(id: TestDatabaseId): boolean;
eachSupportedId(): [TestDatabaseId][];
/**
* Returns a fresh, unique, empty logical database on an instance of the
* given database ID platform.
*
* @param id - The ID of the database platform to use, e.g. 'POSTGRES_13'
* @returns A `Knex` connection object
*/
init(id: TestDatabaseId): Promise<Knex>;
private shutdown;
}
/**
* Sets up handlers for request mocking
* @public
* @param worker - service worker
*/
declare function registerMswTestHooks(worker: {
listen: (t: any) => void;
close: () => void;
resetHandlers: () => void;
}): void;
/**
* A context that allows for more advanced file system operations when writing mock directory content.
*
* @public
*/
interface MockDirectoryContentCallbackContext {
/** Absolute path to the location of this piece of content on the filesystem */
path: string;
/** Creates a symbolic link at the current location */
symlink(target: string): void;
}
/**
* A callback that allows for more advanced file system operations when writing mock directory content.
*
* @public
*/
type MockDirectoryContentCallback = (ctx: MockDirectoryContentCallbackContext) => void;
/**
* The content of a mock directory represented by a nested object structure.
*
* @remarks
*
* When used as input, the keys may contain forward slashes to indicate nested directories.
* Then returned as output, each directory will always be represented as a separate object.
*
* @example
* ```ts
* {
* 'test.txt': 'content',
* 'sub-dir': {
* 'file.txt': 'content',
* 'nested-dir/file.txt': 'content',
* },
* 'empty-dir': {},
* 'binary-file': Buffer.from([0, 1, 2]),
* }
* ```
*
* @public
*/
type MockDirectoryContent = {
[name in string]: MockDirectoryContent | string | Buffer | MockDirectoryContentCallback;
};
/**
* Options for {@link MockDirectory.content}.
*
* @public
*/
interface MockDirectoryContentOptions {
/**
* The path to read content from. Defaults to the root of the mock directory.
*
* An absolute path can also be provided, as long as it is a child path of the mock directory.
*/
path?: string;
/**
* Whether or not to return files as text rather than buffers.
*
* Defaults to checking the file extension against a list of known text extensions.
*/
shouldReadAsText?: boolean | ((path: string, buffer: Buffer) => boolean);
}
/**
* A utility for creating a mock directory that is automatically cleaned up.
*
* @public
*/
interface MockDirectory {
/**
* The path to the root of the mock directory
*/
readonly path: string;
/**
* Resolves a path relative to the root of the mock directory.
*/
resolve(...paths: string[]): string;
/**
* Sets the content of the mock directory. This will remove any existing content.
*
* @example
* ```ts
* mockDir.setContent({
* 'test.txt': 'content',
* 'sub-dir': {
* 'file.txt': 'content',
* 'nested-dir/file.txt': 'content',
* },
* 'empty-dir': {},
* 'binary-file': Buffer.from([0, 1, 2]),
* });
* ```
*/
setContent(root: MockDirectoryContent): void;
/**
* Adds content of the mock directory. This will overwrite existing files.
*
* @example
* ```ts
* mockDir.addContent({
* 'test.txt': 'content',
* 'sub-dir': {
* 'file.txt': 'content',
* 'nested-dir/file.txt': 'content',
* },
* 'empty-dir': {},
* 'binary-file': Buffer.from([0, 1, 2]),
* });
* ```
*/
addContent(root: MockDirectoryContent): void;
/**
* Reads the content of the mock directory.
*
* @remarks
*
* Text files will be returned as strings, while binary files will be returned as buffers.
* By default the file extension is used to determine whether a file should be read as text.
*
* @example
* ```ts
* expect(mockDir.content()).toEqual({
* 'test.txt': 'content',
* 'sub-dir': {
* 'file.txt': 'content',
* 'nested-dir': {
* 'file.txt': 'content',
* },
* },
* 'empty-dir': {},
* 'binary-file': Buffer.from([0, 1, 2]),
* });
* ```
*/
content(options?: MockDirectoryContentOptions): MockDirectoryContent | undefined;
/**
* Clears the content of the mock directory, ensuring that the directory itself exists.
*/
clear(): void;
/**
* Removes the mock directory and all its contents.
*/
remove(): void;
}
/**
* Options for {@link createMockDirectory}.
*
* @public
*/
interface CreateMockDirectoryOptions {
/**
* In addition to creating a temporary directory, also mock `os.tmpdir()` to
* return the mock directory path until the end of the test suite.
*
* When this option is provided the `createMockDirectory` call must happen in
* a scope where calling `afterAll` from Jest is allowed
*
* @returns
*/
mockOsTmpDir?: boolean;
/**
* Initializes the directory with the given content, see {@link MockDirectory.setContent}.
*/
content?: MockDirectoryContent;
}
/**
* Creates a new temporary mock directory that will be removed after the tests have completed.
*
* @public
* @remarks
*
* This method is intended to be called outside of any test, either at top-level or
* within a `describe` block. It will call `afterAll` to make sure that the mock directory
* is removed after the tests have run.
*
* @example
* ```ts
* describe('MySubject', () => {
* const mockDir = createMockDirectory();
*
* beforeEach(mockDir.clear);
*
* it('should work', () => {
* // ... use mockDir
* })
* })
* ```
*/
declare function createMockDirectory(options?: CreateMockDirectoryOptions): MockDirectory;
/** @public */
type ServiceMock<TService> = {
factory: ServiceFactory<TService>;
} & {
[Key in keyof TService]: TService[Key] extends (...args: infer Args) => infer Return ? TService[Key] & jest.MockInstance<Return, Args> : TService[Key];
};
/**
* Mock implementations of the core services, to be used in tests.
*
* @public
* @remarks
*
* There are some variations among the services depending on what needs tests
* might have, but overall there are three main usage patterns:
*
* 1. Creating an actual fake service instance, often with a simplified version
* of functionality, by calling the mock service itself as a function.
*
* ```ts
* // The function often accepts parameters that control its behavior
* const foo = mockServices.foo();
* ```
*
* 2. Creating a mock service, where all methods are replaced with jest mocks, by
* calling the service's `mock` function.
*
* ```ts
* // You can optionally supply a subset of its methods to implement
* const foo = mockServices.foo.mock({
* someMethod: () => 'mocked result',
* });
* // After exercising your test, you can make assertions on the mock:
* expect(foo.someMethod).toHaveBeenCalledTimes(2);
* expect(foo.otherMethod).toHaveBeenCalledWith(testData);
* ```
*
* 3. Creating a service factory that behaves similarly to the mock as per above.
*
* ```ts
* await startTestBackend({
* features: [
* mockServices.foo.factory({
* someMethod: () => 'mocked result',
* })
* ],
* });
* ```
*/
declare namespace mockServices {
function rootConfig(options?: rootConfig.Options): RootConfigService & {
update(options: {
data: JsonObject;
}): void;
};
namespace rootConfig {
type Options = {
data?: JsonObject;
};
const factory: (options?: Options | undefined) => ServiceFactory<RootConfigService, "root", "singleton" | "multiton">;
const mock: (partialImpl?: Partial<RootConfigService> | undefined) => ServiceMock<RootConfigService>;
}
function rootLogger(options?: rootLogger.Options): LoggerService;
namespace rootLogger {
type Options = {
level?: 'none' | 'error' | 'warn' | 'info' | 'debug';
};
const factory: (options?: Options | undefined) => ServiceFactory<LoggerService, "root", "singleton" | "multiton">;
const mock: (partialImpl?: Partial<_backstage_backend_plugin_api.RootLoggerService> | undefined) => ServiceMock<_backstage_backend_plugin_api.RootLoggerService>;
}
namespace auditor {
const factory: () => ServiceFactory<_backstage_backend_plugin_api.AuditorService, "plugin", "singleton">;
const mock: (partialImpl?: Partial<_backstage_backend_plugin_api.AuditorService> | undefined) => ServiceMock<_backstage_backend_plugin_api.AuditorService>;
}
function auth(options?: {
pluginId?: string;
disableDefaultAuthPolicy?: boolean;
}): AuthService;
namespace auth {
const factory: () => ServiceFactory<AuthService, "plugin", "singleton">;
const mock: (partialImpl?: Partial<AuthService> | undefined) => ServiceMock<AuthService>;
}
function discovery(): DiscoveryService;
namespace discovery {
const factory: () => ServiceFactory<DiscoveryService, "plugin", "singleton">;
const mock: (partialImpl?: Partial<DiscoveryService> | undefined) => ServiceMock<DiscoveryService>;
}
/**
* Creates a mock implementation of the `HttpAuthService`.
*
* By default all requests without credentials are treated as requests from
* the default mock user principal. This behavior can be configured with the
* `defaultCredentials` option.
*/
function httpAuth(options?: {
pluginId?: string;
/**
* The default credentials to use if there are no credentials present in the
* incoming request.
*
* By default all requests without credentials are treated as authenticated
* as the default mock user as returned from `mockCredentials.user()`.
*/
defaultCredentials?: BackstageCredentials;
}): HttpAuthService;
namespace httpAuth {
/**
* Creates a mock service factory for the `HttpAuthService`.
*
* By default all requests without credentials are treated as requests from
* the default mock user principal. This behavior can be configured with the
* `defaultCredentials` option.
*/
const factory: (options?: {
defaultCredentials?: BackstageCredentials;
}) => ServiceFactory<HttpAuthService, "plugin", "singleton">;
const mock: (partialImpl?: Partial<HttpAuthService> | undefined) => ServiceMock<HttpAuthService>;
}
/**
* Creates a mock implementation of the `UserInfoService`.
*
* By default it extracts the user's entity ref from a user principal and
* returns that as the only ownership entity ref, but this can be overridden
* by passing in a custom set of user info.
*/
function userInfo(customInfo?: Partial<BackstageUserInfo>): UserInfoService;
namespace userInfo {
/**
* Creates a mock service factory for the `UserInfoService`.
*
* By default it extracts the user's entity ref from a user principal and
* returns that as the only ownership entity ref.
*/
const factory: () => ServiceFactory<UserInfoService, "plugin", "singleton">;
const mock: (partialImpl?: Partial<UserInfoService> | undefined) => ServiceMock<UserInfoService>;
}
namespace cache {
const factory: () => ServiceFactory<_backstage_backend_plugin_api.CacheService, "plugin", "singleton">;
const mock: (partialImpl?: Partial<_backstage_backend_plugin_api.CacheService> | undefined) => ServiceMock<_backstage_backend_plugin_api.CacheService>;
}
/**
* Creates a mock implementation of the
* {@link @backstage/backend-plugin-api#coreServices.database}. Just returns
* the given `knex` instance, which is useful in combination with the
* {@link TestDatabases} facility.
*/
function database(options: {
knex: Knex;
migrations?: {
skip?: boolean;
};
}): DatabaseService;
namespace database {
/**
* Creates a mock factory for the
* {@link @backstage/backend-plugin-api#coreServices.database}. Just returns
* the given `knex` instance if you supply one, which is useful in
* combination with the {@link TestDatabases} facility. Otherwise, it
* returns the regular default database factory which reads config settings.
*/
const factory: (options?: {
knex: Knex;
migrations?: {
skip?: boolean;
};
}) => ServiceFactory<DatabaseService, "plugin", "singleton">;
/**
* Creates a mock of the
* {@link @backstage/backend-plugin-api#coreServices.database}, optionally
* with some given method implementations.
*/
const mock: (partialImpl?: Partial<DatabaseService> | undefined) => ServiceMock<DatabaseService>;
}
namespace rootHealth {
const factory: () => ServiceFactory<_backstage_backend_plugin_api.RootHealthService, "root", "singleton">;
const mock: (partialImpl?: Partial<_backstage_backend_plugin_api.RootHealthService> | undefined) => ServiceMock<_backstage_backend_plugin_api.RootHealthService>;
}
namespace httpRouter {
const factory: () => ServiceFactory<_backstage_backend_plugin_api.HttpRouterService, "plugin", "singleton">;
const mock: (partialImpl?: Partial<_backstage_backend_plugin_api.HttpRouterService> | undefined) => ServiceMock<_backstage_backend_plugin_api.HttpRouterService>;
}
namespace rootHttpRouter {
const factory: () => ServiceFactory<_backstage_backend_plugin_api.RootHttpRouterService, "root", "singleton">;
const mock: (partialImpl?: Partial<_backstage_backend_plugin_api.RootHttpRouterService> | undefined) => ServiceMock<_backstage_backend_plugin_api.RootHttpRouterService>;
}
namespace lifecycle {
const factory: () => ServiceFactory<_backstage_backend_plugin_api.LifecycleService, "plugin", "singleton">;
const mock: (partialImpl?: Partial<_backstage_backend_plugin_api.LifecycleService> | undefined) => ServiceMock<_backstage_backend_plugin_api.LifecycleService>;
}
namespace logger {
const factory: () => ServiceFactory<LoggerService, "plugin", "singleton">;
const mock: (partialImpl?: Partial<LoggerService> | undefined) => ServiceMock<LoggerService>;
}
/**
* Creates a functional mock implementation of the
* {@link @backstage/backend-plugin-api#PermissionsService}.
*/
function permissions(options?: {
result: AuthorizeResult.ALLOW | AuthorizeResult.DENY;
}): PermissionsService;
namespace permissions {
/**
* Creates a mock factory for the
* {@link @backstage/backend-plugin-api#coreServices.permissions}. Just
* returns the given `result` if you supply one. Otherwise, it returns the
* regular default permissions factory.
*/
const factory: (options?: {
result: AuthorizeResult.ALLOW | AuthorizeResult.DENY;
}) => ServiceFactory<PermissionsService, "plugin", "singleton">;
/**
* Creates a mock of the
* {@link @backstage/backend-plugin-api#coreServices.permissions},
* optionally with some given method implementations.
*/
const mock: (partialImpl?: Partial<PermissionsService> | undefined) => ServiceMock<PermissionsService>;
}
namespace permissionsRegistry {
const factory: () => ServiceFactory<_backstage_backend_plugin_api.PermissionsRegistryService, "plugin", "singleton">;
const mock: (partialImpl?: Partial<_backstage_backend_plugin_api.PermissionsRegistryService> | undefined) => ServiceMock<_backstage_backend_plugin_api.PermissionsRegistryService>;
}
namespace rootLifecycle {
const factory: () => ServiceFactory<_backstage_backend_plugin_api.RootLifecycleService, "root", "singleton">;
const mock: (partialImpl?: Partial<_backstage_backend_plugin_api.RootLifecycleService> | undefined) => ServiceMock<_backstage_backend_plugin_api.RootLifecycleService>;
}
function scheduler(): SchedulerService;
namespace scheduler {
const factory: (options?: {
skipTaskRunOnStartup?: boolean;
includeManualTasksOnStartup?: boolean;
includeInitialDelayedTasksOnStartup?: boolean;
}) => ServiceFactory<SchedulerService, "plugin", "singleton">;
const mock: (partialImpl?: Partial<SchedulerService> | undefined) => ServiceMock<SchedulerService>;
}
namespace urlReader {
const factory: () => ServiceFactory<_backstage_backend_plugin_api.UrlReaderService, "plugin", "singleton">;
const mock: (partialImpl?: Partial<_backstage_backend_plugin_api.UrlReaderService> | undefined) => ServiceMock<_backstage_backend_plugin_api.UrlReaderService>;
}
/**
* Creates a functional mock implementation of the
* {@link @backstage/backend-events-node#eventsServiceRef}.
*/
function events(): EventsService;
namespace events {
/**
* Creates a functional mock factory for the
* {@link @backstage/backend-events-node#eventsServiceRef}.
*/
const factory: () => ServiceFactory<EventsService, "plugin", "singleton" | "multiton">;
/**
* Creates a mock of the
* {@link @backstage/backend-events-node#eventsServiceRef}, optionally
* with some given method implementations.
*/
const mock: (partialImpl?: Partial<EventsService> | undefined) => ServiceMock<EventsService>;
}
}
/**
* @public
*/
declare namespace mockCredentials {
/**
* Creates a mocked credentials object for a unauthenticated principal.
*/
function none(): BackstageCredentials<BackstageNonePrincipal>;
/**
* Utilities related to none credentials.
*/
namespace none {
/**
* Returns an authorization header that translates to unauthenticated
* credentials.
*
* This is useful when one wants to explicitly test unauthenticated requests
* while still using the default behavior of the mock HttpAuthService where
* it defaults to user credentials.
*/
function header(): string;
}
/**
* Creates a mocked credentials object for a user principal.
*
* The default user entity reference is 'user:default/mock'.
*/
function user(userEntityRef?: string, options?: {
actor?: {
subject: string;
};
}): BackstageCredentials<BackstageUserPrincipal>;
/**
* Utilities related to user credentials.
*/
namespace user {
/**
* Creates a mocked user token. If a payload is provided it will be encoded
* into the token and forwarded to the credentials object when authenticated
* by the mock auth service.
*/
function token(userEntityRef?: string, options?: {
actor?: {
subject: string;
};
}): string;
/**
* Returns an authorization header with a mocked user token. If a payload is
* provided it will be encoded into the token and forwarded to the
* credentials object when authenticated by the mock auth service.
*/
function header(userEntityRef?: string): string;
function invalidToken(): string;
function invalidHeader(): string;
}
/**
* Creates a mocked credentials object for a user principal with limited
* access.
*
* The default user entity reference is 'user:default/mock'.
*/
function limitedUser(userEntityRef?: string): BackstageCredentials<BackstageUserPrincipal>;
/**
* Utilities related to limited user credentials.
*/
namespace limitedUser {
/**
* Creates a mocked limited user token. If a payload is provided it will be
* encoded into the token and forwarded to the credentials object when
* authenticated by the mock auth service.
*/
function token(userEntityRef?: string): string;
/**
* Returns an authorization header with a mocked limited user token. If a
* payload is provided it will be encoded into the token and forwarded to
* the credentials object when authenticated by the mock auth service.
*/
function cookie(userEntityRef?: string): string;
function invalidToken(): string;
function invalidCookie(): string;
}
/**
* Creates a mocked credentials object for a service principal.
*
* The default subject is 'external:test-service', and no access restrictions.
*/
function service(subject?: string, accessRestrictions?: BackstagePrincipalAccessRestrictions): BackstageCredentials<BackstageServicePrincipal>;
/**
* Utilities related to service credentials.
*/
namespace service {
/**
* Options for the creation of mock service tokens.
*/
type TokenOptions = {
onBehalfOf: BackstageCredentials;
targetPluginId: string;
};
/**
* Creates a mocked service token. The provided options will be encoded into
* the token and forwarded to the credentials object when authenticated by
* the mock auth service.
*/
function token(options?: TokenOptions): string;
/**
* Returns an authorization header with a mocked service token. The provided
* options will be encoded into the token and forwarded to the credentials
* object when authenticated by the mock auth service.
*/
function header(options?: TokenOptions): string;
function invalidToken(): string;
function invalidHeader(): string;
}
}
/**
* Options for {@link ServiceFactoryTester}.
* @public
*/
interface ServiceFactoryTesterOptions {
/**
* Additional service factories to make available as dependencies.
*
* @remarks
*
* If a service factory is provided for a service that already has a default
* implementation, the provided factory will override the default.
*/
dependencies?: Array<ServiceFactory>;
}
/**
* A utility to help test service factories in isolation.
*
* @public
*/
declare class ServiceFactoryTester<TService, TScope extends 'root' | 'plugin', TInstances extends 'singleton' | 'multiton' = 'singleton'> {
#private;
/**
* Creates a new {@link ServiceFactoryTester} used to test the provided subject.
*
* @param subject - The service factory to test.
* @param options - Additional options
* @returns A new tester instance for the provided subject.
*/
static from<TService, TScope extends 'root' | 'plugin', TInstances extends 'singleton' | 'multiton' = 'singleton'>(subject: ServiceFactory<TService, TScope, TInstances>, options?: ServiceFactoryTesterOptions): ServiceFactoryTester<TService, TScope, TInstances>;
private constructor();
/**
* Returns the service instance for the subject.
*
* @remarks
*
* If the subject is a plugin scoped service factory a plugin ID
* can be provided to instantiate the service for a specific plugin.
*
* By default the plugin ID 'test' is used.
*/
getSubject(...args: 'root' extends TScope ? [] : [pluginId?: string]): Promise<TInstances extends 'multiton' ? TService[] : TService>;
/**
* Return the service instance for any of the provided dependencies or built-in services.
*
* @remarks
*
* A plugin ID can optionally be provided for plugin scoped services, otherwise the plugin ID 'test' is used.
*/
getService<TGetService, TGetScope extends 'root' | 'plugin', TGetInstances extends 'singleton' | 'multiton' = 'singleton'>(service: ServiceRef<TGetService, TGetScope, TGetInstances>, ...args: 'root' extends TGetScope ? [] : [pluginId?: string]): Promise<TGetInstances extends 'multiton' ? TGetService[] : TGetService>;
}
/** @public */
interface TestBackendOptions<TExtensionPoints extends any[]> {
extensionPoints?: readonly [
...{
[index in keyof TExtensionPoints]: [
ExtensionPoint<TExtensionPoints[index]>,
Partial<TExtensionPoints[index]>
];
}
];
features?: Array<BackendFeature | Promise<{
default: BackendFeature;
}>>;
}
/** @public */
interface TestBackend extends Backend {
/**
* Provides access to the underling HTTP server for use with utilities
* such as `supertest`.
*
* If the root http router service has been replaced, this will throw an error.
*/
readonly server: ExtendedHttpServer;
}
/** @public */
declare function startTestBackend<TExtensionPoints extends any[]>(options: TestBackendOptions<TExtensionPoints>): Promise<TestBackend>;
/**
* A mock for error handler middleware that can be used in router tests.
* @public
*
* @example
* ```ts
* const app = express();
* app.use(mockErrorHandler());
* ```
*/
declare function mockErrorHandler(_options?: {}, ..._args: never[]): express.ErrorRequestHandler<express_serve_static_core.ParamsDictionary, any, any, qs.ParsedQs, Record<string, any>>;
export { type CreateMockDirectoryOptions, type MockDirectory, type MockDirectoryContent, type MockDirectoryContentCallback, type MockDirectoryContentCallbackContext, type MockDirectoryContentOptions, ServiceFactoryTester, type ServiceFactoryTesterOptions, type ServiceMock, type TestBackend, type TestBackendOptions, type TestCacheId, TestCaches, type TestDatabaseId, TestDatabases, createMockDirectory, mockCredentials, mockErrorHandler, mockServices, registerMswTestHooks, startTestBackend };