chore: explore some of the options for the httpRouter and fix the shutdown in mutliple tests

Co-authored-by: Patrik Oldsberg <poldsberg@gmail.com>
Co-authored-by: Johan Haals <johan@haals.se>
Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
blam
2023-01-04 17:16:11 +01:00
parent 875cd848fd
commit 8f9d0c8fca
6 changed files with 115 additions and 55 deletions
@@ -15,4 +15,3 @@
*/
export { mockTokenManagerFactory } from './mockTokenManagerService';
export { mockConfigFactory } from './mockConfigService';
export { mockDiscoveryFactory } from './mockDiscoveryService';
@@ -1,40 +0,0 @@
/*
* Copyright 2023 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 { SingleHostDiscovery } from '@backstage/backend-common';
import {
coreServices,
createServiceFactory,
} from '@backstage/backend-plugin-api';
import { ConfigReader } from '@backstage/config';
/** @public */
export const mockDiscoveryFactory = createServiceFactory({
service: coreServices.discovery,
deps: {},
async factory() {
// todo(blam): we want to grab the port from the httpRouter when that's available here
// to provide a better way to create our mockDiscoveryService.
const discovery = SingleHostDiscovery.fromConfig(
new ConfigReader({
backend: { baseUrl: 'http://localhost:7007', listen: '0.0.0.0' },
}),
);
return async () => {
return discovery;
};
},
});
@@ -22,6 +22,8 @@ import {
coreServices,
createBackendPlugin,
} from '@backstage/backend-plugin-api';
import { Router } from 'express';
import request from 'supertest';
import { startTestBackend } from './TestBackend';
@@ -184,7 +186,7 @@ describe('TestBackend', () => {
urlReader: coreServices.urlReader,
},
async init(deps) {
expect(Object.keys(deps)).toHaveLength(14);
expect(Object.keys(deps)).toHaveLength(15);
expect(Object.values(deps)).not.toContain(undefined);
},
});
@@ -194,6 +196,30 @@ describe('TestBackend', () => {
await startTestBackend({
services: [],
features: [testPlugin()],
}).then(backend => backend.stop());
});
});
it('should allow making requests via supertest', async () => {
const testPlugin = createBackendPlugin({
id: 'test',
register(env) {
env.registerInit({
deps: {
httpRouter: coreServices.httpRouter,
},
async init({ httpRouter }) {
const router = Router();
router.use('/ping-me', (_, res) => res.json({ message: 'pong' }));
httpRouter.use(router);
},
});
},
});
const { server } = await startTestBackend({ features: [testPlugin()] });
const res = await request(server).get('/api/test/ping-me');
expect(res.status).toEqual(200);
expect(res.body).toEqual({ message: 'pong' });
});
});
@@ -26,23 +26,27 @@ import {
schedulerFactory,
urlReaderFactory,
databaseFactory,
rootHttpRouterFactory,
httpRouterFactory,
} from '@backstage/backend-app-api';
import {
createServiceBuilder,
SingleHostDiscovery,
} from '@backstage/backend-common';
import { Handler } from 'express';
import * as http from 'http';
import Router from 'express-promise-router';
import {
ServiceFactory,
ServiceRef,
createServiceFactory,
BackendFeature,
ExtensionPoint,
coreServices,
} from '@backstage/backend-plugin-api';
import {
mockConfigFactory,
mockTokenManagerFactory,
mockDiscoveryFactory,
} from '../implementations';
import { mockConfigFactory, mockTokenManagerFactory } from '../implementations';
import { AddressInfo } from 'net';
import { ConfigReader } from '@backstage/config';
/** @alpha */
export interface TestBackendOptions<
@@ -75,10 +79,8 @@ const defaultServiceFactories = [
lifecycleFactory(),
loggerFactory(),
mockConfigFactory(),
mockDiscoveryFactory(),
mockTokenManagerFactory(),
permissionsFactory(),
rootHttpRouterFactory(),
rootLifecycleFactory(),
rootLoggerFactory(),
schedulerFactory(),
@@ -99,6 +101,71 @@ export async function startTestBackend<
...otherOptions
} = options;
let server: http.Server;
const rootHttpRouterFactory = createServiceFactory({
service: coreServices.rootHttpRouter,
deps: {
config: coreServices.config,
lifecycle: coreServices.rootLifecycle,
},
async factory({ config, lifecycle }) {
const router = Router();
const service = createServiceBuilder(module)
.loadConfig(config)
.setPort(0);
service.addRouter('', router);
server = await service.start();
// Stop method isn't part of the public API, let's fix that once we move the implementation here.
const stoppableServer = server as typeof server & {
stop: (cb: (error?: Error) => void) => void;
};
lifecycle.addShutdownHook({
async fn() {
await new Promise<void>((resolve, reject) => {
stoppableServer.stop((error?: Error) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
},
labels: { service: 'rootHttpRouter' },
});
return {
use: (path: string, handler: Handler) => {
router.use(path, handler);
},
};
},
});
const discoveryFactory = createServiceFactory({
service: coreServices.discovery,
deps: {
rootHttpRouter: coreServices.rootHttpRouter,
},
async factory() {
if (!server) {
throw new Error('Test server not started yet');
}
const { port } = server.address() as AddressInfo;
const discovery = SingleHostDiscovery.fromConfig(
new ConfigReader({
backend: { baseUrl: `http://localhost:${port}`, listen: { port } },
}),
);
return async () => discovery;
},
});
const factories = services.map(serviceDef => {
if (Array.isArray(serviceDef)) {
// if type is ExtensionPoint?
@@ -131,7 +198,7 @@ export async function startTestBackend<
const backend = createSpecializedBackend({
...otherOptions,
services: factories,
services: [...factories, rootHttpRouterFactory, discoveryFactory],
});
backendInstancesToCleanUp.push(backend);
@@ -153,7 +220,7 @@ export async function startTestBackend<
await backend.start();
return backend;
return Object.assign(backend, { server: server! }) as Backend;
}
let registered = false;