@backstage/backend-test-utils 0.1.32-next.1 → 0.1.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # @backstage/backend-test-utils
2
2
 
3
+ ## 0.1.32
4
+
5
+ ### Patch Changes
6
+
7
+ - 6cfd4d7073: Include implementations for the new `rootLifecycleServiceRef`.
8
+ - 015a6dced6: Updated to make sure that service implementations replace default service implementations.
9
+ - a3ec2f32ea: The `startTestBackend` setup now includes default implementations for all core services.
10
+ - 483e907eaf: Internal updates of `createServiceFactory` from `@backstage/backend-plugin-api`.
11
+ - 51b7a7ed07: The backend started by `startTestBackend` now has default implementations of all core services. It now also returns a `TestBackend` instance, which provides access to the underlying `server` that can be used with testing libraries such as `supertest`.
12
+ - f23eef3aa2: Updated dependency `better-sqlite3` to `^8.0.0`.
13
+ - Updated dependencies
14
+ - @backstage/backend-plugin-api@0.3.0
15
+ - @backstage/cli@0.22.1
16
+ - @backstage/backend-common@0.18.0
17
+ - @backstage/backend-app-api@0.3.0
18
+ - @backstage/config@1.0.6
19
+ - @backstage/types@1.0.2
20
+ - @backstage/plugin-auth-node@0.2.9
21
+
22
+ ## 0.1.32-next.2
23
+
24
+ ### Patch Changes
25
+
26
+ - 015a6dced6: Updated to make sure that service implementations replace default service implementations.
27
+ - a3ec2f32ea: The `startTestBackend` setup now includes default implementations for all core services.
28
+ - f23eef3aa2: Updated dependency `better-sqlite3` to `^8.0.0`.
29
+ - Updated dependencies
30
+ - @backstage/backend-app-api@0.3.0-next.1
31
+ - @backstage/backend-plugin-api@0.3.0-next.1
32
+ - @backstage/backend-common@0.18.0-next.1
33
+ - @backstage/cli@0.22.1-next.2
34
+ - @backstage/config@1.0.6-next.0
35
+
3
36
  ## 0.1.32-next.1
4
37
 
5
38
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/backend-test-utils",
3
- "version": "0.1.32-next.1",
3
+ "version": "0.1.32",
4
4
  "main": "../dist/index.cjs.js",
5
5
  "types": "../dist/index.alpha.d.ts"
6
6
  }
@@ -6,7 +6,10 @@
6
6
 
7
7
  import { Backend } from '@backstage/backend-app-api';
8
8
  import { BackendFeature } from '@backstage/backend-plugin-api';
9
+ import { ConfigService } from '@backstage/backend-plugin-api';
10
+ import { ExtendedHttpServer } from '@backstage/backend-app-api';
9
11
  import { ExtensionPoint } from '@backstage/backend-plugin-api';
12
+ import { JsonObject } from '@backstage/types';
10
13
  import { Knex } from 'knex';
11
14
  import { ServiceFactory } from '@backstage/backend-plugin-api';
12
15
  import { ServiceRef } from '@backstage/backend-plugin-api';
@@ -14,6 +17,11 @@ import { ServiceRef } from '@backstage/backend-plugin-api';
14
17
  /** @public */
15
18
  export declare function isDockerDisabledForTests(): boolean;
16
19
 
20
+ /** @alpha */
21
+ export declare const mockConfigFactory: (options?: {
22
+ data?: JsonObject | undefined;
23
+ } | undefined) => ServiceFactory<ConfigService>;
24
+
17
25
  /**
18
26
  * Sets up handlers for request mocking
19
27
  * @public
@@ -26,7 +34,18 @@ export declare function setupRequestMockHandlers(worker: {
26
34
  }): void;
27
35
 
28
36
  /** @alpha */
29
- export declare function startTestBackend<TServices extends any[], TExtensionPoints extends any[]>(options: TestBackendOptions<TServices, TExtensionPoints>): Promise<Backend>;
37
+ export declare function startTestBackend<TServices extends any[], TExtensionPoints extends any[]>(options: TestBackendOptions<TServices, TExtensionPoints>): Promise<TestBackend>;
38
+
39
+ /** @alpha */
40
+ export declare interface TestBackend extends Backend {
41
+ /**
42
+ * Provides access to the underling HTTP server for use with utilities
43
+ * such as `supertest`.
44
+ *
45
+ * If the root http router service has been replaced, this will throw an error.
46
+ */
47
+ readonly server: ExtendedHttpServer;
48
+ }
30
49
 
31
50
  /** @alpha */
