@backstage/backend-test-utils
Version:
Test helpers library for Backstage backends
790 lines (776 loc) • 30.5 kB
TypeScript
import Keyv from 'keyv';
import { Knex } from 'knex';
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
import { ServiceFactory, RootConfigService, RootLoggerService, AuthService, DiscoveryService, BackstageCredentials, HttpAuthService, BackstageUserInfo, UserInfoService, DatabaseService, PermissionsService, SchedulerService, RootInstanceMetadataService, 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_18' | '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_14'
* @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">;
const mock: (partialImpl?: Partial<RootConfigService> | undefined) => ServiceMock<RootConfigService>;
}
function rootLogger(options?: rootLogger.Options): RootLoggerService;
namespace rootLogger {
type Options = {
level?: 'none' | 'error' | 'warn' | 'info' | 'debug';
};
const factory: (options?: Options | undefined) => ServiceFactory<RootLoggerService, "root", "singleton">;
const mock: (partialImpl?: Partial<RootLoggerService> | undefined) => ServiceMock<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<_backstage_backend_plugin_api.LoggerService, "plugin", "singleton">;
const mock: (partialImpl?: Partial<_backstage_backend_plugin_api.LoggerService> | undefined) => ServiceMock<_backstage_backend_plugin_api.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">;
/**
* 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>;
}
function rootInstanceMetadata(): RootInstanceMetadataService;
namespace rootInstanceMetadata {
const mock: (partialImpl?: Partial<RootInstanceMetadataService> | undefined) => ServiceMock<RootInstanceMetadataService>;
const factory: () => ServiceFactory<RootInstanceMetadataService, "root", "singleton">;
}
}
/**
* @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 { ServiceFactoryTester, TestCaches, TestDatabases, createMockDirectory, mockCredentials, mockErrorHandler, mockServices, registerMswTestHooks, startTestBackend };
export type { CreateMockDirectoryOptions, MockDirectory, MockDirectoryContent, MockDirectoryContentCallback, MockDirectoryContentCallbackContext, MockDirectoryContentOptions, ServiceFactoryTesterOptions, ServiceMock, TestBackend, TestBackendOptions, TestCacheId, TestDatabaseId };