From b3832d10c5332757980cc802c584f56b3c93e541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 16 Apr 2025 22:24:13 +0200 Subject: [PATCH] Add a functional 'mockServices.events()' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/pretty-corners-speak.md | 5 ++ packages/backend-test-utils/report.api.md | 15 +++-- .../next/services/MockEventsService.test.ts | 56 +++++++++++++++++++ .../src/next/services/MockEventsService.ts | 41 ++++++++++++++ .../src/next/services/mockServices.ts | 26 +++++++-- 5 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 .changeset/pretty-corners-speak.md create mode 100644 packages/backend-test-utils/src/next/services/MockEventsService.test.ts create mode 100644 packages/backend-test-utils/src/next/services/MockEventsService.ts diff --git a/.changeset/pretty-corners-speak.md b/.changeset/pretty-corners-speak.md new file mode 100644 index 0000000000..8e24d212fe --- /dev/null +++ b/.changeset/pretty-corners-speak.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-test-utils': minor +--- + +Add a functional `mockServices.events()` diff --git a/packages/backend-test-utils/report.api.md b/packages/backend-test-utils/report.api.md index 5497497068..947862fae7 100644 --- a/packages/backend-test-utils/report.api.md +++ b/packages/backend-test-utils/report.api.md @@ -222,14 +222,17 @@ export namespace mockServices { partialImpl?: Partial | undefined, ) => ServiceMock; } + export function events(): EventsService; // (undocumented) export namespace events { - const // (undocumented) - factory: () => ServiceFactory; - const // (undocumented) - mock: ( - partialImpl?: Partial | undefined, - ) => ServiceMock; + const factory: () => ServiceFactory< + EventsService, + 'plugin', + 'singleton' | 'multiton' + >; + const mock: ( + partialImpl?: Partial | undefined, + ) => ServiceMock; } export function httpAuth(options?: { pluginId?: string; diff --git a/packages/backend-test-utils/src/next/services/MockEventsService.test.ts b/packages/backend-test-utils/src/next/services/MockEventsService.test.ts new file mode 100644 index 0000000000..3cbadd69f5 --- /dev/null +++ b/packages/backend-test-utils/src/next/services/MockEventsService.test.ts @@ -0,0 +1,56 @@ +/* + * Copyright 2025 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 { MockEventsService } from './MockEventsService'; + +describe('MockEventsService', () => { + it('notifies subscribers', async () => { + const service = new MockEventsService(); + + const listener1 = jest.fn(); + const listener2 = jest.fn(); + + await service.subscribe({ + id: 'a', + topics: ['topic1'], + onEvent: listener1, + }); + await service.subscribe({ + id: 'b', + topics: ['topic1', 'topic2'], + onEvent: listener2, + }); + + expect(listener1).not.toHaveBeenCalled(); + expect(listener2).not.toHaveBeenCalled(); + + await service.publish({ + topic: 'topic1', + eventPayload: { payload: 1 }, + }); + + expect(listener1).toHaveBeenCalledTimes(1); + expect(listener2).toHaveBeenCalledTimes(1); + + await service.publish({ + topic: 'topic2', + eventPayload: { payload: 1 }, + }); + + expect(listener1).toHaveBeenCalledTimes(1); + expect(listener2).toHaveBeenCalledTimes(2); + }); +}); diff --git a/packages/backend-test-utils/src/next/services/MockEventsService.ts b/packages/backend-test-utils/src/next/services/MockEventsService.ts new file mode 100644 index 0000000000..abe29e3a8f --- /dev/null +++ b/packages/backend-test-utils/src/next/services/MockEventsService.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2025 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 { + EventParams, + EventsService, + EventsServiceSubscribeOptions, +} from '@backstage/plugin-events-node'; + +export class MockEventsService implements EventsService { + #subscribers: EventsServiceSubscribeOptions[]; + + constructor() { + this.#subscribers = []; + } + + async publish(params: EventParams): Promise { + for (const subscriber of this.#subscribers) { + if (subscriber.topics.includes(params.topic)) { + await subscriber.onEvent(params); + } + } + } + + async subscribe(options: EventsServiceSubscribeOptions): Promise { + this.#subscribers.push(options); + } +} diff --git a/packages/backend-test-utils/src/next/services/mockServices.ts b/packages/backend-test-utils/src/next/services/mockServices.ts index baeae53a77..f68f1337b9 100644 --- a/packages/backend-test-utils/src/next/services/mockServices.ts +++ b/packages/backend-test-utils/src/next/services/mockServices.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { auditorServiceFactory } from '@backstage/backend-defaults/auditor'; import { cacheServiceFactory } from '@backstage/backend-defaults/cache'; import { databaseServiceFactory } from '@backstage/backend-defaults/database'; import { HostDiscovery } from '@backstage/backend-defaults/discovery'; @@ -43,10 +44,7 @@ import { createServiceFactory, } from '@backstage/backend-plugin-api'; import { ConfigReader } from '@backstage/config'; -import { - eventsServiceFactory, - eventsServiceRef, -} from '@backstage/plugin-events-node'; +import { EventsService, eventsServiceRef } from '@backstage/plugin-events-node'; import { JsonObject } from '@backstage/types'; import { Knex } from 'knex'; import { MockAuthService } from './MockAuthService'; @@ -54,7 +52,7 @@ import { MockHttpAuthService } from './MockHttpAuthService'; import { MockRootLoggerService } from './MockRootLoggerService'; import { MockUserInfoService } from './MockUserInfoService'; import { mockCredentials } from './mockCredentials'; -import { auditorServiceFactory } from '@backstage/backend-defaults/auditor'; +import { MockEventsService } from './MockEventsService'; /** @internal */ function createLoggerMock() { @@ -521,8 +519,24 @@ export namespace mockServices { })); } + /** + * Creates a functional mock implementation of the + * {@link @backstage/backend-events-node#eventsServiceRef}. + */ + export function events(): EventsService { + return new MockEventsService(); + } export namespace events { - export const factory = () => eventsServiceFactory; + /** + * Creates a functional mock factory for the + * {@link @backstage/backend-events-node#eventsServiceRef}. + */ + export const factory = simpleFactoryWithOptions(eventsServiceRef, events); + /** + * Creates a mock of the + * {@link @backstage/backend-events-node#eventsServiceRef}, optionally + * with some given method implementations. + */ export const mock = simpleMock(eventsServiceRef, () => ({ publish: jest.fn(), subscribe: jest.fn(),