diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index 09efaf5af0..1316ed4539 100644 --- a/plugins/scaffolder-backend-module-gitlab/package.json +++ b/plugins/scaffolder-backend-module-gitlab/package.json @@ -43,7 +43,8 @@ "devDependencies": { "@backstage/backend-common": "workspace:^", "@backstage/cli": "workspace:^", - "@backstage/core-app-api": "workspace:^" + "@backstage/core-app-api": "workspace:^", + "jest-date-mock": "^1.0.8" }, "files": [ "dist" diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabIssueAction.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabIssueAction.test.ts new file mode 100644 index 0000000000..cd85454665 --- /dev/null +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabIssueAction.test.ts @@ -0,0 +1,212 @@ +/* + * Copyright 2021 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 { PassThrough } from 'stream'; +import { getVoidLogger } from '@backstage/backend-common'; +import { createGitlabIssueAction } from './createGitlabIssueAction'; +import { ConfigReader } from '@backstage/core-app-api'; +import { ScmIntegrations } from '@backstage/integration'; +import { advanceTo, clear } from 'jest-date-mock'; + +const mockGitlabClient = { + Issues: { + create: jest.fn(), + }, +}; +jest.mock('@gitbeaker/rest', () => ({ + Gitlab: class { + constructor() { + return mockGitlabClient; + } + }, +})); + +describe('gitlab:issues:create', () => { + beforeEach(() => { + jest.clearAllMocks(); + advanceTo(new Date(1990, 9, 24, 12, 0, 0)); // Set the desired date and time + }); + + afterEach(() => { + clear(); // Reset the date mock after each test + }); + + const config = new ConfigReader({ + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'myIntegrationsToken', + apiBaseUrl: 'https://gitlab.com/api/v4', + }, + ], + }, + }); + const integrations = ScmIntegrations.fromConfig(config); + + const action = createGitlabIssueAction({ integrations }); + + it('should return a Gitlab issue when called with minimal input params', async () => { + const mockContext = { + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: 123, + title: 'Computer banks to rule the world', + // token: 'myAwesomeToken', + }, + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + mockGitlabClient.Issues.create.mockResolvedValue({ + id: 42, + web_url: 'https://gitlab.com/hangar18-/issues/42', + }); + + await action.handler({ + ...mockContext, + }); + + expect(mockGitlabClient.Issues.create).toHaveBeenCalledWith( + 123, + 'Computer banks to rule the world', + { + issueType: undefined, + description: '', + assigneeIds: [], + confidential: false, + epicId: undefined, + labels: '', + createdAt: new Date().toISOString(), + dueDate: undefined, + discussionToResolve: '', + mergeRequestToResolveDiscussionsOf: undefined, + milestoneId: undefined, + weight: undefined, + }, + ); + + expect(mockContext.output).toHaveBeenCalledWith('issueId', 42); + expect(mockContext.output).toHaveBeenCalledWith( + 'issueUrl', + 'https://gitlab.com/hangar18-/issues/42', + ); + }); + + it('should return a Gitlab issue when called with oAuth Token', async () => { + const mockContext = { + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: 123, + title: 'Computer banks to rule the world', + token: 'myAwesomeToken', + }, + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + mockGitlabClient.Issues.create.mockResolvedValue({ + id: 42, + web_url: 'https://gitlab.com/hangar18-/issues/42', + }); + + await action.handler({ + ...mockContext, + }); + + expect(mockGitlabClient.Issues.create).toHaveBeenCalledWith( + 123, + 'Computer banks to rule the world', + { + issueType: undefined, + description: '', + assigneeIds: [], + confidential: false, + epicId: undefined, + labels: '', + createdAt: new Date().toISOString(), + dueDate: undefined, + discussionToResolve: '', + mergeRequestToResolveDiscussionsOf: undefined, + milestoneId: undefined, + weight: undefined, + }, + ); + + expect(mockContext.output).toHaveBeenCalledWith('issueId', 42); + expect(mockContext.output).toHaveBeenCalledWith( + 'issueUrl', + 'https://gitlab.com/hangar18-/issues/42', + ); + }); + + it('should return a Gitlab issue when called with an epic id', async () => { + const mockContext = { + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: 123, + title: 'Instruments to sight the stars', + token: 'myAwesomeToken', + epicId: 1234, + }, + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + mockGitlabClient.Issues.create.mockResolvedValue({ + id: 42, + web_url: 'https://gitlab.com/hangar18-/issues/42', + }); + + await action.handler({ + ...mockContext, + }); + + expect(mockGitlabClient.Issues.create).toHaveBeenCalledWith( + 123, + 'Computer banks to rule the world', + { + issueType: undefined, + description: '', + assigneeIds: [], + confidential: false, + epicId: 1234, + labels: '', + createdAt: new Date().toISOString(), + dueDate: undefined, + discussionToResolve: '', + mergeRequestToResolveDiscussionsOf: undefined, + milestoneId: undefined, + weight: undefined, + }, + ); + + expect(mockContext.output).toHaveBeenCalledWith('issueId', 42); + expect(mockContext.output).toHaveBeenCalledWith( + 'issueUrl', + 'https://gitlab.com/hangar18-/issues/42', + ); + }); +}); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabIssueAction.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabIssueAction.ts index 57f7d753e4..408d307612 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabIssueAction.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabIssueAction.ts @@ -158,7 +158,6 @@ export const createGitlabIssueAction = (options: { } = commonGitlabConfig.merge(issueInputProperties).parse(ctx.input); const { host } = parseRepoUrl(repoUrl, integrations); - const api = getClient({ host, integrations, token }); let isEpicScoped = false; @@ -178,8 +177,7 @@ export const createGitlabIssueAction = (options: { ); } } - - // TODO: do I really need the convertDate? + console.log('After epic test'); const mappedCreatedAt = convertDate( String(createdAt), new Date().toISOString(), @@ -202,6 +200,11 @@ export const createGitlabIssueAction = (options: { milestoneId, weight, }; + console.log('After setting issues options'); + console.log({ issueOptions }); + console.log(`Other options: ${projectId}, ${title}`); + + console.log({ token }); const response = (await api.Issues.create( projectId, @@ -209,6 +212,8 @@ export const createGitlabIssueAction = (options: { issueOptions, )) as IssueSchema; + console.log('After calling the issues endpoint'); + ctx.output('issueId', response.id); ctx.output('issueUrl', response.web_url); } catch (error: any) { diff --git a/plugins/scaffolder-backend-module-gitlab/src/util.test.ts b/plugins/scaffolder-backend-module-gitlab/src/util.test.ts index c8307a6bd5..0829d1321f 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/util.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/util.test.ts @@ -18,19 +18,26 @@ import * as util from './util'; import { Gitlab, GroupSchema } from '@gitbeaker/rest'; // Mock the Gitlab client and its methods -jest.mock('@gitbeaker/rest', () => { - return { - Gitlab: jest.fn().mockImplementation(() => ({ - Groups: { - show: jest.fn(), - search: jest.fn(), - }, - })), - }; -}); +const setupGitlabMock = () => { + jest.mock('@gitbeaker/rest', () => { + return { + Gitlab: jest.fn().mockImplementation(() => ({ + Groups: { + show: jest.fn(), + }, + })), + }; + }); +}; describe('getTopLevelParentGroup', () => { - const config = { + beforeEach(() => { + setupGitlabMock(); + }); + + afterEach(() => jest.resetAllMocks()); + + const mockConfig = { gitlab: [ { host: 'gitlab.com', @@ -47,8 +54,8 @@ describe('getTopLevelParentGroup', () => { it('should return the top-level parent group', async () => { // Instance with token const mockGitlabClient = new Gitlab({ - host: config.gitlab[0].host, - token: config.gitlab[0].token!, + host: mockConfig.gitlab[0].host, + token: mockConfig.gitlab[0].token!, }); // Mocked nested groups diff --git a/plugins/scaffolder-backend-module-gitlab/src/util.ts b/plugins/scaffolder-backend-module-gitlab/src/util.ts index d100dfd8ef..098aa05667 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/util.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/util.ts @@ -120,7 +120,7 @@ export function getClient(props: { }; gitlabOptions[tokenType] = requestToken; - + console.log({ gitlabOptions }); return new Gitlab(gitlabOptions); } diff --git a/yarn.lock b/yarn.lock index a17bae88f1..1df2e9a722 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8655,7 +8655,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-scaffolder-backend-module-gitlab@workspace:^, @backstage/plugin-scaffolder-backend-module-gitlab@workspace:plugins/scaffolder-backend-module-gitlab": +"@backstage/plugin-scaffolder-backend-module-gitlab@workspace:plugins/scaffolder-backend-module-gitlab": version: 0.0.0-use.local resolution: "@backstage/plugin-scaffolder-backend-module-gitlab@workspace:plugins/scaffolder-backend-module-gitlab" dependencies: @@ -8668,6 +8668,7 @@ __metadata: "@backstage/plugin-scaffolder-node": "workspace:^" "@gitbeaker/node": ^35.8.0 "@gitbeaker/rest": ^39.25.0 + jest-date-mock: ^1.0.8 yaml: ^2.0.0 zod: ^3.21.4 languageName: unknown @@ -26921,7 +26922,6 @@ __metadata: "@backstage/plugin-rollbar-backend": "workspace:^" "@backstage/plugin-scaffolder-backend": "workspace:^" "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "workspace:^" - "@backstage/plugin-scaffolder-backend-module-gitlab": "workspace:^" "@backstage/plugin-scaffolder-backend-module-rails": "workspace:^" "@backstage/plugin-search-backend": "workspace:^" "@backstage/plugin-search-backend-module-catalog": "workspace:^" @@ -31108,6 +31108,13 @@ __metadata: languageName: node linkType: hard +"jest-date-mock@npm:^1.0.8": + version: 1.0.8 + resolution: "jest-date-mock@npm:1.0.8" + checksum: 7b012870440b0df742d0b651435b91625dbbf02d916633a0c70c7deb5d5087f9aadc59847602312368826325909e70e95cd412896a0c9ee1d5ac382000bf5ef9 + languageName: node + linkType: hard + "jest-diff@npm:^28.1.3": version: 28.1.3 resolution: "jest-diff@npm:28.1.3"