diff --git a/.changeset/little-rivers-deny.md b/.changeset/little-rivers-deny.md new file mode 100644 index 0000000000..8a6c60311e --- /dev/null +++ b/.changeset/little-rivers-deny.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-test-utils': minor +--- + +Made it possible to construct `mockServices.database` with a given knex instance diff --git a/docs/backend-system/building-plugins-and-modules/02-testing.md b/docs/backend-system/building-plugins-and-modules/02-testing.md index 38107f0242..1ec100cff3 100644 --- a/docs/backend-system/building-plugins-and-modules/02-testing.md +++ b/docs/backend-system/building-plugins-and-modules/02-testing.md @@ -224,10 +224,7 @@ your test database. ```ts const { knex, subject } = await createSubject(databaseId); const { server } = await startTestBackend({ - features: [ - myPlugin(), - mockServices.database.mock({ getClient: async () => knex }), - ], + features: [myPlugin(), mockServices.database.factory({ knex })], }); ``` diff --git a/packages/backend-defaults/src/entrypoints/auth/plugin/keys/DatabaseKeyStore.test.ts b/packages/backend-defaults/src/entrypoints/auth/plugin/keys/DatabaseKeyStore.test.ts index 551cccaf73..feb253e7ed 100644 --- a/packages/backend-defaults/src/entrypoints/auth/plugin/keys/DatabaseKeyStore.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/plugin/keys/DatabaseKeyStore.test.ts @@ -46,7 +46,7 @@ describe('DatabaseKeyStore', () => { knex, logger, keyStore: await DatabaseKeyStore.create({ - database: { getClient: async () => knex }, + database: mockServices.database({ knex }), logger, }), }; diff --git a/packages/backend-defaults/src/entrypoints/scheduler/lib/DefaultSchedulerService.test.ts b/packages/backend-defaults/src/entrypoints/scheduler/lib/DefaultSchedulerService.test.ts index 63b900fab2..199d25f2aa 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/lib/DefaultSchedulerService.test.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/lib/DefaultSchedulerService.test.ts @@ -14,16 +14,11 @@ * limitations under the License. */ -import { - TestDatabaseId, - TestDatabases, - mockServices, -} from '@backstage/backend-test-utils'; +import { TestDatabases, mockServices } from '@backstage/backend-test-utils'; import { Duration } from 'luxon'; import waitForExpect from 'wait-for-expect'; import { DefaultSchedulerService } from './DefaultSchedulerService'; import { createTestScopedSignal } from './__testUtils__/createTestScopedSignal'; -import { DatabaseService } from '@backstage/backend-plugin-api'; jest.setTimeout(60_000); @@ -32,19 +27,12 @@ describe('TaskScheduler', () => { const databases = TestDatabases.create(); const testScopedSignal = createTestScopedSignal(); - async function createDatabase( - databaseId: TestDatabaseId, - ): Promise { - const knex = await databases.init(databaseId); - return { - getClient: async () => knex, - }; - } - it.each(databases.eachSupportedId())( 'can return a working v1 plugin impl, %p', async databaseId => { - const database = await createDatabase(databaseId); + const knex = await databases.init(databaseId); + const database = mockServices.database({ knex }); + const manager = DefaultSchedulerService.create({ database, logger }); const fn = jest.fn(); @@ -65,7 +53,9 @@ describe('TaskScheduler', () => { it.each(databases.eachSupportedId())( 'can return a working v2 plugin impl, %p', async databaseId => { - const database = await createDatabase(databaseId); + const knex = await databases.init(databaseId); + const database = mockServices.database({ knex }); + const manager = DefaultSchedulerService.create({ database, logger }); const fn = jest.fn(); diff --git a/packages/backend-test-utils/report.api.md b/packages/backend-test-utils/report.api.md index 263435c920..a79bd9d03c 100644 --- a/packages/backend-test-utils/report.api.md +++ b/packages/backend-test-utils/report.api.md @@ -175,14 +175,23 @@ export namespace mockServices { partialImpl?: Partial | undefined, ) => ServiceMock; } + export function database(options: { + knex: Knex; + migrations?: { + skip?: boolean; + }; + }): DatabaseService; // (undocumented) export namespace database { - const // (undocumented) - factory: () => ServiceFactory; - const // (undocumented) - mock: ( - partialImpl?: Partial | undefined, - ) => ServiceMock; + const factory: (options?: { + knex: Knex; + migrations?: { + skip?: boolean; + }; + }) => ServiceFactory; + const mock: ( + partialImpl?: Partial | undefined, + ) => ServiceMock; } // (undocumented) export function discovery(): DiscoveryService; diff --git a/packages/backend-test-utils/src/next/services/mockServices.ts b/packages/backend-test-utils/src/next/services/mockServices.ts index 55632f4734..a2c3b34ead 100644 --- a/packages/backend-test-utils/src/next/services/mockServices.ts +++ b/packages/backend-test-utils/src/next/services/mockServices.ts @@ -30,6 +30,7 @@ import { AuthService, BackstageCredentials, BackstageUserInfo, + DatabaseService, DiscoveryService, HttpAuthService, LoggerService, @@ -51,6 +52,7 @@ import { MockHttpAuthService } from './MockHttpAuthService'; import { MockRootLoggerService } from './MockRootLoggerService'; import { MockUserInfoService } from './MockUserInfoService'; import { mockCredentials } from './mockCredentials'; +import { Knex } from 'knex'; /** @internal */ function createLoggerMock() { @@ -376,8 +378,45 @@ export namespace mockServices { })); } + /** + * 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. + */ + export function database(options: { + knex: Knex; + migrations?: { skip?: boolean }; + }): DatabaseService { + return { + getClient: async () => options.knex, + migrations: options.migrations, + }; + } export namespace database { - export const factory = () => databaseServiceFactory; + /** + * 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. + */ + export const factory = (options?: { + knex: Knex; + migrations?: { skip?: boolean }; + }) => + options + ? createServiceFactory({ + service: coreServices.database, + deps: {}, + factory: () => database(options), + }) + : databaseServiceFactory; + /** + * Creates a mock of the + * {@link @backstage/backend-plugin-api#coreServices.database}, optionally + * with some given method implementations. + */ export const mock = simpleMock(coreServices.database, () => ({ getClient: jest.fn(), })); diff --git a/plugins/app-backend/src/lib/assets/StaticAssetsStore.test.ts b/plugins/app-backend/src/lib/assets/StaticAssetsStore.test.ts index 51b949a623..bd7a01e5e8 100644 --- a/plugins/app-backend/src/lib/assets/StaticAssetsStore.test.ts +++ b/plugins/app-backend/src/lib/assets/StaticAssetsStore.test.ts @@ -14,24 +14,9 @@ * limitations under the License. */ -import { Knex as KnexType } from 'knex'; import { TestDatabases, mockServices } from '@backstage/backend-test-utils'; import { StaticAssetsStore } from './StaticAssetsStore'; -const logger = mockServices.logger.mock(); - -function createDatabaseManager( - client: KnexType, - skipMigrations: boolean = false, -) { - return { - getClient: async () => client, - migrations: { - skip: skipMigrations, - }, - }; -} - jest.setTimeout(60_000); describe('StaticAssetsStore', () => { @@ -40,11 +25,11 @@ describe('StaticAssetsStore', () => { it.each(databases.eachSupportedId())( 'should store and retrieve assets, %p', async databaseId => { - const client = await databases.init(databaseId); - const database = createDatabaseManager(client); + const knex = await databases.init(databaseId); + const store = await StaticAssetsStore.create({ - logger, - database, + logger: mockServices.logger.mock(), + database: mockServices.database({ knex }), }); await store.storeAssets([ @@ -82,11 +67,11 @@ describe('StaticAssetsStore', () => { it.each(databases.eachSupportedId())( 'should update assets timestamps, but not contents, %p', async databaseId => { - const client = await databases.init(databaseId); - const database = createDatabaseManager(client); + const knex = await databases.init(databaseId); + const store = await StaticAssetsStore.create({ - logger, - database, + logger: mockServices.logger.mock(), + database: mockServices.database({ knex }), }); await store.storeAssets([ @@ -134,10 +119,10 @@ describe('StaticAssetsStore', () => { 'should trim old assets, %p', async databaseId => { const knex = await databases.init(databaseId); - const database = createDatabaseManager(knex); + const store = await StaticAssetsStore.create({ - logger, - database, + logger: mockServices.logger.mock(), + database: mockServices.database({ knex }), }); await store.storeAssets([ @@ -179,10 +164,10 @@ describe('StaticAssetsStore', () => { 'should isolate assets in namespace, %p', async databaseId => { const knex = await databases.init(databaseId); - const database = createDatabaseManager(knex); + const store = await StaticAssetsStore.create({ - logger, - database, + logger: mockServices.logger.mock(), + database: mockServices.database({ knex }), }); const otherStore = store.withNamespace('other'); diff --git a/plugins/catalog-backend/src/tests/performance/getEntitiesPerformance.test.ts b/plugins/catalog-backend/src/tests/performance/getEntitiesPerformance.test.ts index 47fd664b71..5721fd8218 100644 --- a/plugins/catalog-backend/src/tests/performance/getEntitiesPerformance.test.ts +++ b/plugins/catalog-backend/src/tests/performance/getEntitiesPerformance.test.ts @@ -14,22 +14,22 @@ * limitations under the License. */ -import { describePerformanceTest, performanceTraceEnabled } from './lib/env'; -import { startTestBackend, TestDatabases } from '@backstage/backend-test-utils'; +import { createBackendModule } from '@backstage/backend-plugin-api'; +import { + TestDatabases, + mockServices, + startTestBackend, +} from '@backstage/backend-test-utils'; +import { CatalogClient } from '@backstage/catalog-client'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; +import { Knex } from 'knex'; import { applyDatabaseMigrations } from '../../database/migrations'; import { SyntheticLoadEntitiesProcessor, SyntheticLoadEntitiesProvider, SyntheticLoadOptions, } from './lib/catalogModuleSyntheticLoadEntities'; -import { CatalogClient } from '@backstage/catalog-client'; -import { - coreServices, - createBackendModule, - createServiceFactory, -} from '@backstage/backend-plugin-api'; -import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; -import { Knex } from 'knex'; +import { describePerformanceTest, performanceTraceEnabled } from './lib/env'; jest.setTimeout(600_000); @@ -85,11 +85,7 @@ describePerformanceTest('getEntitiesPerformanceTest', () => { const backend = await startTestBackend({ features: [ import('@backstage/plugin-catalog-backend/alpha'), - createServiceFactory({ - service: coreServices.database, - deps: {}, - factory: () => ({ getClient: async () => knex }), - }), + mockServices.database.factory({ knex }), createBackendModule({ pluginId: 'catalog', moduleId: 'synthetic-load-entities', diff --git a/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts b/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts index e0d3f87124..9e515edb5d 100644 --- a/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts +++ b/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts @@ -14,15 +14,11 @@ * limitations under the License. */ +import { createBackendModule } from '@backstage/backend-plugin-api'; import { - coreServices, - createBackendModule, - createServiceFactory, -} from '@backstage/backend-plugin-api'; -import { + TestDatabases, mockServices, startTestBackend, - TestDatabases, } from '@backstage/backend-test-utils'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { Knex } from 'knex'; @@ -182,11 +178,7 @@ describePerformanceTest('stitchingPerformance', () => { features: [ import('@backstage/plugin-catalog-backend/alpha'), mockServices.rootConfig.factory({ data: config }), - createServiceFactory({ - service: coreServices.database, - deps: {}, - factory: () => ({ getClient: async () => knex }), - }), + mockServices.database.factory({ knex }), createBackendModule({ pluginId: 'catalog', moduleId: 'synthetic-load-entities', @@ -239,11 +231,7 @@ describePerformanceTest('stitchingPerformance', () => { features: [ import('@backstage/plugin-catalog-backend/alpha'), mockServices.rootConfig.factory({ data: config }), - createServiceFactory({ - service: coreServices.database, - deps: {}, - factory: () => ({ getClient: async () => knex }), - }), + mockServices.database.factory({ knex }), createBackendModule({ pluginId: 'catalog', moduleId: 'synthetic-load-entities', diff --git a/plugins/events-backend/src/service/EventsPlugin.test.ts b/plugins/events-backend/src/service/EventsPlugin.test.ts index 2c0befe1fd..c14659573d 100644 --- a/plugins/events-backend/src/service/EventsPlugin.test.ts +++ b/plugins/events-backend/src/service/EventsPlugin.test.ts @@ -144,9 +144,7 @@ describe('eventsPlugin', () => { async function mockKnexFactory(databaseId: TestDatabaseId) { const knex = await databases.init(databaseId); - return mockServices.database.mock({ - getClient: async () => knex, - }).factory; + return mockServices.database.factory({ knex }); } let backend: TestBackend | undefined = undefined; diff --git a/plugins/notifications-backend/src/database/DatabaseNotificationsStore.test.ts b/plugins/notifications-backend/src/database/DatabaseNotificationsStore.test.ts index 90dd3932d0..e0536c50ac 100644 --- a/plugins/notifications-backend/src/database/DatabaseNotificationsStore.test.ts +++ b/plugins/notifications-backend/src/database/DatabaseNotificationsStore.test.ts @@ -13,7 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; +import { + TestDatabaseId, + TestDatabases, + mockServices, +} from '@backstage/backend-test-utils'; import { DatabaseNotificationsStore } from './DatabaseNotificationsStore'; import { Knex } from 'knex'; import { @@ -28,15 +32,10 @@ const databases = TestDatabases.create(); async function createStore(databaseId: TestDatabaseId) { const knex = await databases.init(databaseId); - const mgr = { - getClient: async () => knex, - migrations: { - skip: false, - }, - }; + const database = mockServices.database({ knex, migrations: { skip: false } }); return { knex, - storage: await DatabaseNotificationsStore.create({ database: mgr }), + storage: await DatabaseNotificationsStore.create({ database }), }; } diff --git a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts index c638ffc3f9..d68b36fb54 100644 --- a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts +++ b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts @@ -14,8 +14,11 @@ * limitations under the License. */ -import { Knex as KnexType } from 'knex'; -import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; +import { + TestDatabaseId, + TestDatabases, + mockServices, +} from '@backstage/backend-test-utils'; import { IndexableDocument } from '@backstage/plugin-search-common'; import { PgSearchHighlightOptions } from '../PgSearchEngine'; import { DatabaseDocumentStore } from './DatabaseDocumentStore'; @@ -32,18 +35,6 @@ const highlightOptions: PgSearchHighlightOptions = { fragmentDelimiter: ' ... ', }; -function createDatabaseManager( - client: KnexType, - skipMigrations: boolean = false, -) { - return { - getClient: async () => client, - migrations: { - skip: skipMigrations, - }, - }; -} - jest.setTimeout(60_000); describe('DatabaseDocumentStore', () => { @@ -66,8 +57,7 @@ describe('DatabaseDocumentStore', () => { 'should fail to create, %p', async databaseId => { const knex = await databases.init(databaseId); - const databaseManager = createDatabaseManager(knex); - + const databaseManager = mockServices.database({ knex }); await expect( async () => await DatabaseDocumentStore.create(databaseManager), ).rejects.toThrow(); @@ -82,7 +72,7 @@ describe('DatabaseDocumentStore', () => { async function createStore(databaseId: TestDatabaseId) { const knex = await databases.init(databaseId); - const databaseManager = createDatabaseManager(knex); + const databaseManager = mockServices.database({ knex }); const store = await DatabaseDocumentStore.create(databaseManager); return { store, knex }; diff --git a/plugins/user-settings-backend/src/database/DatabaseUserSettingsStore.test.ts b/plugins/user-settings-backend/src/database/DatabaseUserSettingsStore.test.ts index eb50e26179..3b8b18e61c 100644 --- a/plugins/user-settings-backend/src/database/DatabaseUserSettingsStore.test.ts +++ b/plugins/user-settings-backend/src/database/DatabaseUserSettingsStore.test.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; +import { + TestDatabaseId, + TestDatabases, + mockServices, +} from '@backstage/backend-test-utils'; import { Knex } from 'knex'; import { DatabaseUserSettingsStore, @@ -29,12 +33,7 @@ const databases = TestDatabases.create({ async function createStore(databaseId: TestDatabaseId) { const knex = await databases.init(databaseId); - const databaseManager = { - getClient: async () => knex, - migrations: { - skip: false, - }, - }; + const databaseManager = mockServices.database({ knex }); return { knex,