From 093b09d5ec8ff799ac47b859ba8b7bef2f572429 Mon Sep 17 00:00:00 2001 From: Lokesh Kaki Date: Sun, 15 Mar 2026 19:50:29 -0500 Subject: [PATCH] test(catalog-backend-module-gitlab): add analyzer coverage and update module wiring assertions Signed-off-by: Lokesh Kaki --- .../events/analyzeGitLabWebhookEvent.test.ts | 258 ++++++++++++++++++ ...oduleGitlabDiscoveryEntityProvider.test.ts | 13 +- 2 files changed, 268 insertions(+), 3 deletions(-) create mode 100644 plugins/catalog-backend-module-gitlab/src/events/analyzeGitLabWebhookEvent.test.ts diff --git a/plugins/catalog-backend-module-gitlab/src/events/analyzeGitLabWebhookEvent.test.ts b/plugins/catalog-backend-module-gitlab/src/events/analyzeGitLabWebhookEvent.test.ts new file mode 100644 index 0000000000..bda802d67a --- /dev/null +++ b/plugins/catalog-backend-module-gitlab/src/events/analyzeGitLabWebhookEvent.test.ts @@ -0,0 +1,258 @@ +/* + * Copyright 2026 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 { mockServices } from '@backstage/backend-test-utils'; +import { InputError } from '@backstage/errors'; +import { analyzeGitLabWebhookEvent } from './analyzeGitLabWebhookEvent'; + +const isRelevantPath = (path: string): boolean => + path.endsWith('.yaml') || path.endsWith('.yml'); + +describe('analyzeGitLabWebhookEvent', () => { + const logger = mockServices.logger.mock(); + + describe('push', () => { + it('handles file add, modify, and delete', async () => { + const payload = { + object_kind: 'push', + ref: 'refs/heads/main', + project: { + web_url: 'https://gitlab.example.com/group-a/repo-a', + path_with_namespace: 'group-a/repo-a', + default_branch: 'main', + }, + commits: [ + { + id: 'c1', + added: ['catalog-info.yaml'], + modified: ['docs/catalog-info.yml'], + removed: [], + }, + { + id: 'c2', + added: [], + modified: [], + removed: ['old/catalog-info.yaml'], + }, + ], + }; + + await expect( + analyzeGitLabWebhookEvent('push', payload, { + logger, + isRelevantPath, + }), + ).resolves.toMatchInlineSnapshot(` + { + "events": [ + { + "context": { + "commitUrl": "https://gitlab.example.com/group-a/repo-a/-/commit/c1", + }, + "type": "location.updated", + "url": "https://gitlab.example.com/group-a/repo-a/-/blob/main/docs/catalog-info.yml", + }, + { + "context": { + "commitUrl": "https://gitlab.example.com/group-a/repo-a/-/commit/c1", + }, + "type": "location.created", + "url": "https://gitlab.example.com/group-a/repo-a/-/blob/main/catalog-info.yaml", + }, + { + "context": { + "commitUrl": "https://gitlab.example.com/group-a/repo-a/-/commit/c2", + }, + "type": "location.deleted", + "url": "https://gitlab.example.com/group-a/repo-a/-/blob/main/old/catalog-info.yaml", + }, + ], + "result": "ok", + } + `); + }); + + it('handles file rename as location move', async () => { + const payload = { + object_kind: 'push', + ref: 'refs/heads/main', + project: { + web_url: 'https://gitlab.example.com/group-a/repo-a', + path_with_namespace: 'group-a/repo-a', + default_branch: 'main', + }, + commits: [ + { + id: 'c3', + added: ['new/catalog-info.yaml'], + modified: [], + removed: ['old/catalog-info.yaml'], + }, + ], + }; + + await expect( + analyzeGitLabWebhookEvent('push', payload, { + logger, + isRelevantPath, + }), + ).resolves.toMatchInlineSnapshot(` + { + "events": [ + { + "context": { + "commitUrl": "https://gitlab.example.com/group-a/repo-a/-/commit/c3", + }, + "fromUrl": "https://gitlab.example.com/group-a/repo-a/-/blob/main/old/catalog-info.yaml", + "toUrl": "https://gitlab.example.com/group-a/repo-a/-/blob/main/new/catalog-info.yaml", + "type": "location.moved", + }, + ], + "result": "ok", + } + `); + }); + }); + + describe('repository_update', () => { + it('handles repository rename as repository move', async () => { + const payload = { + object_kind: 'repository_update', + event_name: 'project_rename', + old_path_with_namespace: 'group-a/repo-a-old', + project: { + web_url: 'https://gitlab.example.com/group-a/repo-a', + path_with_namespace: 'group-a/repo-a', + }, + }; + + await expect( + analyzeGitLabWebhookEvent('repository_update', payload, { + logger, + isRelevantPath, + }), + ).resolves.toMatchInlineSnapshot(` + { + "events": [ + { + "fromUrl": "https://gitlab.example.com/group-a/repo-a-old", + "toUrl": "https://gitlab.example.com/group-a/repo-a", + "type": "repository.moved", + }, + ], + "result": "ok", + } + `); + }); + + it('handles repository transfer as repository move', async () => { + const payload = { + object_kind: 'repository_update', + event_name: 'project_transfer', + project: { + web_url: 'https://gitlab.example.com/group-b/repo-a', + path_with_namespace: 'group-b/repo-a', + }, + changes: { + path_with_namespace: { + from: 'group-a/repo-a', + to: 'group-b/repo-a', + }, + }, + }; + + await expect( + analyzeGitLabWebhookEvent('repository_update', payload, { + logger, + isRelevantPath, + }), + ).resolves.toMatchInlineSnapshot(` + { + "events": [ + { + "fromUrl": "https://gitlab.example.com/group-a/repo-a", + "toUrl": "https://gitlab.example.com/group-b/repo-a", + "type": "repository.moved", + }, + ], + "result": "ok", + } + `); + }); + + it('handles repository delete', async () => { + const payload = { + object_kind: 'repository_update', + event_name: 'project_destroy', + project: { + web_url: 'https://gitlab.example.com/group-a/repo-a', + path_with_namespace: 'group-a/repo-a', + }, + }; + + await expect( + analyzeGitLabWebhookEvent('repository_update', payload, { + logger, + isRelevantPath, + }), + ).resolves.toMatchInlineSnapshot(` + { + "events": [ + { + "type": "repository.deleted", + "url": "https://gitlab.example.com/group-a/repo-a", + }, + ], + "result": "ok", + } + `); + }); + }); + + it('returns unsupported-event for unsupported event types', async () => { + await expect( + analyzeGitLabWebhookEvent( + 'merge_request', + { + object_kind: 'merge_request', + }, + { + logger, + isRelevantPath, + }, + ), + ).resolves.toEqual({ + result: 'unsupported-event', + event: 'merge_request', + }); + }); + + it('throws on malformed payloads', async () => { + await expect( + analyzeGitLabWebhookEvent('push', undefined, { + logger, + isRelevantPath, + }), + ).rejects.toBeInstanceOf(InputError); + + await expect( + analyzeGitLabWebhookEvent('push', [], { + logger, + isRelevantPath, + }), + ).rejects.toBeInstanceOf(InputError); + }); +}); \ No newline at end of file diff --git a/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.test.ts b/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.test.ts index b99fc6e2da..e664ecdada 100644 --- a/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.test.ts @@ -100,9 +100,16 @@ describe('catalogModuleGitlabDiscoveryEntityProvider', () => { 'GitlabDiscoveryEntityProvider:test-id', ); await provider.connect(connection); - expect(events.subscribed).toHaveLength(1); - expect(events.subscribed[0].id).toEqual( - 'GitlabDiscoveryEntityProvider:test-id', + expect(events.subscribed).toHaveLength(2); + expect(events.subscribed).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: 'catalog-gitlab-scm-events-bridge', + }), + expect.objectContaining({ + id: 'GitlabDiscoveryEntityProvider:test-id', + }), + ]), ); expect(runner).toHaveBeenCalledTimes(1); });