backstage
388 строк · 10.9 Кб
1/*
2* Copyright 2022 The Backstage Authors
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8* http://www.apache.org/licenses/LICENSE-2.0
9*
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15*/
16
17import * as util from './util';
18import { Gitlab, GroupSchema } from '@gitbeaker/rest';
19import { InputError } from '@backstage/errors';
20
21// Mock the Gitlab client and its methods
22const mockGitlabClient = {
23Groups: {
24show: jest.fn(),
25},
26Projects: {
27show: jest.fn(),
28},
29Epics: {
30all: jest.fn(),
31},
32};
33
34jest.mock('@gitbeaker/rest', () => ({
35Gitlab: class {
36constructor() {
37return mockGitlabClient;
38}
39},
40}));
41
42const mockConfig = {
43gitlab: [
44{
45host: 'gitlab.com',
46token: 'withToken',
47apiBaseUrl: 'gitlab.com/api/v4',
48},
49{
50host: 'gitlab.com',
51apiBaseUrl: 'gitlab.com/api/v4',
52},
53],
54};
55describe('getTopLevelParentGroup', () => {
56afterEach(() => jest.resetAllMocks());
57
58// Mocked nested groups
59const mockGroups: GroupSchema[] = [
60{
61id: 789,
62parent_id: 0,
63path: '',
64description: '',
65visibility: '',
66share_with_group_lock: false,
67require_two_factor_authentication: false,
68two_factor_grace_period: 0,
69project_creation_level: '',
70subgroup_creation_level: '',
71lfs_enabled: false,
72default_branch_protection: 0,
73request_access_enabled: false,
74created_at: '',
75avatar_url: '',
76full_name: '',
77full_path: '',
78web_url: '',
79name: '',
80},
81{
82id: 456,
83parent_id: 789,
84path: '',
85description: '',
86visibility: '',
87share_with_group_lock: false,
88require_two_factor_authentication: false,
89two_factor_grace_period: 0,
90project_creation_level: '',
91subgroup_creation_level: '',
92lfs_enabled: false,
93default_branch_protection: 0,
94request_access_enabled: false,
95created_at: '',
96avatar_url: '',
97full_name: '',
98full_path: '',
99web_url: '',
100name: '',
101},
102{
103id: 123,
104parent_id: 456,
105path: '',
106description: '',
107visibility: '',
108share_with_group_lock: false,
109require_two_factor_authentication: false,
110two_factor_grace_period: 0,
111project_creation_level: '',
112subgroup_creation_level: '',
113lfs_enabled: false,
114default_branch_protection: 0,
115request_access_enabled: false,
116created_at: '',
117avatar_url: '',
118full_name: '',
119full_path: '',
120web_url: '',
121name: '',
122},
123];
124
125// Top level group
126const mockTopParentGroup: GroupSchema = {
127id: 789,
128parent_id: 0,
129path: '',
130description: '',
131visibility: '',
132share_with_group_lock: false,
133require_two_factor_authentication: false,
134two_factor_grace_period: 0,
135project_creation_level: '',
136subgroup_creation_level: '',
137lfs_enabled: false,
138default_branch_protection: 0,
139request_access_enabled: false,
140created_at: '',
141avatar_url: '',
142full_name: '',
143full_path: '',
144web_url: '',
145name: '',
146};
147
148it('should return the top-level parent group if the input group has a parent in the hierarchy', async () => {
149// Instance with token
150const apiClient = new Gitlab({
151host: mockConfig.gitlab[0].host,
152token: mockConfig.gitlab[0].token!,
153});
154
155const showSpy = jest.spyOn(mockGitlabClient.Groups, 'show');
156
157// Mock implementation of Groups.show
158showSpy.mockImplementation(
159async (groupId: string | number): Promise<any> => {
160const id =
161typeof groupId === 'number' ? groupId : parseInt(groupId, 10);
162const mockGroup = mockGroups.find(group => group.id === id) || null;
163return mockGroup as GroupSchema;
164},
165);
166
167const action = util.getTopLevelParentGroup(apiClient, 123);
168
169const result = await action;
170expect(result).toEqual(mockTopParentGroup);
171});
172
173it('should return the input group if it has no parents in the hierarchy', async () => {
174// Instance with token
175const apiClient = new Gitlab({
176host: mockConfig.gitlab[0].host,
177token: mockConfig.gitlab[0].token!,
178});
179
180const showSpy = jest.spyOn(mockGitlabClient.Groups, 'show');
181
182// Mock implementation of Groups.show
183showSpy.mockImplementation(
184async (groupId: string | number): Promise<any> => {
185const id =
186typeof groupId === 'number' ? groupId : parseInt(groupId, 10);
187const mockGroup = mockGroups.find(group => group.id === id) || null;
188return mockGroup as GroupSchema;
189},
190);
191
192const action = util.getTopLevelParentGroup(apiClient, 789);
193
194const result = await action;
195expect(result).toEqual(mockTopParentGroup);
196});
197});
198
199describe('checkEpicScope', () => {
200afterEach(() => jest.resetAllMocks());
201
202it('should return true if the project is inside the epic scope', async () => {
203const apiClient = new Gitlab({
204host: mockConfig.gitlab[0].host,
205token: mockConfig.gitlab[0].token!,
206});
207
208const projectId = 123;
209const epicId = 456;
210
211// Mock project, top-level parent group, and epic
212const mockProject = {
213id: 123,
214name: 'You learn',
215namespace: { id: 789 },
216path_with_namespace: 'at-once/you-learn',
217};
218const mockTopParentGroup = {
219id: 789,
220name: 'LivingTwice',
221full_path: 'at-once/you-learn',
222};
223const mockEpic = { id: epicId, group_id: 789 };
224
225mockGitlabClient.Projects.show.mockResolvedValue(mockProject);
226mockGitlabClient.Groups.show.mockResolvedValue(mockTopParentGroup);
227mockGitlabClient.Epics.all.mockResolvedValue([mockEpic]);
228
229const result = await util.checkEpicScope(apiClient, projectId, epicId);
230
231expect(result).toBe(true);
232expect(mockGitlabClient.Projects.show).toHaveBeenCalledWith(projectId);
233expect(mockGitlabClient.Groups.show).toHaveBeenCalledWith(
234mockProject.namespace.id,
235);
236expect(mockGitlabClient.Epics.all).toHaveBeenCalledWith(
237mockTopParentGroup.id,
238);
239});
240
241it('should return false if the project is not inside the epic scope', async () => {
242const apiClient = new Gitlab({
243host: mockConfig.gitlab[0].host,
244token: mockConfig.gitlab[0].token!,
245});
246
247const projectId = 123;
248const epicId = 45;
249
250// Mock project, top-level parent group, and epic
251const mockProject = {
252id: 123,
253name: 'You learn',
254namespace: { id: 32 },
255path_with_namespace: 'at-once/you-learn',
256};
257const mockTopParentGroup = {
258id: 32,
259name: 'TheWalls',
260full_path: 'you-built/within',
261};
262
263const mockEpic = { id: epicId, group_id: 32 };
264
265mockGitlabClient.Projects.show.mockResolvedValue(mockProject);
266mockGitlabClient.Groups.show.mockResolvedValue(mockTopParentGroup);
267mockGitlabClient.Epics.all.mockResolvedValue([mockEpic]);
268
269const result = await util.checkEpicScope(apiClient, projectId, epicId);
270
271expect(result).toBe(false);
272expect(mockGitlabClient.Projects.show).toHaveBeenCalledWith(projectId);
273expect(mockGitlabClient.Groups.show).toHaveBeenCalledWith(
274mockProject.namespace.id,
275);
276expect(mockGitlabClient.Epics.all).toHaveBeenCalledWith(
277mockTopParentGroup.id,
278);
279});
280
281it('should throw an InputError if the project is not found', async () => {
282const apiClient = new Gitlab({
283host: mockConfig.gitlab[0].host,
284token: mockConfig.gitlab[0].token!,
285});
286
287const projectId = 123;
288const epicId = 456;
289
290mockGitlabClient.Projects.show.mockResolvedValue(null);
291
292await expect(
293util.checkEpicScope(apiClient, projectId, epicId),
294).rejects.toThrow(InputError);
295expect(mockGitlabClient.Projects.show).toHaveBeenCalledWith(projectId);
296});
297
298it('should throw an InputError if the top-level parent group is not found', async () => {
299const apiClient = new Gitlab({
300host: mockConfig.gitlab[0].host,
301token: mockConfig.gitlab[0].token!,
302});
303
304const projectId = 123;
305const epicId = 456;
306
307mockGitlabClient.Projects.show.mockResolvedValue({
308id: 123,
309name: 'You learn',
310namespace: { id: 789 },
311path_with_namespace: 'at-once/you-learn',
312});
313mockGitlabClient.Groups.show.mockResolvedValue(null);
314
315await expect(
316util.checkEpicScope(apiClient, projectId, epicId),
317).rejects.toThrow(InputError);
318expect(mockGitlabClient.Projects.show).toHaveBeenCalledWith(projectId);
319expect(mockGitlabClient.Groups.show).toHaveBeenCalledWith(789);
320});
321
322it('should throw an InputError if the epic is not found', async () => {
323const apiClient = new Gitlab({
324host: mockConfig.gitlab[0].host,
325token: mockConfig.gitlab[0].token!,
326});
327
328const projectId = 123;
329const epicId = 456;
330
331mockGitlabClient.Projects.show.mockResolvedValue({
332id: 123,
333name: 'You learn',
334namespace: { id: 789 },
335path_with_namespace: 'at-once/you-learn',
336});
337mockGitlabClient.Groups.show.mockResolvedValue({
338id: 789,
339name: 'LivingTwice',
340full_path: 'at-once/you-learn',
341});
342mockGitlabClient.Epics.all.mockResolvedValue([]);
343
344await expect(
345util.checkEpicScope(apiClient, projectId, epicId),
346).rejects.toThrow(InputError);
347expect(mockGitlabClient.Projects.show).toHaveBeenCalledWith(projectId);
348expect(mockGitlabClient.Groups.show).toHaveBeenCalledWith(789);
349expect(mockGitlabClient.Epics.all).toHaveBeenCalledWith(789);
350});
351});
352
353describe('convertDate', () => {
354it('should convert a valid input date with miliseconds to an ISO string', () => {
355const inputDate = '1970-01-01T12:00:00.000Z';
356const defaultDate = '1978-10-09T12:00:00Z';
357
358const result = util.convertDate(inputDate, defaultDate);
359
360expect(result).toEqual('1970-01-01T12:00:00.000Z');
361});
362
363it('should convert a valid input date to an ISO string', () => {
364const inputDate = '1970-01-01T12:00:00Z';
365const defaultDate = '1978-10-09T12:00:00Z';
366
367const result = util.convertDate(inputDate, defaultDate);
368
369expect(result).toEqual('1970-01-01T12:00:00.000Z');
370});
371
372it('should use default date if input date is undefined', () => {
373const inputDate = undefined;
374const defaultDate = '1970-01-01T12:00:00Z';
375
376const result = util.convertDate(inputDate, defaultDate);
377
378expect(result).toEqual('1970-01-01T12:00:00.000Z');
379});
380
381it('should throw an InputError if input date is invalid', () => {
382const inputDate = 'invalidDate';
383const defaultDate = '2023-02-01T12:00:00Z';
384
385// Expecting an InputError to be thrown
386expect(() => util.convertDate(inputDate, defaultDate)).toThrow(InputError);
387});
388});
389