3 * Copyright (C) 2009-2024 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.common.almsettings.gitlab;
22 import java.util.List;
23 import java.util.Optional;
24 import org.junit.jupiter.api.BeforeEach;
25 import org.junit.jupiter.api.Test;
26 import org.junit.jupiter.api.extension.ExtendWith;
27 import org.mockito.Answers;
28 import org.mockito.ArgumentCaptor;
29 import org.mockito.InjectMocks;
30 import org.mockito.Mock;
31 import org.mockito.junit.jupiter.MockitoExtension;
32 import org.sonar.alm.client.gitlab.GitLabBranch;
33 import org.sonar.alm.client.gitlab.GitlabApplicationClient;
34 import org.sonar.alm.client.gitlab.GitlabServerException;
35 import org.sonar.alm.client.gitlab.Project;
36 import org.sonar.db.DbClient;
37 import org.sonar.db.DbSession;
38 import org.sonar.db.alm.pat.AlmPatDto;
39 import org.sonar.db.alm.setting.ALM;
40 import org.sonar.db.alm.setting.AlmSettingDto;
41 import org.sonar.db.alm.setting.ProjectAlmSettingDto;
42 import org.sonar.db.project.CreationMethod;
43 import org.sonar.db.project.ProjectDto;
44 import org.sonar.server.common.almintegration.ProjectKeyGenerator;
45 import org.sonar.server.common.almsettings.DevOpsProjectDescriptor;
46 import org.sonar.server.common.project.ProjectCreator;
47 import org.sonar.server.component.ComponentCreationData;
48 import org.sonar.server.user.UserSession;
50 import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
51 import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
52 import static org.mockito.ArgumentMatchers.any;
53 import static org.mockito.ArgumentMatchers.eq;
54 import static org.mockito.Mockito.lenient;
55 import static org.mockito.Mockito.mock;
56 import static org.mockito.Mockito.verify;
57 import static org.mockito.Mockito.when;
59 @ExtendWith(MockitoExtension.class)
60 class GitlabProjectCreatorTest {
62 private static final String PROJECT_UUID = "projectUuid";
64 private static final String USER_LOGIN = "userLogin";
65 private static final String USER_UUID = "userUuid";
67 private static final String REPOSITORY_PATH_WITH_NAMESPACE = "pathWith/namespace";
69 private static final String GITLAB_PROJECT_NAME = "gitlabProjectName";
71 private static final String REPOSITORY_ID = "1234";
73 private static final String MAIN_BRANCH_NAME = "defaultBranch";
75 private static final String ALM_SETTING_KEY = "gitlab_config_1";
76 private static final String ALM_SETTING_UUID = "almSettingUuid";
78 private static final String USER_PAT = "1234";
80 public static final String GITLAB_URL = "http://api.com";
81 @Mock(answer = Answers.RETURNS_DEEP_STUBS)
82 private DbClient dbClient;
85 private ProjectKeyGenerator projectKeyGenerator;
88 private ProjectCreator projectCreator;
91 private AlmSettingDto almSettingDto;
93 private DevOpsProjectDescriptor devOpsProjectDescriptor;
95 private GitlabApplicationClient gitlabApplicationClient;
97 private UserSession userSession;
100 private GitlabProjectCreator underTest;
104 lenient().when(userSession.getLogin()).thenReturn(USER_LOGIN);
105 lenient().when(userSession.getUuid()).thenReturn(USER_UUID);
107 lenient().when(almSettingDto.getUrl()).thenReturn(GITLAB_URL);
108 lenient().when(almSettingDto.getKey()).thenReturn(ALM_SETTING_KEY);
109 lenient().when(almSettingDto.getUuid()).thenReturn(ALM_SETTING_UUID);
111 lenient().when(devOpsProjectDescriptor.projectIdentifier()).thenReturn(REPOSITORY_ID);
112 lenient().when(devOpsProjectDescriptor.url()).thenReturn(GITLAB_URL);
113 lenient().when(devOpsProjectDescriptor.alm()).thenReturn(ALM.GITLAB);
117 void isScanAllowedUsingPermissionsFromDevopsPlatform_shouldThrowUnsupportedOperationException() {
118 assertThatExceptionOfType(UnsupportedOperationException.class)
119 .isThrownBy(() -> underTest.isScanAllowedUsingPermissionsFromDevopsPlatform())
120 .withMessage("Not Implemented");
124 void createProjectAndBindToDevOpsPlatform_whenUserHasNoPat_throws() {
125 assertThatExceptionOfType(IllegalArgumentException.class)
126 .isThrownBy(() -> underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, false, null, null))
127 .withMessage("personal access token for 'gitlab_config_1' is missing");
131 void createProjectAndBindToDevOpsPlatform_whenRepoNotFound_throws() {
133 when(gitlabApplicationClient.getProject(GITLAB_URL, USER_PAT, Long.valueOf(REPOSITORY_ID))).thenThrow(new GitlabServerException(404, "Not found"));
134 assertThatExceptionOfType(IllegalStateException.class)
135 .isThrownBy(() -> underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, false, null, null))
136 .withMessage("Failed to fetch GitLab project with ID '1234' from 'http://api.com'");
141 void createProjectAndBindToDevOpsPlatform_whenRepoFoundOnGitlab_successfullyCreatesProject() {
145 mockProjectCreation("projectKey", "projectName");
147 underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, true, "projectKey", "projectName");
149 ArgumentCaptor<ProjectAlmSettingDto> projectAlmSettingCaptor = ArgumentCaptor.forClass(ProjectAlmSettingDto.class);
151 verify(dbClient.projectAlmSettingDao()).insertOrUpdate(any(), projectAlmSettingCaptor.capture(), eq(ALM_SETTING_KEY), eq("projectName"), eq("projectKey"));
153 ProjectAlmSettingDto createdProjectAlmSettingDto = projectAlmSettingCaptor.getValue();
155 assertThat(createdProjectAlmSettingDto.getAlmSettingUuid()).isEqualTo(ALM_SETTING_UUID);
156 assertThat(createdProjectAlmSettingDto.getAlmRepo()).isEqualTo(REPOSITORY_ID);
157 assertThat(createdProjectAlmSettingDto.getProjectUuid()).isEqualTo(PROJECT_UUID);
158 assertThat(createdProjectAlmSettingDto.getMonorepo()).isTrue();
162 void createProjectAndBindToDevOpsPlatform_whenNoKeyAndNameSpecified_generatesKeyAndUsersGitlabProjectName() {
167 String generatedProjectKey = "generatedProjectKey";
168 when(projectKeyGenerator.generateUniqueProjectKey(REPOSITORY_PATH_WITH_NAMESPACE)).thenReturn(generatedProjectKey);
170 mockProjectCreation(generatedProjectKey, GITLAB_PROJECT_NAME);
172 underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, true, null, null);
174 ArgumentCaptor<ProjectAlmSettingDto> projectAlmSettingCaptor = ArgumentCaptor.forClass(ProjectAlmSettingDto.class);
176 verify(dbClient.projectAlmSettingDao()).insertOrUpdate(any(), projectAlmSettingCaptor.capture(), eq(ALM_SETTING_KEY), eq(GITLAB_PROJECT_NAME), eq(generatedProjectKey));
178 ProjectAlmSettingDto createdProjectAlmSettingDto = projectAlmSettingCaptor.getValue();
180 assertThat(createdProjectAlmSettingDto.getAlmSettingUuid()).isEqualTo(ALM_SETTING_UUID);
181 assertThat(createdProjectAlmSettingDto.getAlmRepo()).isEqualTo(REPOSITORY_ID);
182 assertThat(createdProjectAlmSettingDto.getProjectUuid()).isEqualTo(PROJECT_UUID);
183 assertThat(createdProjectAlmSettingDto.getMonorepo()).isTrue();
186 private void mockPatForUser() {
187 AlmPatDto almPatDto = mock();
188 when(almPatDto.getPersonalAccessToken()).thenReturn(USER_PAT);
189 when(dbClient.almPatDao().selectByUserAndAlmSetting(any(), eq(USER_UUID), eq(almSettingDto))).thenReturn(Optional.of(almPatDto));
192 private void mockGitlabProject() {
193 Project project = mock(Project.class);
194 lenient().when(project.getPathWithNamespace()).thenReturn(REPOSITORY_PATH_WITH_NAMESPACE);
195 when(project.getName()).thenReturn(GITLAB_PROJECT_NAME);
196 when(gitlabApplicationClient.getProject(GITLAB_URL, USER_PAT, Long.valueOf(REPOSITORY_ID))).thenReturn(project);
200 private void mockMainBranch() {
201 when(gitlabApplicationClient.getBranches(GITLAB_URL, USER_PAT, Long.valueOf(REPOSITORY_ID)))
202 .thenReturn(List.of(new GitLabBranch("notMain", false), new GitLabBranch(MAIN_BRANCH_NAME, true)));
205 private void mockProjectCreation(String projectKey, String projectName) {
206 ComponentCreationData componentCreationData = mock();
207 ProjectDto projectDto = mock();
208 when(componentCreationData.projectDto()).thenReturn(projectDto);
209 when(projectDto.getUuid()).thenReturn(PROJECT_UUID);
210 when(projectDto.getKey()).thenReturn(projectKey);
211 when(projectDto.getName()).thenReturn(projectName);
212 when(projectCreator.createProject(any(), eq(projectKey), eq(projectName), eq(MAIN_BRANCH_NAME), eq(CreationMethod.ALM_IMPORT_API)))
213 .thenReturn(componentCreationData);