backend-defaults,test-utils: use, export, and test health service

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-07-17 19:59:42 +02:00
parent 16cc26c0a2
commit 4e79d199cc
8 changed files with 112 additions and 3 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-defaults': patch
---
The `createHealthRouter` utility that allows you to create a health check router is now exported via `@backstage/backend-defaults/rootHttpRouter`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-test-utils': patch
---
The default services for `startTestBackend` and `ServiceFactoryTester` now includes the Root Health Service.
@@ -3,6 +3,7 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
/// <reference types="express" />
/// <reference types="node" />
import { Config } from '@backstage/config';
@@ -17,10 +18,17 @@ import { LoggerService } from '@backstage/backend-plugin-api';
import { RequestHandler } from 'express';
import { RequestListener } from 'http';
import { RootConfigService } from '@backstage/backend-plugin-api';
import { RootHealthService } from '@backstage/backend-plugin-api';
import { RootHttpRouterService } from '@backstage/backend-plugin-api';
import { Router } from 'express';
import type { Server } from 'node:http';
import { ServiceFactory } from '@backstage/backend-plugin-api';
// @public (undocumented)
export function createHealthRouter(options: {
health: RootHealthService;
}): Router;
// @public
export function createHttpServer(
listener: RequestListener,
@@ -1,5 +1,3 @@
import { RootHealthService } from '@backstage/backend-plugin-api';
/*
* Copyright 2024 The Backstage Authors
*
@@ -16,9 +14,13 @@ import { RootHealthService } from '@backstage/backend-plugin-api';
* limitations under the License.
*/
import { RootHealthService } from '@backstage/backend-plugin-api';
import Router from 'express-promise-router';
import { Request, Response } from 'express';
/**
* @public
*/
export function createHealthRouter(options: { health: RootHealthService }) {
const router = Router();
@@ -18,6 +18,7 @@ export {
DefaultRootHttpRouter,
type DefaultRootHttpRouterOptions,
} from './DefaultRootHttpRouter';
export { createHealthRouter } from './createHealthRouter';
export * from './http';
export {
rootHttpRouterServiceFactory,
@@ -0,0 +1,74 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
ServiceFactoryTester,
mockServices,
} from '@backstage/backend-test-utils';
import { Express } from 'express';
import request from 'supertest';
import { rootHttpRouterServiceFactory } from './rootHttpRouterServiceFactory';
import { coreServices } from '@backstage/backend-plugin-api';
describe('rootHttpRouterServiceFactory', () => {
it('should make the health endpoints available', async () => {
let app: Express | undefined = undefined;
const tester = ServiceFactoryTester.from(
rootHttpRouterServiceFactory({
configure(options) {
options.applyDefaults();
app = options.app;
},
}),
{
dependencies: [
mockServices.rootConfig.factory({
data: {
app: { baseUrl: 'http://localhost' },
backend: {
baseUrl: 'http://localhost',
listen: { host: '', port: 0 },
},
},
}),
],
},
);
// Trigger creation of the http service, accessing the app instance through the configure callback
await tester.getSubject();
await request(app!)
.get('/.backstage/health/v1/liveness')
.expect(200, { status: 'ok' });
await request(app!).get('/.backstage/health/v1/readiness').expect(503, {
message: 'Backend has not started yet',
status: 'error',
});
const lifecycle = await tester.getService(coreServices.rootLifecycle);
await (lifecycle as any).startup(); // Trigger startup by calling the private startup method
await request(app!).get('/.backstage/health/v1/readiness').expect(200, {
status: 'ok',
});
expect('test').toBe('test');
});
});
@@ -242,6 +242,15 @@ describe('TestBackend', () => {
expect(res.body).toEqual({ message: 'pong' });
});
it('should expose health check endpoints', async () => {
const { server } = await startTestBackend({ features: [] });
const res = await request(server).get('/.backstage/health/v1/liveness');
expect(res.status).toEqual(200);
expect(res.body).toEqual({ status: 'ok' });
});
it('should provide extension point implementations', async () => {
expect.assertions(3);
@@ -37,6 +37,7 @@ import express from 'express';
// Direct internal import to avoid duplication
// eslint-disable-next-line @backstage/no-forbidden-package-imports
import { InternalBackendFeature } from '@backstage/backend-plugin-api/src/wiring/types';
import { createHealthRouter } from '@backstage/backend-defaults/rootHttpRouter';
/** @public */
export interface TestBackendOptions<TExtensionPoints extends any[]> {
@@ -73,6 +74,7 @@ export const defaultServiceFactories = [
mockServices.lifecycle.factory(),
mockServices.logger.factory(),
mockServices.permissions.factory(),
mockServices.rootHealth.factory(),
mockServices.rootLifecycle.factory(),
mockServices.rootLogger.factory(),
mockServices.scheduler.factory(),
@@ -251,15 +253,18 @@ export async function startTestBackend<TExtensionPoints extends any[]>(
config: coreServices.rootConfig,
lifecycle: coreServices.rootLifecycle,
rootLogger: coreServices.rootLogger,
health: coreServices.rootHealth,
},
async factory({ config, lifecycle, rootLogger }) {
async factory({ config, lifecycle, rootLogger, health }) {
const router = DefaultRootHttpRouter.create();
const logger = rootLogger.child({ service: 'rootHttpRouter' });
const app = express();
const middleware = MiddlewareFactory.create({ config, logger });
const healthRouter = createHealthRouter({ health });
app.use(healthRouter);
app.use(router.handler());
app.use(middleware.notFound());
app.use(middleware.error());