From 55f685fd9f137972065e9889d31be87468d2e4c1 Mon Sep 17 00:00:00 2001 From: Benjamin Janssens Date: Wed, 4 Sep 2024 14:35:26 +0200 Subject: [PATCH] test: add more tests Signed-off-by: Benjamin Janssens --- ...bitbucketCloudPullRequest.examples.test.ts | 7 +- .../actions/bitbucketCloudPullRequest.test.ts | 418 ++++++++++++++++++ .../src/actions/bitbucketCloudPullRequest.ts | 2 +- 3 files changed, 424 insertions(+), 3 deletions(-) create mode 100644 plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.test.ts diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts index 6d5aaaeab1..14d33b8dba 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -171,7 +171,10 @@ describe('publish:bitbucketCloud:pull-request', () => { const responseOfPullRequests = { type: '', links: { - self: { href: '', name: '' }, + self: { + href: 'https://bitbucket.org/workspace/repo/pull-requests/1', + name: '', + }, html: { href: '', name: '' }, commits: { href: '', name: '' }, approve: { href: '', name: '' }, diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.test.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.test.ts new file mode 100644 index 0000000000..ef900a57c7 --- /dev/null +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.test.ts @@ -0,0 +1,418 @@ +/* + * Copyright 2024 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. + */ + +jest.mock('@backstage/plugin-scaffolder-node', () => { + return { + ...jest.requireActual('@backstage/plugin-scaffolder-node'), + initRepoAndPush: jest.fn().mockResolvedValue({ + commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', + }), + commitAndPushRepo: jest.fn().mockResolvedValue({ + commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', + }), + }; +}); + +import { createPublishBitbucketCloudPullRequestAction } from './bitbucketCloudPullRequest'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { registerMswTestHooks } from '@backstage/backend-test-utils'; +import { ScmIntegrations } from '@backstage/integration'; +import { ConfigReader } from '@backstage/config'; +import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; + +describe('publish:bitbucketCloud:pull-request', () => { + const config = new ConfigReader({ + integrations: { + bitbucketCloud: [ + { + username: 'test-user', + appPassword: 'test-password', + }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + const action = createPublishBitbucketCloudPullRequestAction({ + integrations, + config, + }); + const mockContext = createMockActionContext({ + input: { + repoUrl: 'bitbucket.org?workspace=workspace&project=project&repo=repo', + title: 'Add Scaffolder actions for Bitbucket Cloud', + description: + 'I just made a Pull Request that Add Scaffolder actions for Bitbucket Cloud', + targetBranch: 'master', + sourceBranch: 'develop', + }, + }); + const responseOfBranches = { + pagelen: 1, + size: 187, + values: [ + { + name: 'issue-9.3/AUI-5343-assistive-class', + links: { + commits: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commits/issue-9.3/AUI-5343-assistive-class', + }, + self: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/refs/branches/issue-9.3/AUI-5343-assistive-class', + }, + html: { + href: 'https://bitbucket.org/atlassian/aui/branch/issue-9.3/AUI-5343-assistive-class', + }, + }, + default_merge_strategy: 'squash', + merge_strategies: ['merge_commit', 'squash', 'fast_forward'], + type: 'branch', + target: { + hash: 'e5d1cde9069fcb9f0af90403a4de2150c125a148', + repository: { + links: { + self: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui', + }, + html: { href: 'https://bitbucket.org/atlassian/aui' }, + avatar: { + href: 'https://bytebucket.org/ravatar/%7B585074de-7b60-4fd1-81ed-e0bc7fafbda5%7D?ts=86317', + }, + }, + type: 'repository', + name: 'aui', + full_name: 'atlassian/aui', + uuid: '{585074de-7b60-4fd1-81ed-e0bc7fafbda5}', + }, + links: { + self: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148', + }, + comments: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148/comments', + }, + patch: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/patch/e5d1cde9069fcb9f0af90403a4de2150c125a148', + }, + html: { + href: 'https://bitbucket.org/atlassian/aui/commits/e5d1cde9069fcb9f0af90403a4de2150c125a148', + }, + diff: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/diff/e5d1cde9069fcb9f0af90403a4de2150c125a148', + }, + approve: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148/approve', + }, + statuses: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148/statuses', + }, + }, + author: { + raw: 'Marcin Konopka ', + type: 'author', + user: { + display_name: 'Marcin Konopka', + uuid: '{47cc24f4-2a05-4420-88fe-0417535a110a}', + links: { + self: { + href: 'https://api.bitbucket.org/2.0/users/%7B47cc24f4-2a05-4420-88fe-0417535a110a%7D', + }, + html: { + href: 'https://bitbucket.org/%7B47cc24f4-2a05-4420-88fe-0417535a110a%7D/', + }, + avatar: { + href: 'https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/initials/MK-1.png', + }, + }, + nickname: 'Marcin Konopka', + type: 'user', + account_id: '60113d2b47a9540069f4de03', + }, + }, + parents: [ + { + hash: '87f7fc92b00464ae47b13ef65c91884e4ac9be51', + type: 'commit', + links: { + self: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/87f7fc92b00464ae47b13ef65c91884e4ac9be51', + }, + html: { + href: 'https://bitbucket.org/atlassian/aui/commits/87f7fc92b00464ae47b13ef65c91884e4ac9be51', + }, + }, + }, + ], + date: '2021-04-13T13:44:49+00:00', + message: 'wip\n', + type: 'commit', + }, + }, + ], + page: 1, + next: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/refs/branches?pagelen=1&page=2', + }; + const responseOfDefaultBranch = { + type: '', + links: { + self: { href: '', name: '' }, + html: { href: '', name: '' }, + avatar: { href: '', name: '' }, + pullrequests: { href: '', name: '' }, + commits: { href: '', name: '' }, + forks: { href: '', name: '' }, + watchers: { href: '', name: '' }, + downloads: { href: '', name: '' }, + clone: [{ href: '', name: '' }], + hooks: { href: '', name: '' }, + }, + uuid: '', + full_name: '', + is_private: true, + scm: 'git', + owner: { type: '' }, + name: '', + description: '', + created_on: '', + updated_on: '', + size: 2154, + language: '', + has_issues: true, + has_wiki: true, + fork_policy: 'allow_forks', + project: { type: '' }, + mainbranch: { type: '' }, + }; + const responseOfPullRequests = { + type: '', + links: { + self: { + href: 'https://bitbucket.org/workspace/repo/pull-requests/1', + name: '', + }, + html: { href: '', name: '' }, + commits: { href: '', name: '' }, + approve: { href: '', name: '' }, + diff: { href: '', name: '' }, + diffstat: { href: '', name: '' }, + comments: { href: '', name: '' }, + activity: { href: '', name: '' }, + merge: { href: '', name: '' }, + decline: { href: '', name: '' }, + }, + id: 108, + title: '', + rendered: { + title: { raw: '', markup: 'markdown', html: '' }, + description: { raw: '', markup: 'markdown', html: '' }, + reason: { raw: '', markup: 'markdown', html: '' }, + }, + summary: { raw: '', markup: 'markdown', html: '' }, + state: 'OPEN', + author: { type: '' }, + source: { + repository: { type: '' }, + branch: { + name: '', + merge_strategies: ['merge_commit'], + default_merge_strategy: '', + }, + commit: { hash: '' }, + }, + destination: { + repository: { type: '' }, + branch: { + name: '', + merge_strategies: ['merge_commit'], + default_merge_strategy: '', + }, + commit: { hash: '' }, + }, + merge_commit: { hash: '' }, + comment_count: 51, + task_count: 53, + close_source_branch: true, + closed_by: { type: '' }, + reason: '', + created_on: '', + updated_on: '', + reviewers: [{ type: '' }], + participants: [{ type: '' }], + }; + const handlers = [ + rest.get( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/refs/branches', + (_, res, ctx) => { + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfBranches), + ); + }, + ), + rest.get( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo', + (_, res, ctx) => { + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfDefaultBranch), + ); + }, + ), + rest.post( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/pullrequests', + (_, res, ctx) => { + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfPullRequests), + ); + }, + ), + ]; + + const server = setupServer(); + registerMswTestHooks(server); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should throw an error when the repoUrl is not well formed', async () => { + await expect( + action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoUrl: 'bitbucket.org?project=project&repo=repo', + }, + }), + ).rejects.toThrow(/missing workspace/); + + await expect( + action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoUrl: 'bitbucket.org?workspace=workspace&project=project', + }, + }), + ).rejects.toThrow(/missing repo/); + }); + + it('should throw if there is no integration config provided', async () => { + await expect( + action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoUrl: 'missing.com?workspace=workspace&project=project&repo=repo', + }, + }), + ).rejects.toThrow(/No matching integration configuration/); + }); + + it('should call the correct APIs with basic auth', async () => { + expect.assertions(2); + server.use( + rest.get( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/refs/branches', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + 'Basic dGVzdC11c2VyOnRlc3QtcGFzc3dvcmQ=', + ); + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfBranches), + ); + }, + ), + rest.post( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/pullrequests', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + 'Basic dGVzdC11c2VyOnRlc3QtcGFzc3dvcmQ=', + ); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfPullRequests), + ); + }, + ), + ); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoUrl: 'bitbucket.org?workspace=workspace&project=project&repo=repo', + }, + }); + }); + + it('should work if the token is provided through ctx.input', async () => { + expect.assertions(2); + const token = 'user-token'; + server.use( + rest.get( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/refs/branches', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe(`Bearer ${token}`); + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfBranches), + ); + }, + ), + rest.post( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/pullrequests', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe(`Bearer ${token}`); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfPullRequests), + ); + }, + ), + ); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoUrl: 'bitbucket.org?workspace=workspace&project=project&repo=repo', + token: token, + }, + }); + }); + + it('should call outputs with the correct urls', async () => { + server.use(...handlers); + + await action.handler(mockContext); + + expect(mockContext.output).toHaveBeenCalledWith( + 'pullRequestUrl', + 'https://bitbucket.org/workspace/repo/pull-requests/1', + ); + }); +}); diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts index 71944c18ae..d3593dc6f5 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts @@ -317,7 +317,7 @@ export function createPublishBitbucketCloudPullRequestAction(options: { if (!workspace) { throw new InputError( - `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`, + `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing workspace`, ); }