32
51
  export declare interface TestBackendOptions<TServices extends any[], TExtensionPoints extends any[]> {
@@ -6,7 +6,10 @@
6
6
 
7
7
  import { Backend } from '@backstage/backend-app-api';
8
8
  import { BackendFeature } from '@backstage/backend-plugin-api';
9
+ import { ConfigService } from '@backstage/backend-plugin-api';
10
+ import { ExtendedHttpServer } from '@backstage/backend-app-api';
9
11
  import { ExtensionPoint } from '@backstage/backend-plugin-api';
12
+ import { JsonObject } from '@backstage/types';
10
13
  import { Knex } from 'knex';
11
14
  import { ServiceFactory } from '@backstage/backend-plugin-api';
12
15
  import { ServiceRef } from '@backstage/backend-plugin-api';
@@ -14,6 +17,8 @@ import { ServiceRef } from '@backstage/backend-plugin-api';
14
17
  /** @public */
15
18
  export declare function isDockerDisabledForTests(): boolean;
16
19
 
20
+ /* Excluded from this release type: mockConfigFactory */
21
+
17
22
  /**
18
23
  * Sets up handlers for request mocking
19
24
  * @public
@@ -27,6 +32,8 @@ export declare function setupRequestMockHandlers(worker: {
27
32
 
28
33
  /* Excluded from this release type: startTestBackend */
29
34
 
35
+ /* Excluded from this release type: TestBackend */
36
+
30
37
  /* Excluded from this release type: TestBackendOptions */
31
38
 
32
39
  /**
package/dist/index.cjs.js CHANGED
@@ -9,6 +9,7 @@ var createConnection = require('knex');
9
9
  var uuid = require('uuid');
10
10
  var backendAppApi = require('@backstage/backend-app-api');
11
11
  var backendPluginApi = require('@backstage/backend-plugin-api');
12
+ var express = require('express');
12
13
 
13
14
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
14
15
 
@@ -31,6 +32,7 @@ function _interopNamespace(e) {
31
32
  }
32
33
 
33
34
  var createConnection__default = /*#__PURE__*/_interopDefaultLegacy(createConnection);
35
+ var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
34
36
 
35
37
  function isDockerDisabledForTests() {
36
38
  return Boolean(process.env.BACKSTAGE_TEST_DISABLE_DOCKER) || !Boolean(process.env.CI);
@@ -144,6 +146,17 @@ const LARGER_POOL_CONFIG = {
144
146
  }
145
147
  };
146
148
  class TestDatabases {
149
+ /**
150
+ * Creates an empty `TestDatabases` instance, and sets up Jest to clean up
151
+ * all of its acquired resources after all tests finish.
152
+ *
153
+ * You typically want to create just a single instance like this at the top
154
+ * of your test file or `describe` block, and then call `init` many times on
155
+ * that instance inside the individual tests. Spinning up a "physical"
156
+ * database instance takes a considerable amount of time, slowing down tests.
157
+ * But initializing a new logical database inside that instance using `init`
158
+ * is very fast.
159
+ */
147
160
  static create(options) {
148
161
  const defaultOptions = {
149
162
  ids: Object.keys(allDatabases),
@@ -188,6 +201,13 @@ class TestDatabases {
188
201
  eachSupportedId() {
189
202
  return this.supportedIds.map((id) => [id]);
190
203
  }
204
+ /**
205
+ * Returns a fresh, unique, empty logical database on an instance of the
206
+ * given database ID platform.
207
+ *
208
+ * @param id - The ID of the database platform to use, e.g. 'POSTGRES_13'
209
+ * @returns A `Knex` connection object
210
+ */
191
211
  async init(id) {
192
212
  const properties = allDatabases[id];
193
213
  if (!properties) {
@@ -328,11 +348,142 @@ function setupRequestMockHandlers(worker) {
328
348
  afterEach(() => worker.resetHandlers());
329
349
  }
330
350
 
351
+ const mockConfigFactory = backendPluginApi.createServiceFactory(
352
+ (options) => ({
353
+ service: backendPluginApi.coreServices.config,
354
+ deps: {},
355
+ async factory() {
356
+ return new config.ConfigReader(options == null ? void 0 : options.data, "mock-config");
357
+ }
358
+ })
359
+ );
360
+
361
+ var __accessCheck = (obj, member, msg) => {
362
+ if (!member.has(obj))
363
+ throw TypeError("Cannot " + msg);
364
+ };
365
+ var __privateGet = (obj, member, getter) => {
366
+ __accessCheck(obj, member, "read from private field");
367
+ return getter ? getter.call(obj) : member.get(obj);
368
+ };
369
+ var __privateAdd = (obj, member, value) => {
370
+ if (member.has(obj))
371
+ throw TypeError("Cannot add the same private member more than once");
372
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
373
+ };
374
+ var __privateSet = (obj, member, value, setter) => {
375
+ __accessCheck(obj, member, "write to private field");
376
+ setter ? setter.call(obj, value) : member.set(obj, value);
377
+ return value;
378
+ };
379
+ var __privateMethod = (obj, member, method) => {
380
+ __accessCheck(obj, member, "access private method");
381
+ return method;
382
+ };
383
+ var _levels, _meta, _log, log_fn;
384
+ const _MockLogger = class {
385
+ constructor(levels, meta) {
386
+ __privateAdd(this, _log);
387
+ __privateAdd(this, _levels, void 0);
388
+ __privateAdd(this, _meta, void 0);
389
+ if (typeof levels === "boolean") {
390
+ __privateSet(this, _levels, {
391
+ error: levels,
392
+ debug: levels,
393
+ info: levels,
394
+ warn: levels
395
+ });
396
+ } else {
397
+ __privateSet(this, _levels, levels);
398
+ }
399
+ __privateSet(this, _meta, meta);
400
+ }
401
+ error(message, meta) {
402
+ __privateMethod(this, _log, log_fn).call(this, "error", message, meta);
403
+ }
404
+ warn(message, meta) {
405
+ __privateMethod(this, _log, log_fn).call(this, "warn", message, meta);
406
+ }
407
+ info(message, meta) {
408
+ __privateMethod(this, _log, log_fn).call(this, "info", message, meta);
409
+ }
410
+ debug(message, meta) {
411
+ __privateMethod(this, _log, log_fn).call(this, "debug", message, meta);
412
+ }
413
+ child(meta) {
414
+ return new _MockLogger(__privateGet(this, _levels), { ...__privateGet(this, _meta), ...meta });
415
+ }
416
+ };
417
+ let MockLogger = _MockLogger;
418
+ _levels = new WeakMap();
419
+ _meta = new WeakMap();
420
+ _log = new WeakSet();
421
+ log_fn = function(level, message, meta) {
422
+ if (__privateGet(this, _levels)[level]) {
423
+ const labels = Object.entries(__privateGet(this, _meta)).map(([key, value]) => `${key}=${value}`).join(",");
424
+ console[level](`${labels} ${message}`, meta);
425
+ }
426
+ };
427
+ const mockRootLoggerService = backendPluginApi.createServiceFactory({
428
+ service: backendPluginApi.coreServices.rootLogger,
429
+ deps: {},
430
+ async factory(_deps) {
431
+ return new MockLogger(false, {});
432
+ }
433
+ });
434
+
435
+ class TokenManagerMock {
436
+ async getToken() {
437
+ return { token: "mock-token" };
438
+ }
439
+ async authenticate(token) {
440
+ if (token !== "mock-token") {
441
+ throw new Error("Invalid token");
442
+ }
443
+ }
444
+ }
445
+ const mockTokenManagerFactory = backendPluginApi.createServiceFactory({
446
+ service: backendPluginApi.coreServices.tokenManager,
447
+ deps: {},
448
+ async factory() {
449
+ return new TokenManagerMock();
450
+ }
451
+ });
452
+
453
+ class MockIdentityServiceImpl {
454
+ getIdentity(_options) {
455
+ return Promise.resolve({
456
+ token: "mock-token",
457
+ identity: {
458
+ type: "user",
459
+ userEntityRef: "user:default/mock-user",
460
+ ownershipEntityRefs: []
461
+ }
462
+ });
463
+ }
464
+ }
465
+ const mockIdentityFactory = backendPluginApi.createServiceFactory({
466
+ service: backendPluginApi.coreServices.identity,
467
+ deps: {},
468
+ async factory() {
469
+ return new MockIdentityServiceImpl();
470
+ }
471
+ });
472
+
331
473
  const defaultServiceFactories = [
332
- backendAppApi.rootLoggerFactory(),
333
- backendAppApi.loggerFactory(),
474
+ backendAppApi.cacheFactory(),
475
+ backendAppApi.databaseFactory(),
476
+ backendAppApi.httpRouterFactory(),
334
477
  backendAppApi.lifecycleFactory(),
335
- backendAppApi.rootLifecycleFactory()
478
+ backendAppApi.loggerFactory(),
479
+ mockConfigFactory(),
480
+ mockRootLoggerService(),
481
+ mockIdentityFactory(),
482
+ mockTokenManagerFactory(),
483
+ backendAppApi.permissionsFactory(),
484
+ backendAppApi.rootLifecycleFactory(),
485
+ backendAppApi.schedulerFactory(),
486
+ backendAppApi.urlReaderFactory()
336
487
  ];
337
488
  const backendInstancesToCleanUp = new Array();
338
489
  async function startTestBackend(options) {
@@ -342,6 +493,55 @@ async function startTestBackend(options) {
342
493
  features = [],
343
494
  ...otherOptions
344
495
  } = options;
496
+ let server;
497
+ const rootHttpRouterFactory = backendPluginApi.createServiceFactory({
498
+ service: backendPluginApi.coreServices.rootHttpRouter,
499
+ deps: {
500
+ config: backendPluginApi.coreServices.config,
501
+ lifecycle: backendPluginApi.coreServices.rootLifecycle,
502
+ rootLogger: backendPluginApi.coreServices.rootLogger
503
+ },
504
+ async factory({ config, lifecycle, rootLogger }) {
505
+ const router = backendAppApi.DefaultRootHttpRouter.create();
506
+ const logger = rootLogger.child({ service: "rootHttpRouter" });
507
+ const app = express__default["default"]();
508
+ const middleware = backendAppApi.MiddlewareFactory.create({ config, logger });
509
+ app.use(router.handler());
510
+ app.use(middleware.notFound());
511
+ app.use(middleware.error());
512
+ server = await backendAppApi.createHttpServer(
513
+ app,
514
+ { listen: { host: "", port: 0 } },
515
+ { logger }
516
+ );
517
+ lifecycle.addShutdownHook({
518
+ async fn() {
519
+ await server.stop();
520
+ },
521
+ logger
522
+ });
523
+ await server.start();
524
+ return router;
525
+ }
526
+ });
527
+ const discoveryFactory = backendPluginApi.createServiceFactory({
528
+ service: backendPluginApi.coreServices.discovery,
529
+ deps: {
530
+ rootHttpRouter: backendPluginApi.coreServices.rootHttpRouter
531
+ },
532
+ async factory() {
533
+ if (!server) {
534
+ throw new Error("Test server not started yet");
535
+ }
536
+ const port = server.port();
537
+ const discovery = backendCommon.SingleHostDiscovery.fromConfig(
538
+ new config.ConfigReader({
539
+ backend: { baseUrl: `http://localhost:${port}`, listen: { port } }
540
+ })
541
+ );
542
+ return discovery;
543
+ }
544
+ });
345
545
  const factories = services.map((serviceDef) => {
346
546
  if (Array.isArray(serviceDef)) {
347
547
  const [ref, impl] = serviceDef;
@@ -349,7 +549,7 @@ async function startTestBackend(options) {
349
549
  return backendPluginApi.createServiceFactory({
350
550
  service: ref,
351
551
  deps: {},
352
- factory: async () => async () => impl
552
+ factory: async () => impl
353
553
  })();
354
554
  }
355
555
  return backendPluginApi.createServiceFactory({
@@ -358,16 +558,19 @@ async function startTestBackend(options) {
358
558
  factory: async () => impl
359
559
  })();
360
560
  }
561
+ if (typeof serviceDef === "function") {
562
+ return serviceDef();
563
+ }
361
564
  return serviceDef;
362
565
  });
363
566
  for (const factory of defaultServiceFactories) {
364
- if (!factories.some((f) => f.service === factory.service)) {
567
+ if (!factories.some((f) => f.service.id === factory.service.id)) {
365
568
  factories.push(factory);
366
569
  }
367
570
  }
368
571
  const backend = backendAppApi.createSpecializedBackend({
369
572
  ...otherOptions,
370
- services: factories
573
+ services: [...factories, rootHttpRouterFactory, discoveryFactory]
371
574
  });
372
575
  backendInstancesToCleanUp.push(backend);
373
576
  backend.add({
@@ -384,7 +587,14 @@ async function startTestBackend(options) {
384
587
  backend.add(feature);
385
588
  }
386
589
  await backend.start();
387
- return backend;
590
+ return Object.assign(backend, {
591
+ get server() {
592
+ if (!server) {
593
+ throw new Error("TestBackend server is not available");
594
+ }
595
+ return server;
596
+ }
597
+ });
388
598
  }
389
599
  let registered = false;
390
600
  function registerTestHooks() {
@@ -412,6 +622,7 @@ registerTestHooks();
412
622
 
413
623
  exports.TestDatabases = TestDatabases;
414
624
  exports.isDockerDisabledForTests = isDockerDisabledForTests;
625
+ exports.mockConfigFactory = mockConfigFactory;
415
626
  exports.setupRequestMockHandlers = setupRequestMockHandlers;
416
627
  exports.startTestBackend = startTestBackend;
417
628
  //# sourceMappingURL=index.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/util/isDockerDisabledForTests.ts","../src/database/startMysqlContainer.ts","../src/database/startPostgresContainer.ts","../src/database/types.ts","../src/database/TestDatabases.ts","../src/msw/setupRequestMockHandlers.ts","../src/next/wiring/TestBackend.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/** @public */\nexport function isDockerDisabledForTests() {\n // If we are not running in continuous integration, the default is to skip\n // the (relatively heavy, long running) docker based tests. If you want to\n // still run local tests for all databases, just pass either the CI=1 env\n // parameter to your test runner, or individual connection strings per\n // database.\n return (\n Boolean(process.env.BACKSTAGE_TEST_DISABLE_DOCKER) ||\n !Boolean(process.env.CI)\n );\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport createConnection, { Knex } from 'knex';\nimport { v4 as uuid } from 'uuid';\n\nasync function waitForMysqlReady(\n connection: Knex.MySqlConnectionConfig,\n): Promise<void> {\n const startTime = Date.now();\n const db = createConnection({ client: 'mysql2', connection });\n\n try {\n for (;;) {\n try {\n const result = await db.select(db.raw('version() AS version'));\n if (result[0]?.version) {\n return;\n }\n } catch (e) {\n if (Date.now() - startTime > 30_000) {\n throw new Error(\n `Timed out waiting for the database to be ready for connections, ${e}`,\n );\n }\n }\n\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n } finally {\n db.destroy();\n }\n}\n\nexport async function startMysqlContainer(image: string) {\n const user = 'root';\n const password = uuid();\n\n // Lazy-load to avoid side-effect of importing testcontainers\n const { GenericContainer } = await import('testcontainers');\n\n const container = await new GenericContainer(image)\n .withExposedPorts(3306)\n .withEnv('MYSQL_ROOT_PASSWORD', password)\n .withTmpFs({ '/var/lib/mysql': 'rw' })\n .start();\n\n const host = container.getHost();\n const port = container.getMappedPort(3306);\n const stop = async () => {\n await container.stop({ timeout: 10_000 });\n };\n\n await waitForMysqlReady({ host, port, user, password });\n\n return { host, port, user, password, stop };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport createConnection, { Knex } from 'knex';\nimport { v4 as uuid } from 'uuid';\n\nasync function waitForPostgresReady(\n connection: Knex.PgConnectionConfig,\n): Promise<void> {\n const startTime = Date.now();\n const db = createConnection({ client: 'pg', connection });\n\n try {\n for (;;) {\n try {\n const result = await db.select(db.raw('version()'));\n if (Array.isArray(result) && result[0]?.version) {\n return;\n }\n } catch (e) {\n if (Date.now() - startTime > 30_000) {\n throw new Error(\n `Timed out waiting for the database to be ready for connections, ${e}`,\n );\n }\n }\n\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n } finally {\n db.destroy();\n }\n}\n\nexport async function startPostgresContainer(image: string) {\n const user = 'postgres';\n const password = uuid();\n\n // Lazy-load to avoid side-effect of importing testcontainers\n const { GenericContainer } = await import('testcontainers');\n\n const container = await new GenericContainer(image)\n .withExposedPorts(5432)\n .withEnv('POSTGRES_PASSWORD', password)\n .withTmpFs({ '/var/lib/postgresql/data': 'rw' })\n .start();\n\n const host = container.getHost();\n const port = container.getMappedPort(5432);\n const stop = async () => {\n await container.stop({ timeout: 10_000 });\n };\n\n await waitForPostgresReady({ host, port, user, password });\n\n return { host, port, user, password, stop };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DatabaseManager } from '@backstage/backend-common';\nimport { Knex } from 'knex';\n\n/**\n * The possible databases to test against.\n *\n * @public\n */\nexport type TestDatabaseId =\n | 'POSTGRES_13'\n | 'POSTGRES_9'\n | 'MYSQL_8'\n | 'SQLITE_3';\n\nexport type TestDatabaseProperties = {\n name: string;\n driver: string;\n dockerImageName?: string;\n connectionStringEnvironmentVariableName?: string;\n};\n\nexport type Instance = {\n stopContainer?: () => Promise<void>;\n databaseManager: DatabaseManager;\n connections: Array<Knex>;\n};\n\nexport const allDatabases: Record<TestDatabaseId, TestDatabaseProperties> =\n Object.freeze({\n POSTGRES_13: {\n name: 'Postgres 13.x',\n driver: 'pg',\n dockerImageName: 'postgres:13',\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING',\n },\n POSTGRES_9: {\n name: 'Postgres 9.x',\n driver: 'pg',\n dockerImageName: 'postgres:9',\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING',\n },\n MYSQL_8: {\n name: 'MySQL 8.x',\n driver: 'mysql2',\n dockerImageName: 'mysql:8',\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING',\n },\n SQLITE_3: {\n name: 'SQLite 3.x',\n driver: 'better-sqlite3',\n },\n });\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DatabaseManager } from '@backstage/backend-common';\nimport { ConfigReader } from '@backstage/config';\nimport { randomBytes } from 'crypto';\nimport { Knex } from 'knex';\nimport { isDockerDisabledForTests } from '../util/isDockerDisabledForTests';\nimport { startMysqlContainer } from './startMysqlContainer';\nimport { startPostgresContainer } from './startPostgresContainer';\nimport {\n allDatabases,\n Instance,\n TestDatabaseId,\n TestDatabaseProperties,\n} from './types';\n\nconst LARGER_POOL_CONFIG = {\n pool: {\n min: 0,\n max: 50,\n },\n};\n\n/**\n * Encapsulates the creation of ephemeral test database instances for use\n * inside unit or integration tests.\n *\n * @public\n */\nexport class TestDatabases {\n private readonly instanceById: Map<string, Instance>;\n private readonly supportedIds: TestDatabaseId[];\n\n /**\n * Creates an empty `TestDatabases` instance, and sets up Jest to clean up\n * all of its acquired resources after all tests finish.\n *\n * You typically want to create just a single instance like this at the top\n * of your test file or `describe` block, and then call `init` many times on\n * that instance inside the individual tests. Spinning up a \"physical\"\n * database instance takes a considerable amount of time, slowing down tests.\n * But initializing a new logical database inside that instance using `init`\n * is very fast.\n */\n static create(options?: {\n ids?: TestDatabaseId[];\n disableDocker?: boolean;\n }): TestDatabases {\n const defaultOptions = {\n ids: Object.keys(allDatabases) as TestDatabaseId[],\n disableDocker: isDockerDisabledForTests(),\n };\n\n const { ids, disableDocker } = Object.assign(\n {},\n defaultOptions,\n options ?? {},\n );\n\n const supportedIds = ids.filter(id => {\n const properties = allDatabases[id];\n if (!properties) {\n return false;\n }\n // If the caller has set up the env with an explicit connection string,\n // we'll assume that this database will work\n if (\n properties.connectionStringEnvironmentVariableName &&\n process.env[properties.connectionStringEnvironmentVariableName]\n ) {\n return true;\n }\n // If the database doesn't require docker at all, there's nothing to worry\n // about\n if (!properties.dockerImageName) {\n return true;\n }\n // If the database requires docker, but docker is disabled, we will fail.\n if (disableDocker) {\n return false;\n }\n return true;\n });\n\n const databases = new TestDatabases(supportedIds);\n\n if (supportedIds.length > 0) {\n afterAll(async () => {\n await databases.shutdown();\n });\n }\n\n return databases;\n }\n\n private constructor(supportedIds: TestDatabaseId[]) {\n this.instanceById = new Map();\n this.supportedIds = supportedIds;\n }\n\n supports(id: TestDatabaseId): boolean {\n return this.supportedIds.includes(id);\n }\n\n eachSupportedId(): [TestDatabaseId][] {\n return this.supportedIds.map(id => [id]);\n }\n\n /**\n * Returns a fresh, unique, empty logical database on an instance of the\n * given database ID platform.\n *\n * @param id - The ID of the database platform to use, e.g. 'POSTGRES_13'\n * @returns A `Knex` connection object\n */\n async init(id: TestDatabaseId): Promise<Knex> {\n const properties = allDatabases[id];\n if (!properties) {\n const candidates = Object.keys(allDatabases).join(', ');\n throw new Error(\n `Unknown test database ${id}, possible values are ${candidates}`,\n );\n }\n if (!this.supportedIds.includes(id)) {\n const candidates = this.supportedIds.join(', ');\n throw new Error(\n `Unsupported test database ${id} for this environment, possible values are ${candidates}`,\n );\n }\n\n let instance: Instance | undefined = this.instanceById.get(id);\n\n // Ensure that a testcontainers instance is up for this ID\n if (!instance) {\n instance = await this.initAny(properties);\n this.instanceById.set(id, instance);\n }\n\n // Ensure that a unique logical database is created in the instance\n const connection = await instance.databaseManager\n .forPlugin(`db${randomBytes(16).toString('hex')}`)\n .getClient();\n\n instance.connections.push(connection);\n\n return connection;\n }\n\n private async initAny(properties: TestDatabaseProperties): Promise<Instance> {\n // Use the connection string if provided\n if (properties.driver === 'pg' || properties.driver === 'mysql2') {\n const envVarName = properties.connectionStringEnvironmentVariableName;\n if (envVarName) {\n const connectionString = process.env[envVarName];\n if (connectionString) {\n const databaseManager = DatabaseManager.fromConfig(\n new ConfigReader({\n backend: {\n database: {\n knexConfig: properties.driver.includes('sqlite')\n ? {}\n : LARGER_POOL_CONFIG,\n client: properties.driver,\n connection: connectionString,\n },\n },\n }),\n );\n return {\n databaseManager,\n connections: [],\n };\n }\n }\n }\n\n // Otherwise start a container for the purpose\n switch (properties.driver) {\n case 'pg':\n return this.initPostgres(properties);\n case 'mysql2':\n return this.initMysql(properties);\n case 'better-sqlite3':\n case 'sqlite3':\n return this.initSqlite(properties);\n default:\n throw new Error(`Unknown database driver ${properties.driver}`);\n }\n }\n\n private async initPostgres(\n properties: TestDatabaseProperties,\n ): Promise<Instance> {\n const { host, port, user, password, stop } = await startPostgresContainer(\n properties.dockerImageName!,\n );\n\n const databaseManager = DatabaseManager.fromConfig(\n new ConfigReader({\n backend: {\n database: {\n knexConfig: LARGER_POOL_CONFIG,\n client: 'pg',\n connection: { host, port, user, password },\n },\n },\n }),\n );\n\n return {\n stopContainer: stop,\n databaseManager,\n connections: [],\n };\n }\n\n private async initMysql(\n properties: TestDatabaseProperties,\n ): Promise<Instance> {\n const { host, port, user, password, stop } = await startMysqlContainer(\n properties.dockerImageName!,\n );\n\n const databaseManager = DatabaseManager.fromConfig(\n new ConfigReader({\n backend: {\n database: {\n knexConfig: LARGER_POOL_CONFIG,\n client: 'mysql2',\n connection: { host, port, user, password },\n },\n },\n }),\n );\n\n return {\n stopContainer: stop,\n databaseManager,\n connections: [],\n };\n }\n\n private async initSqlite(\n properties: TestDatabaseProperties,\n ): Promise<Instance> {\n const databaseManager = DatabaseManager.fromConfig(\n new ConfigReader({\n backend: {\n database: {\n client: properties.driver,\n connection: ':memory:',\n },\n },\n }),\n );\n\n return {\n databaseManager,\n connections: [],\n };\n }\n\n private async shutdown() {\n const instances = [...this.instanceById.values()];\n await Promise.all(\n instances.map(async ({ stopContainer, connections }) => {\n try {\n await Promise.all(connections.map(c => c.destroy()));\n } catch {\n // ignore\n }\n try {\n await stopContainer?.();\n } catch {\n // ignore\n }\n }),\n );\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Sets up handlers for request mocking\n * @public\n * @param worker - service worker\n */\nexport function setupRequestMockHandlers(worker: {\n listen: (t: any) => void;\n close: () => void;\n resetHandlers: () => void;\n}) {\n beforeAll(() => worker.listen({ onUnhandledRequest: 'error' }));\n afterAll(() => worker.close());\n afterEach(() => worker.resetHandlers());\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Backend,\n createSpecializedBackend,\n lifecycleFactory,\n rootLifecycleFactory,\n loggerFactory,\n rootLoggerFactory,\n} from '@backstage/backend-app-api';\nimport {\n ServiceFactory,\n ServiceRef,\n createServiceFactory,\n BackendFeature,\n ExtensionPoint,\n} from '@backstage/backend-plugin-api';\n\n/** @alpha */\nexport interface TestBackendOptions<\n TServices extends any[],\n TExtensionPoints extends any[],\n> {\n services?: readonly [\n ...{\n [index in keyof TServices]:\n | ServiceFactory<TServices[index]>\n | (() => ServiceFactory<TServices[index]>)\n | [ServiceRef<TServices[index]>, Partial<TServices[index]>];\n },\n ];\n extensionPoints?: readonly [\n ...{\n [index in keyof TExtensionPoints]: [\n ExtensionPoint<TExtensionPoints[index]>,\n Partial<TExtensionPoints[index]>,\n ];\n },\n ];\n features?: BackendFeature[];\n}\n\nconst defaultServiceFactories = [\n rootLoggerFactory(),\n loggerFactory(),\n lifecycleFactory(),\n rootLifecycleFactory(),\n];\n\nconst backendInstancesToCleanUp = new Array<Backend>();\n\n/** @alpha */\nexport async function startTestBackend<\n TServices extends any[],\n TExtensionPoints extends any[],\n>(options: TestBackendOptions<TServices, TExtensionPoints>): Promise<Backend> {\n const {\n services = [],\n extensionPoints = [],\n features = [],\n ...otherOptions\n } = options;\n\n const factories = services.map(serviceDef => {\n if (Array.isArray(serviceDef)) {\n // if type is ExtensionPoint?\n // do something differently?\n const [ref, impl] = serviceDef;\n if (ref.scope === 'plugin') {\n return createServiceFactory({\n service: ref,\n deps: {},\n factory: async () => async () => impl,\n })();\n }\n return createServiceFactory({\n service: ref,\n deps: {},\n factory: async () => impl,\n })();\n }\n return serviceDef as ServiceFactory;\n });\n\n for (const factory of defaultServiceFactories) {\n if (!factories.some(f => f.service === factory.service)) {\n factories.push(factory);\n }\n }\n\n const backend = createSpecializedBackend({\n ...otherOptions,\n services: factories,\n });\n\n backendInstancesToCleanUp.push(backend);\n\n backend.add({\n id: `---test-extension-point-registrar`,\n register(reg) {\n for (const [ref, impl] of extensionPoints) {\n reg.registerExtensionPoint(ref, impl);\n }\n\n reg.registerInit({ deps: {}, async init() {} });\n },\n });\n\n for (const feature of features) {\n backend.add(feature);\n }\n\n await backend.start();\n\n return backend;\n}\n\nlet registered = false;\nfunction registerTestHooks() {\n if (typeof afterAll !== 'function') {\n return;\n }\n if (registered) {\n return;\n }\n registered = true;\n\n afterAll(async () => {\n await Promise.all(\n backendInstancesToCleanUp.map(async backend => {\n try {\n await backend.stop();\n } catch (error) {\n console.error(`Failed to stop backend after tests, ${error}`);\n }\n }),\n );\n backendInstancesToCleanUp.length = 0;\n });\n}\n\nregisterTestHooks();\n"],"names":["createConnection","uuid","randomBytes","DatabaseManager","ConfigReader","rootLoggerFactory","loggerFactory","lifecycleFactory","rootLifecycleFactory","createServiceFactory","createSpecializedBackend"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/util/isDockerDisabledForTests.ts","../src/database/startMysqlContainer.ts","../src/database/startPostgresContainer.ts","../src/database/types.ts","../src/database/TestDatabases.ts","../src/msw/setupRequestMockHandlers.ts","../src/next/implementations/mockConfigService.ts","../src/next/implementations/mockRootLoggerService.ts","../src/next/implementations/mockTokenManagerService.ts","../src/next/implementations/mockIdentityService.ts","../src/next/wiring/TestBackend.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/** @public */\nexport function isDockerDisabledForTests() {\n // If we are not running in continuous integration, the default is to skip\n // the (relatively heavy, long running) docker based tests. If you want to\n // still run local tests for all databases, just pass either the CI=1 env\n // parameter to your test runner, or individual connection strings per\n // database.\n return (\n Boolean(process.env.BACKSTAGE_TEST_DISABLE_DOCKER) ||\n !Boolean(process.env.CI)\n );\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport createConnection, { Knex } from 'knex';\nimport { v4 as uuid } from 'uuid';\n\nasync function waitForMysqlReady(\n connection: Knex.MySqlConnectionConfig,\n): Promise<void> {\n const startTime = Date.now();\n const db = createConnection({ client: 'mysql2', connection });\n\n try {\n for (;;) {\n try {\n const result = await db.select(db.raw('version() AS version'));\n if (result[0]?.version) {\n return;\n }\n } catch (e) {\n if (Date.now() - startTime > 30_000) {\n throw new Error(\n `Timed out waiting for the database to be ready for connections, ${e}`,\n );\n }\n }\n\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n } finally {\n db.destroy();\n }\n}\n\nexport async function startMysqlContainer(image: string) {\n const user = 'root';\n const password = uuid();\n\n // Lazy-load to avoid side-effect of importing testcontainers\n const { GenericContainer } = await import('testcontainers');\n\n const container = await new GenericContainer(image)\n .withExposedPorts(3306)\n .withEnv('MYSQL_ROOT_PASSWORD', password)\n .withTmpFs({ '/var/lib/mysql': 'rw' })\n .start();\n\n const host = container.getHost();\n const port = container.getMappedPort(3306);\n const stop = async () => {\n await container.stop({ timeout: 10_000 });\n };\n\n await waitForMysqlReady({ host, port, user, password });\n\n return { host, port, user, password, stop };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport createConnection, { Knex } from 'knex';\nimport { v4 as uuid } from 'uuid';\n\nasync function waitForPostgresReady(\n connection: Knex.PgConnectionConfig,\n): Promise<void> {\n const startTime = Date.now();\n const db = createConnection({ client: 'pg', connection });\n\n try {\n for (;;) {\n try {\n const result = await db.select(db.raw('version()'));\n if (Array.isArray(result) && result[0]?.version) {\n return;\n }\n } catch (e) {\n if (Date.now() - startTime > 30_000) {\n throw new Error(\n `Timed out waiting for the database to be ready for connections, ${e}`,\n );\n }\n }\n\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n } finally {\n db.destroy();\n }\n}\n\nexport async function startPostgresContainer(image: string) {\n const user = 'postgres';\n const password = uuid();\n\n // Lazy-load to avoid side-effect of importing testcontainers\n const { GenericContainer } = await import('testcontainers');\n\n const container = await new GenericContainer(image)\n .withExposedPorts(5432)\n .withEnv('POSTGRES_PASSWORD', password)\n .withTmpFs({ '/var/lib/postgresql/data': 'rw' })\n .start();\n\n const host = container.getHost();\n const port = container.getMappedPort(5432);\n const stop = async () => {\n await container.stop({ timeout: 10_000 });\n };\n\n await waitForPostgresReady({ host, port, user, password });\n\n return { host, port, user, password, stop };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DatabaseManager } from '@backstage/backend-common';\nimport { Knex } from 'knex';\n\n/**\n * The possible databases to test against.\n *\n * @public\n */\nexport type TestDatabaseId =\n | 'POSTGRES_13'\n | 'POSTGRES_9'\n | 'MYSQL_8'\n | 'SQLITE_3';\n\nexport type TestDatabaseProperties = {\n name: string;\n driver: string;\n dockerImageName?: string;\n connectionStringEnvironmentVariableName?: string;\n};\n\nexport type Instance = {\n stopContainer?: () => Promise<void>;\n databaseManager: DatabaseManager;\n connections: Array<Knex>;\n};\n\nexport const allDatabases: Record<TestDatabaseId, TestDatabaseProperties> =\n Object.freeze({\n POSTGRES_13: {\n name: 'Postgres 13.x',\n driver: 'pg',\n dockerImageName: 'postgres:13',\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING',\n },\n POSTGRES_9: {\n name: 'Postgres 9.x',\n driver: 'pg',\n dockerImageName: 'postgres:9',\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING',\n },\n MYSQL_8: {\n name: 'MySQL 8.x',\n driver: 'mysql2',\n dockerImageName: 'mysql:8',\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING',\n },\n SQLITE_3: {\n name: 'SQLite 3.x',\n driver: 'better-sqlite3',\n },\n });\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DatabaseManager } from '@backstage/backend-common';\nimport { ConfigReader } from '@backstage/config';\nimport { randomBytes } from 'crypto';\nimport { Knex } from 'knex';\nimport { isDockerDisabledForTests } from '../util/isDockerDisabledForTests';\nimport { startMysqlContainer } from './startMysqlContainer';\nimport { startPostgresContainer } from './startPostgresContainer';\nimport {\n allDatabases,\n Instance,\n TestDatabaseId,\n TestDatabaseProperties,\n} from './types';\n\nconst LARGER_POOL_CONFIG = {\n pool: {\n min: 0,\n max: 50,\n },\n};\n\n/**\n * Encapsulates the creation of ephemeral test database instances for use\n * inside unit or integration tests.\n *\n * @public\n */\nexport class TestDatabases {\n private readonly instanceById: Map<string, Instance>;\n private readonly supportedIds: TestDatabaseId[];\n\n /**\n * Creates an empty `TestDatabases` instance, and sets up Jest to clean up\n * all of its acquired resources after all tests finish.\n *\n * You typically want to create just a single instance like this at the top\n * of your test file or `describe` block, and then call `init` many times on\n * that instance inside the individual tests. Spinning up a \"physical\"\n * database instance takes a considerable amount of time, slowing down tests.\n * But initializing a new logical database inside that instance using `init`\n * is very fast.\n */\n static create(options?: {\n ids?: TestDatabaseId[];\n disableDocker?: boolean;\n }): TestDatabases {\n const defaultOptions = {\n ids: Object.keys(allDatabases) as TestDatabaseId[],\n disableDocker: isDockerDisabledForTests(),\n };\n\n const { ids, disableDocker } = Object.assign(\n {},\n defaultOptions,\n options ?? {},\n );\n\n const supportedIds = ids.filter(id => {\n const properties = allDatabases[id];\n if (!properties) {\n return false;\n }\n // If the caller has set up the env with an explicit connection string,\n // we'll assume that this database will work\n if (\n properties.connectionStringEnvironmentVariableName &&\n process.env[properties.connectionStringEnvironmentVariableName]\n ) {\n return true;\n }\n // If the database doesn't require docker at all, there's nothing to worry\n // about\n if (!properties.dockerImageName) {\n return true;\n }\n // If the database requires docker, but docker is disabled, we will fail.\n if (disableDocker) {\n return false;\n }\n return true;\n });\n\n const databases = new TestDatabases(supportedIds);\n\n if (supportedIds.length > 0) {\n afterAll(async () => {\n await databases.shutdown();\n });\n }\n\n return databases;\n }\n\n private constructor(supportedIds: TestDatabaseId[]) {\n this.instanceById = new Map();\n this.supportedIds = supportedIds;\n }\n\n supports(id: TestDatabaseId): boolean {\n return this.supportedIds.includes(id);\n }\n\n eachSupportedId(): [TestDatabaseId][] {\n return this.supportedIds.map(id => [id]);\n }\n\n /**\n * Returns a fresh, unique, empty logical database on an instance of the\n * given database ID platform.\n *\n * @param id - The ID of the database platform to use, e.g. 'POSTGRES_13'\n * @returns A `Knex` connection object\n */\n async init(id: TestDatabaseId): Promise<Knex> {\n const properties = allDatabases[id];\n if (!properties) {\n const candidates = Object.keys(allDatabases).join(', ');\n throw new Error(\n `Unknown test database ${id}, possible values are ${candidates}`,\n );\n }\n if (!this.supportedIds.includes(id)) {\n const candidates = this.supportedIds.join(', ');\n throw new Error(\n `Unsupported test database ${id} for this environment, possible values are ${candidates}`,\n );\n }\n\n let instance: Instance | undefined = this.instanceById.get(id);\n\n // Ensure that a testcontainers instance is up for this ID\n if (!instance) {\n instance = await this.initAny(properties);\n this.instanceById.set(id, instance);\n }\n\n // Ensure that a unique logical database is created in the instance\n const connection = await instance.databaseManager\n .forPlugin(`db${randomBytes(16).toString('hex')}`)\n .getClient();\n\n instance.connections.push(connection);\n\n return connection;\n }\n\n private async initAny(properties: TestDatabaseProperties): Promise<Instance> {\n // Use the connection string if provided\n if (properties.driver === 'pg' || properties.driver === 'mysql2') {\n const envVarName = properties.connectionStringEnvironmentVariableName;\n if (envVarName) {\n const connectionString = process.env[envVarName];\n if (connectionString) {\n const databaseManager = DatabaseManager.fromConfig(\n new ConfigReader({\n backend: {\n database: {\n knexConfig: properties.driver.includes('sqlite')\n ? {}\n : LARGER_POOL_CONFIG,\n client: properties.driver,\n connection: connectionString,\n },\n },\n }),\n );\n return {\n databaseManager,\n connections: [],\n };\n }\n }\n }\n\n // Otherwise start a container for the purpose\n switch (properties.driver) {\n case 'pg':\n return this.initPostgres(properties);\n case 'mysql2':\n return this.initMysql(properties);\n case 'better-sqlite3':\n case 'sqlite3':\n return this.initSqlite(properties);\n default:\n throw new Error(`Unknown database driver ${properties.driver}`);\n }\n }\n\n private async initPostgres(\n properties: TestDatabaseProperties,\n ): Promise<Instance> {\n const { host, port, user, password, stop } = await startPostgresContainer(\n properties.dockerImageName!,\n );\n\n const databaseManager = DatabaseManager.fromConfig(\n new ConfigReader({\n backend: {\n database: {\n knexConfig: LARGER_POOL_CONFIG,\n client: 'pg',\n connection: { host, port, user, password },\n },\n },\n }),\n );\n\n return {\n stopContainer: stop,\n databaseManager,\n connections: [],\n };\n }\n\n private async initMysql(\n properties: TestDatabaseProperties,\n ): Promise<Instance> {\n const { host, port, user, password, stop } = await startMysqlContainer(\n properties.dockerImageName!,\n );\n\n const databaseManager = DatabaseManager.fromConfig(\n new ConfigReader({\n backend: {\n database: {\n knexConfig: LARGER_POOL_CONFIG,\n client: 'mysql2',\n connection: { host, port, user, password },\n },\n },\n }),\n );\n\n return {\n stopContainer: stop,\n databaseManager,\n connections: [],\n };\n }\n\n private async initSqlite(\n properties: TestDatabaseProperties,\n ): Promise<Instance> {\n const databaseManager = DatabaseManager.fromConfig(\n new ConfigReader({\n backend: {\n database: {\n client: properties.driver,\n connection: ':memory:',\n },\n },\n }),\n );\n\n return {\n databaseManager,\n connections: [],\n };\n }\n\n private async shutdown() {\n const instances = [...this.instanceById.values()];\n await Promise.all(\n instances.map(async ({ stopContainer, connections }) => {\n try {\n await Promise.all(connections.map(c => c.destroy()));\n } catch {\n // ignore\n }\n try {\n await stopContainer?.();\n } catch {\n // ignore\n }\n }),\n );\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Sets up handlers for request mocking\n * @public\n * @param worker - service worker\n */\nexport function setupRequestMockHandlers(worker: {\n listen: (t: any) => void;\n close: () => void;\n resetHandlers: () => void;\n}) {\n beforeAll(() => worker.listen({ onUnhandledRequest: 'error' }));\n afterAll(() => worker.close());\n afterEach(() => worker.resetHandlers());\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport { ConfigReader } from '@backstage/config';\nimport { JsonObject } from '@backstage/types';\n\n/** @alpha */\nexport const mockConfigFactory = createServiceFactory(\n (options?: { data?: JsonObject }) => ({\n service: coreServices.config,\n deps: {},\n async factory() {\n return new ConfigReader(options?.data, 'mock-config');\n },\n }),\n);\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createServiceFactory,\n LoggerService,\n LogMeta,\n RootLoggerService,\n} from '@backstage/backend-plugin-api';\n\ninterface MockLoggerOptions {\n levels:\n | boolean\n | { error: boolean; warn: boolean; info: boolean; debug: boolean };\n}\n\nclass MockLogger implements RootLoggerService {\n #levels: Exclude<MockLoggerOptions['levels'], boolean>;\n #meta: LogMeta;\n\n error(message: string, meta?: LogMeta | Error | undefined): void {\n this.#log('error', message, meta);\n }\n\n warn(message: string, meta?: LogMeta | Error | undefined): void {\n this.#log('warn', message, meta);\n }\n\n info(message: string, meta?: LogMeta | Error | undefined): void {\n this.#log('info', message, meta);\n }\n\n debug(message: string, meta?: LogMeta | Error | undefined): void {\n this.#log('debug', message, meta);\n }\n\n child(meta: LogMeta): LoggerService {\n return new MockLogger(this.#levels, { ...this.#meta, ...meta });\n }\n\n constructor(levels: MockLoggerOptions['levels'], meta: LogMeta) {\n if (typeof levels === 'boolean') {\n this.#levels = {\n error: levels,\n debug: levels,\n info: levels,\n warn: levels,\n };\n } else {\n this.#levels = levels;\n }\n this.#meta = meta;\n }\n\n #log(\n level: 'error' | 'warn' | 'info' | 'debug',\n message: string,\n meta?: LogMeta | Error | undefined,\n ) {\n if (this.#levels[level]) {\n const labels = Object.entries(this.#meta)\n .map(([key, value]) => `${key}=${value}`)\n .join(',');\n console[level](`${labels} ${message}`, meta);\n }\n }\n}\n\n/** @alpha */\nexport const mockRootLoggerService = createServiceFactory({\n service: coreServices.rootLogger,\n deps: {},\n async factory(_deps) {\n return new MockLogger(false, {});\n },\n});\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { TokenManager } from '@backstage/backend-common';\nimport {\n coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\n\nclass TokenManagerMock implements TokenManager {\n async getToken(): Promise<{ token: string }> {\n return { token: 'mock-token' };\n }\n async authenticate(token: string): Promise<void> {\n if (token !== 'mock-token') {\n throw new Error('Invalid token');\n }\n }\n}\n\n/** @alpha */\nexport const mockTokenManagerFactory = createServiceFactory({\n service: coreServices.tokenManager,\n deps: {},\n async factory() {\n return new TokenManagerMock();\n },\n});\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n coreServices,\n createServiceFactory,\n IdentityService,\n} from '@backstage/backend-plugin-api';\nimport {\n IdentityApiGetIdentityRequest,\n BackstageIdentityResponse,\n} from '@backstage/plugin-auth-node';\n\nclass MockIdentityServiceImpl implements IdentityService {\n getIdentity(\n _options: IdentityApiGetIdentityRequest,\n ): Promise<BackstageIdentityResponse | undefined> {\n return Promise.resolve({\n token: 'mock-token',\n identity: {\n type: 'user',\n userEntityRef: 'user:default/mock-user',\n ownershipEntityRefs: [],\n },\n });\n }\n}\n\nexport const mockIdentityFactory = createServiceFactory({\n service: coreServices.identity,\n deps: {},\n async factory() {\n return new MockIdentityServiceImpl();\n },\n});\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Backend,\n createSpecializedBackend,\n lifecycleFactory,\n rootLifecycleFactory,\n loggerFactory,\n cacheFactory,\n permissionsFactory,\n schedulerFactory,\n urlReaderFactory,\n databaseFactory,\n httpRouterFactory,\n MiddlewareFactory,\n createHttpServer,\n ExtendedHttpServer,\n DefaultRootHttpRouter,\n} from '@backstage/backend-app-api';\nimport { SingleHostDiscovery } from '@backstage/backend-common';\nimport {\n ServiceFactory,\n ServiceRef,\n createServiceFactory,\n BackendFeature,\n ExtensionPoint,\n coreServices,\n} from '@backstage/backend-plugin-api';\n\nimport { mockConfigFactory } from '../implementations/mockConfigService';\nimport { mockRootLoggerService } from '../implementations/mockRootLoggerService';\nimport { mockTokenManagerFactory } from '../implementations/mockTokenManagerService';\nimport { ConfigReader } from '@backstage/config';\nimport express from 'express';\nimport { mockIdentityFactory } from '../implementations/mockIdentityService';\n\n/** @alpha */\nexport interface TestBackendOptions<\n TServices extends any[],\n TExtensionPoints extends any[],\n> {\n services?: readonly [\n ...{\n [index in keyof TServices]:\n | ServiceFactory<TServices[index]>\n | (() => ServiceFactory<TServices[index]>)\n | [ServiceRef<TServices[index]>, Partial<TServices[index]>];\n },\n ];\n extensionPoints?: readonly [\n ...{\n [index in keyof TExtensionPoints]: [\n ExtensionPoint<TExtensionPoints[index]>,\n Partial<TExtensionPoints[index]>,\n ];\n },\n ];\n features?: BackendFeature[];\n}\n\n/** @alpha */\nexport interface TestBackend extends Backend {\n /**\n * Provides access to the underling HTTP server for use with utilities\n * such as `supertest`.\n *\n * If the root http router service has been replaced, this will throw an error.\n */\n readonly server: ExtendedHttpServer;\n}\n\nconst defaultServiceFactories = [\n cacheFactory(),\n databaseFactory(),\n httpRouterFactory(),\n lifecycleFactory(),\n loggerFactory(),\n mockConfigFactory(),\n mockRootLoggerService(),\n mockIdentityFactory(),\n mockTokenManagerFactory(),\n permissionsFactory(),\n rootLifecycleFactory(),\n schedulerFactory(),\n urlReaderFactory(),\n];\n\nconst backendInstancesToCleanUp = new Array<Backend>();\n\n/** @alpha */\nexport async function startTestBackend<\n TServices extends any[],\n TExtensionPoints extends any[],\n>(\n options: TestBackendOptions<TServices, TExtensionPoints>,\n): Promise<TestBackend> {\n const {\n services = [],\n extensionPoints = [],\n features = [],\n ...otherOptions\n } = options;\n\n let server: ExtendedHttpServer;\n\n const rootHttpRouterFactory = createServiceFactory({\n service: coreServices.rootHttpRouter,\n deps: {\n config: coreServices.config,\n lifecycle: coreServices.rootLifecycle,\n rootLogger: coreServices.rootLogger,\n },\n async factory({ config, lifecycle, rootLogger }) {\n const router = DefaultRootHttpRouter.create();\n const logger = rootLogger.child({ service: 'rootHttpRouter' });\n\n const app = express();\n\n const middleware = MiddlewareFactory.create({ config, logger });\n\n app.use(router.handler());\n app.use(middleware.notFound());\n app.use(middleware.error());\n\n server = await createHttpServer(\n app,\n { listen: { host: '', port: 0 } },\n { logger },\n );\n\n lifecycle.addShutdownHook({\n async fn() {\n await server.stop();\n },\n logger,\n });\n\n await server.start();\n\n return router;\n },\n });\n\n const discoveryFactory = createServiceFactory({\n service: coreServices.discovery,\n deps: {\n rootHttpRouter: coreServices.rootHttpRouter,\n },\n async factory() {\n if (!server) {\n throw new Error('Test server not started yet');\n }\n const port = server.port();\n const discovery = SingleHostDiscovery.fromConfig(\n new ConfigReader({\n backend: { baseUrl: `http://localhost:${port}`, listen: { port } },\n }),\n );\n return discovery;\n },\n });\n\n const factories = services.map(serviceDef => {\n if (Array.isArray(serviceDef)) {\n // if type is ExtensionPoint?\n // do something differently?\n const [ref, impl] = serviceDef;\n if (ref.scope === 'plugin') {\n return createServiceFactory({\n service: ref as ServiceRef<unknown, 'plugin'>,\n deps: {},\n factory: async () => impl,\n })();\n }\n return createServiceFactory({\n service: ref as ServiceRef<unknown, 'root'>,\n deps: {},\n factory: async () => impl,\n })();\n }\n if (typeof serviceDef === 'function') {\n return serviceDef();\n }\n return serviceDef as ServiceFactory;\n });\n\n for (const factory of defaultServiceFactories) {\n if (!factories.some(f => f.service.id === factory.service.id)) {\n factories.push(factory);\n }\n }\n\n const backend = createSpecializedBackend({\n ...otherOptions,\n services: [...factories, rootHttpRouterFactory, discoveryFactory],\n });\n\n backendInstancesToCleanUp.push(backend);\n\n backend.add({\n id: `---test-extension-point-registrar`,\n register(reg) {\n for (const [ref, impl] of extensionPoints) {\n reg.registerExtensionPoint(ref, impl);\n }\n\n reg.registerInit({ deps: {}, async init() {} });\n },\n });\n\n for (const feature of features) {\n backend.add(feature);\n }\n\n await backend.start();\n\n return Object.assign(backend, {\n get server() {\n if (!server) {\n throw new Error('TestBackend server is not available');\n }\n return server;\n },\n });\n}\n\nlet registered = false;\nfunction registerTestHooks() {\n if (typeof afterAll !== 'function') {\n return;\n }\n if (registered) {\n return;\n }\n registered = true;\n\n afterAll(async () => {\n await Promise.all(\n backendInstancesToCleanUp.map(async backend => {\n try {\n await backend.stop();\n } catch (error) {\n console.error(`Failed to stop backend after tests, ${error}`);\n }\n }),\n );\n backendInstancesToCleanUp.length = 0;\n });\n}\n\nregisterTestHooks();\n"],"names":["createConnection","uuid","randomBytes","DatabaseManager","ConfigReader","createServiceFactory","coreServices","cacheFactory","databaseFactory","httpRouterFactory","lifecycleFactory","loggerFactory","permissionsFactory","rootLifecycleFactory","schedulerFactory","urlReaderFactory","DefaultRootHttpRouter","express","MiddlewareFactory","createHttpServer","SingleHostDiscovery","createSpecializedBackend"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -6,7 +6,10 @@
6
6
 
7
7
  import { Backend } from '@backstage/backend-app-api';
8
8
  import { BackendFeature } from '@backstage/backend-plugin-api';
9
+ import { ConfigService } from '@backstage/backend-plugin-api';
10
+ import { ExtendedHttpServer } from '@backstage/backend-app-api';
9
11
  import { ExtensionPoint } from '@backstage/backend-plugin-api';
12
+ import { JsonObject } from '@backstage/types';
10
13
  import { Knex } from 'knex';
11
14
  import { ServiceFactory } from '@backstage/backend-plugin-api';
12
15
  import { ServiceRef } from '@backstage/backend-plugin-api';
@@ -14,6 +17,8 @@ import { ServiceRef } from '@backstage/backend-plugin-api';
14
17
  /** @public */
15
18
  export declare function isDockerDisabledForTests(): boolean;
16
19
 
20
+ /* Excluded from this release type: mockConfigFactory */
21
+
17
22
  /**
18
23
  * Sets up handlers for request mocking
19
24
  * @public
@@ -27,6 +32,8 @@ export declare function setupRequestMockHandlers(worker: {
27
32
 
28
33
  /* Excluded from this release type: startTestBackend */
29
34
 
35
+ /* Excluded from this release type: TestBackend */
36
+
30
37
  /* Excluded from this release type: TestBackendOptions */
31
38
 
32
39
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/backend-test-utils",
3
3
  "description": "Test helpers library for Backstage backends",
4
- "version": "0.1.32-next.1",
4
+ "version": "0.1.32",
5
5
  "main": "dist/index.cjs.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "publishConfig": {
@@ -34,12 +34,15 @@
34
34
  "start": "backstage-cli package start"
35
35
  },
36
36
  "dependencies": {
37
- "@backstage/backend-app-api": "^0.2.5-next.0",
38
- "@backstage/backend-common": "^0.18.0-next.0",
39
- "@backstage/backend-plugin-api": "^0.2.1-next.0",
40
- "@backstage/cli": "^0.22.1-next.1",
41
- "@backstage/config": "^1.0.6-next.0",
42
- "better-sqlite3": "^7.5.0",
37
+ "@backstage/backend-app-api": "^0.3.0",
38
+ "@backstage/backend-common": "^0.18.0",
39
+ "@backstage/backend-plugin-api": "^0.3.0",
40
+ "@backstage/cli": "^0.22.1",
41
+ "@backstage/config": "^1.0.6",
42
+ "@backstage/plugin-auth-node": "^0.2.9",
43
+ "@backstage/types": "^1.0.2",
44
+ "better-sqlite3": "^8.0.0",
45
+ "express": "^4.17.1",
43
46
  "knex": "^2.0.0",
44
47
  "msw": "^0.49.0",
45
48
  "mysql2": "^2.2.5",
@@ -48,7 +51,9 @@
48
51
  "uuid": "^8.0.0"
49
52
  },
50
53
  "devDependencies": {
51
- "@backstage/cli": "^0.22.1-next.1"
54
+ "@backstage/cli": "^0.22.1",
55
+ "@types/supertest": "^2.0.8",
56
+ "supertest": "^6.1.3"
52
57
  },
53
58
  "files": [
54
59
  "dist",