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.bitbucketserver;
22 import java.util.List;
23 import java.util.Optional;
24 import org.junit.jupiter.api.Test;
25 import org.junit.jupiter.api.extension.ExtendWith;
26 import org.mockito.Answers;
27 import org.mockito.ArgumentCaptor;
28 import org.mockito.InjectMocks;
29 import org.mockito.Mock;
30 import org.mockito.junit.jupiter.MockitoExtension;
31 import org.sonar.alm.client.bitbucketserver.BitbucketServerRestClient;
32 import org.sonar.alm.client.bitbucketserver.Branch;
33 import org.sonar.alm.client.bitbucketserver.BranchesList;
34 import org.sonar.alm.client.bitbucketserver.Project;
35 import org.sonar.alm.client.bitbucketserver.Repository;
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.assertThatThrownBy;
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 BitbucketServerProjectCreatorTest {
62 private static final String USER_LOGIN = "userLogin";
63 private static final String USER_UUID = "userUuid";
64 private static final String DOP_REPOSITORY_ID = "repository";
65 private static final String DOP_PROJECT_ID = "project";
66 private static final String URL = "http://rest/api/1.0/projects/projectKey/repos/repoName";
67 private static final String ALM_SETTING_KEY = "bitbucketserver_config_1";
68 private static final String ALM_SETTING_UUID = "almSettingUuid";
69 private static final String USER_PAT = "1234";
70 private static final String PROJECT_UUID = "projectUuid";
71 private static final String MAIN_BRANCH_NAME = "main";
73 @Mock(answer = Answers.RETURNS_DEEP_STUBS)
74 private DbClient dbClient;
76 private AlmSettingDto almSettingDto;
78 private DevOpsProjectDescriptor devOpsProjectDescriptor;
80 private UserSession userSession;
82 private BitbucketServerRestClient bitbucketServerRestClient;
84 private ProjectCreator projectCreator;
86 private ProjectKeyGenerator projectKeyGenerator;
89 private BitbucketServerProjectCreator underTest;
92 void isScanAllowedUsingPermissionsFromDevopsPlatform_shouldThrowUnsupportedOperationException() {
93 assertThatThrownBy(() -> underTest.isScanAllowedUsingPermissionsFromDevopsPlatform())
94 .isInstanceOf(UnsupportedOperationException.class)
95 .hasMessage("Not Implemented");
99 void createProjectAndBindToDevOpsPlatform_whenPatIsMissing_shouldThrow() {
100 mockValidUserSession();
101 mockValidAlmSettingsDto();
102 assertThatThrownBy(() -> underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, false, null, null))
103 .isInstanceOf(IllegalArgumentException.class)
104 .hasMessage("personal access token for 'bitbucketserver_config_1' is missing");
108 void createProjectAndBindToDevOpsPlatform_whenBitBucketProjectNotProvided_shouldThrow() {
109 mockValidUserSession();
110 mockValidAlmSettingsDto();
111 mockValidPatForUser();
113 assertThatThrownBy(() -> underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, false, "projectKey", null))
114 .isInstanceOf(IllegalArgumentException.class)
115 .hasMessage("The BitBucket project, in which the repository null is located, is mandatory");
119 void createProjectAndBindToDevOpsPlatform_whenRepositoryNotFound_shouldThrow() {
120 mockValidUserSession();
121 mockValidAlmSettingsDto();
122 mockValidPatForUser();
123 mockValidProjectDescriptor();
124 when(bitbucketServerRestClient.getRepo(URL, USER_PAT, DOP_PROJECT_ID, DOP_REPOSITORY_ID)).thenThrow(new IllegalArgumentException("Problem"));
126 assertThatThrownBy(() -> underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, false, "projectKey", null))
127 .isInstanceOf(IllegalArgumentException.class)
128 .hasMessage("Problem");
132 void createProjectAndBindToDevOpsPlatform_whenRepoFoundOnBitbucket_successfullyCreatesProject() {
133 mockValidUserSession();
134 mockValidAlmSettingsDto();
135 mockValidPatForUser();
136 mockValidProjectDescriptor();
137 mockValidBitBucketRepository();
138 mockProjectCreation("projectKey", "projectName");
140 underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, true, "projectKey", "projectName");
142 ArgumentCaptor<ProjectAlmSettingDto> projectAlmSettingCaptor = ArgumentCaptor.forClass(ProjectAlmSettingDto.class);
143 verify(dbClient.projectAlmSettingDao()).insertOrUpdate(any(), projectAlmSettingCaptor.capture(), eq(ALM_SETTING_KEY), eq("projectName"), eq("projectKey"));
144 ProjectAlmSettingDto createdProjectAlmSettingDto = projectAlmSettingCaptor.getValue();
146 assertThat(createdProjectAlmSettingDto.getAlmSettingUuid()).isEqualTo(ALM_SETTING_UUID);
147 assertThat(createdProjectAlmSettingDto.getAlmRepo()).isEqualTo(DOP_REPOSITORY_ID);
148 assertThat(createdProjectAlmSettingDto.getProjectUuid()).isEqualTo(PROJECT_UUID);
149 assertThat(createdProjectAlmSettingDto.getMonorepo()).isTrue();
153 void createProjectAndBindToDevOpsPlatform_whenNoKeyAndNameSpecified_generatesKeyAndUsersBitbucketRepositoryName() {
154 mockValidUserSession();
155 mockValidAlmSettingsDto();
156 mockValidPatForUser();
157 mockValidProjectDescriptor();
158 Repository repository = mockValidBitBucketRepository();
159 String generatedProjectKey = "generatedProjectKey";
160 when(projectKeyGenerator.generateUniqueProjectKey(repository.getProject().getKey(), repository.getSlug())).thenReturn(generatedProjectKey);
161 mockProjectCreation(generatedProjectKey, repository.getName());
163 underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, true, null, null);
165 ArgumentCaptor<ProjectAlmSettingDto> projectAlmSettingCaptor = ArgumentCaptor.forClass(ProjectAlmSettingDto.class);
166 verify(dbClient.projectAlmSettingDao()).insertOrUpdate(any(), projectAlmSettingCaptor.capture(), eq(ALM_SETTING_KEY), eq(repository.getName()), eq(generatedProjectKey));
167 ProjectAlmSettingDto createdProjectAlmSettingDto = projectAlmSettingCaptor.getValue();
169 assertThat(createdProjectAlmSettingDto.getAlmSettingUuid()).isEqualTo(ALM_SETTING_UUID);
170 assertThat(createdProjectAlmSettingDto.getAlmRepo()).isEqualTo(DOP_REPOSITORY_ID);
171 assertThat(createdProjectAlmSettingDto.getProjectUuid()).isEqualTo(PROJECT_UUID);
172 assertThat(createdProjectAlmSettingDto.getMonorepo()).isTrue();
175 private void mockValidUserSession() {
176 lenient().when(userSession.getLogin()).thenReturn(USER_LOGIN);
177 lenient().when(userSession.getUuid()).thenReturn(USER_UUID);
180 private void mockValidPatForUser() {
181 AlmPatDto almPatDto = mock();
182 when(almPatDto.getPersonalAccessToken()).thenReturn(USER_PAT);
183 when(dbClient.almPatDao().selectByUserAndAlmSetting(any(), eq(USER_UUID), eq(almSettingDto))).thenReturn(Optional.of(almPatDto));
186 private void mockValidAlmSettingsDto() {
187 lenient().when(almSettingDto.getUrl()).thenReturn(URL);
188 lenient().when(almSettingDto.getKey()).thenReturn(ALM_SETTING_KEY);
189 lenient().when(almSettingDto.getUuid()).thenReturn(ALM_SETTING_UUID);
192 private void mockValidProjectDescriptor() {
193 lenient().when(devOpsProjectDescriptor.alm()).thenReturn(ALM.BITBUCKET);
194 lenient().when(devOpsProjectDescriptor.url()).thenReturn(URL);
195 lenient().when(devOpsProjectDescriptor.repositoryIdentifier()).thenReturn(DOP_REPOSITORY_ID);
196 lenient().when(devOpsProjectDescriptor.projectIdentifier()).thenReturn(DOP_PROJECT_ID);
199 private Repository mockValidBitBucketRepository() {
200 Repository repository = new Repository(DOP_REPOSITORY_ID, "Repository name", 12L, new Project(DOP_PROJECT_ID, "Project name", 42L));
201 when(bitbucketServerRestClient.getRepo(URL, USER_PAT, DOP_PROJECT_ID, DOP_REPOSITORY_ID)).thenReturn(repository);
203 BranchesList branches = new BranchesList(List.of(
204 new Branch(MAIN_BRANCH_NAME, true),
205 new Branch("feature", false)));
206 when(bitbucketServerRestClient.getBranches(URL, USER_PAT, DOP_PROJECT_ID, DOP_REPOSITORY_ID)).thenReturn(branches);
211 private void mockProjectCreation(String projectKey, String projectName) {
212 ComponentCreationData componentCreationData = mock();
213 ProjectDto projectDto = mock();
214 when(componentCreationData.projectDto()).thenReturn(projectDto);
215 when(projectDto.getUuid()).thenReturn(PROJECT_UUID);
216 when(projectDto.getKey()).thenReturn(projectKey);
217 when(projectDto.getName()).thenReturn(projectName);
218 when(projectCreator.createProject(any(), eq(projectKey), eq(projectName), eq(MAIN_BRANCH_NAME), eq(CreationMethod.ALM_IMPORT_API)))
219 .thenReturn(componentCreationData);