]> source.dussan.org Git - sonarqube.git/blob
a25e53acd6d258e9c2a2354e2f1b530bd812187b
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
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.
10  *
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.
15  *
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.
19  */
20 package org.sonar.server.almintegration.ws.bitbucketcloud;
21
22 import java.util.Optional;
23 import org.junit.Before;
24 import org.junit.Rule;
25 import org.junit.Test;
26 import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudRestClient;
27 import org.sonar.alm.client.bitbucket.bitbucketcloud.MainBranch;
28 import org.sonar.alm.client.bitbucket.bitbucketcloud.Project;
29 import org.sonar.alm.client.bitbucket.bitbucketcloud.Repository;
30 import org.sonar.api.server.ws.WebService;
31 import org.sonar.api.utils.System2;
32 import org.sonar.core.platform.EditionProvider;
33 import org.sonar.core.platform.PlatformEditionProvider;
34 import org.sonar.core.util.SequenceUuidFactory;
35 import org.sonar.db.DbTester;
36 import org.sonar.db.alm.pat.AlmPatDto;
37 import org.sonar.db.alm.setting.AlmSettingDto;
38 import org.sonar.db.alm.setting.ProjectAlmSettingDto;
39 import org.sonar.db.component.BranchDto;
40 import org.sonar.db.newcodeperiod.NewCodePeriodDto;
41 import org.sonar.db.project.ProjectDto;
42 import org.sonar.db.user.UserDto;
43 import org.sonar.server.almintegration.ws.ImportHelper;
44 import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
45 import org.sonar.server.component.ComponentUpdater;
46 import org.sonar.server.es.TestProjectIndexers;
47 import org.sonar.server.exceptions.BadRequestException;
48 import org.sonar.server.exceptions.ForbiddenException;
49 import org.sonar.server.exceptions.NotFoundException;
50 import org.sonar.server.exceptions.UnauthorizedException;
51 import org.sonar.server.favorite.FavoriteUpdater;
52 import org.sonar.server.l18n.I18nRule;
53 import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver;
54 import org.sonar.server.permission.PermissionTemplateService;
55 import org.sonar.server.project.DefaultBranchNameResolver;
56 import org.sonar.server.project.ProjectDefaultVisibility;
57 import org.sonar.server.project.Visibility;
58 import org.sonar.server.tester.UserSessionRule;
59 import org.sonar.server.ws.TestRequest;
60 import org.sonar.server.ws.WsActionTester;
61 import org.sonarqube.ws.Projects;
62
63 import static java.util.Objects.requireNonNull;
64 import static org.assertj.core.api.Assertions.assertThat;
65 import static org.assertj.core.api.Assertions.assertThatThrownBy;
66 import static org.assertj.core.api.Assertions.tuple;
67 import static org.mockito.ArgumentMatchers.any;
68 import static org.mockito.Mockito.mock;
69 import static org.mockito.Mockito.verify;
70 import static org.mockito.Mockito.when;
71 import static org.sonar.db.alm.integration.pat.AlmPatsTesting.newAlmPatDto;
72 import static org.sonar.db.newcodeperiod.NewCodePeriodType.NUMBER_OF_DAYS;
73 import static org.sonar.db.newcodeperiod.NewCodePeriodType.REFERENCE_BRANCH;
74 import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS;
75 import static org.sonar.db.permission.GlobalPermission.SCAN;
76 import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_CODE_DEFINITION_TYPE;
77 import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_CODE_DEFINITION_VALUE;
78
79 public class ImportBitbucketCloudRepoActionIT {
80
81   private static final String GENERATED_PROJECT_KEY = "TEST_PROJECT_KEY";
82
83   @Rule
84   public UserSessionRule userSession = UserSessionRule.standalone();
85   @Rule
86   public DbTester db = DbTester.create(true);
87   @Rule
88   public final I18nRule i18n = new I18nRule();
89
90   private final ProjectDefaultVisibility projectDefaultVisibility = mock(ProjectDefaultVisibility.class);
91   private final BitbucketCloudRestClient bitbucketCloudRestClient = mock(BitbucketCloudRestClient.class);
92
93   DefaultBranchNameResolver defaultBranchNameResolver = mock(DefaultBranchNameResolver.class);
94   private final ComponentUpdater componentUpdater = new ComponentUpdater(db.getDbClient(), i18n, System2.INSTANCE,
95     mock(PermissionTemplateService.class), new FavoriteUpdater(db.getDbClient()), new TestProjectIndexers(), new SequenceUuidFactory(),
96     defaultBranchNameResolver, true);
97
98   private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession);
99   private final ProjectKeyGenerator projectKeyGenerator = mock(ProjectKeyGenerator.class);
100   private PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class);
101   private NewCodeDefinitionResolver newCodeDefinitionResolver = new NewCodeDefinitionResolver(db.getDbClient(), editionProvider);
102   private final WsActionTester ws = new WsActionTester(new ImportBitbucketCloudRepoAction(db.getDbClient(), userSession,
103     bitbucketCloudRestClient, projectDefaultVisibility, componentUpdater, importHelper, projectKeyGenerator, newCodeDefinitionResolver, defaultBranchNameResolver));
104
105   @Before
106   public void before() {
107     when(projectDefaultVisibility.get(any())).thenReturn(Visibility.PRIVATE);
108     when(projectKeyGenerator.generateUniqueProjectKey(any(), any())).thenReturn(GENERATED_PROJECT_KEY);
109   }
110
111   @Test
112   public void import_project() {
113     UserDto user = db.users().insertUser();
114     userSession.logIn(user).addPermission(PROVISION_PROJECTS);
115     AlmSettingDto almSetting = db.almSettings().insertBitbucketCloudAlmSetting();
116     db.almPats().insert(dto -> {
117       dto.setAlmSettingUuid(almSetting.getUuid());
118       dto.setUserUuid(user.getUuid());
119     });
120     Repository repo = getGsonBBCRepo();
121     when(bitbucketCloudRestClient.getRepo(any(), any(), any())).thenReturn(repo);
122
123     Projects.CreateWsResponse response = ws.newRequest()
124       .setParam("almSetting", almSetting.getKey())
125       .setParam("repositorySlug", "repo-slug-1")
126       .executeProtobuf(Projects.CreateWsResponse.class);
127
128     Projects.CreateWsResponse.Project result = response.getProject();
129     assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
130     assertThat(result.getName()).isEqualTo(repo.getName());
131
132     Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
133     assertThat(projectDto).isPresent();
134     Optional<ProjectAlmSettingDto> projectAlmSettingDto = db.getDbClient().projectAlmSettingDao().selectByProject(db.getSession(), projectDto.get());
135     assertThat(projectAlmSettingDto).isPresent();
136     assertThat(projectAlmSettingDto.get().getAlmRepo()).isEqualTo("repo-slug-1");
137
138     Optional<BranchDto> branchDto = db.getDbClient().branchDao().selectByBranchKey(db.getSession(), projectDto.get().getUuid(), "develop");
139     assertThat(branchDto).isPresent();
140     assertThat(branchDto.get().isMain()).isTrue();
141     verify(projectKeyGenerator).generateUniqueProjectKey(requireNonNull(almSetting.getAppId()), repo.getSlug());
142
143     assertThat(db.getDbClient().newCodePeriodDao().selectAll(db.getSession()))
144       .isEmpty();
145   }
146
147   @Test
148   public void import_project_with_NCD_developer_edition() {
149     when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.DEVELOPER));
150
151     UserDto user = db.users().insertUser();
152     userSession.logIn(user).addPermission(PROVISION_PROJECTS);
153     AlmSettingDto almSetting = db.almSettings().insertBitbucketCloudAlmSetting();
154     db.almPats().insert(dto -> {
155       dto.setAlmSettingUuid(almSetting.getUuid());
156       dto.setUserUuid(user.getUuid());
157     });
158     Repository repo = getGsonBBCRepo();
159     when(bitbucketCloudRestClient.getRepo(any(), any(), any())).thenReturn(repo);
160
161     Projects.CreateWsResponse response = ws.newRequest()
162       .setParam("almSetting", almSetting.getKey())
163       .setParam("repositorySlug", "repo-slug-1")
164       .setParam(PARAM_NEW_CODE_DEFINITION_TYPE, "NUMBER_OF_DAYS")
165       .setParam(PARAM_NEW_CODE_DEFINITION_VALUE, "30")
166       .executeProtobuf(Projects.CreateWsResponse.class);
167
168     Projects.CreateWsResponse.Project result = response.getProject();
169     assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
170     assertThat(result.getName()).isEqualTo(repo.getName());
171
172     Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
173     assertThat(projectDto).isPresent();
174
175     assertThat(db.getDbClient().newCodePeriodDao().selectByProject(db.getSession(),  projectDto.get().getUuid()))
176       .isPresent()
177       .get()
178       .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue, NewCodePeriodDto::getBranchUuid)
179       .containsExactly(NUMBER_OF_DAYS, "30", null);
180   }
181
182   @Test
183   public void import_project_with_NCD_community_edition() {
184     when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.COMMUNITY));
185
186     UserDto user = db.users().insertUser();
187     userSession.logIn(user).addPermission(PROVISION_PROJECTS);
188     AlmSettingDto almSetting = db.almSettings().insertBitbucketCloudAlmSetting();
189     db.almPats().insert(dto -> {
190       dto.setAlmSettingUuid(almSetting.getUuid());
191       dto.setUserUuid(user.getUuid());
192     });
193     Repository repo = getGsonBBCRepo();
194     when(bitbucketCloudRestClient.getRepo(any(), any(), any())).thenReturn(repo);
195
196     Projects.CreateWsResponse response = ws.newRequest()
197       .setParam("almSetting", almSetting.getKey())
198       .setParam("repositorySlug", "repo-slug-1")
199       .setParam(PARAM_NEW_CODE_DEFINITION_TYPE, "NUMBER_OF_DAYS")
200       .setParam(PARAM_NEW_CODE_DEFINITION_VALUE, "30")
201       .executeProtobuf(Projects.CreateWsResponse.class);
202
203     Projects.CreateWsResponse.Project result = response.getProject();
204     assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
205     assertThat(result.getName()).isEqualTo(repo.getName());
206
207     Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
208     assertThat(projectDto).isPresent();
209
210     String projectUuid = projectDto.get().getUuid();
211     assertThat(db.getDbClient().newCodePeriodDao().selectByBranch(db.getSession(), projectUuid, projectUuid))
212       .isPresent()
213       .get()
214       .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue, NewCodePeriodDto::getBranchUuid)
215       .containsExactly(NUMBER_OF_DAYS, "30", projectUuid);
216   }
217
218   @Test
219   public void import_project_reference_branch_ncd_no_default_branch_name() {
220     when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.DEVELOPER));
221     when(defaultBranchNameResolver.getEffectiveMainBranchName()).thenReturn("default-branch");
222
223     UserDto user = db.users().insertUser();
224     userSession.logIn(user).addPermission(PROVISION_PROJECTS);
225     AlmSettingDto almSetting = db.almSettings().insertBitbucketCloudAlmSetting();
226     db.almPats().insert(dto -> {
227       dto.setAlmSettingUuid(almSetting.getUuid());
228       dto.setUserUuid(user.getUuid());
229     });
230     Repository repo = getGsonBBCRepoWithNoMainBranchName();
231     when(bitbucketCloudRestClient.getRepo(any(), any(), any())).thenReturn(repo);
232
233     Projects.CreateWsResponse response = ws.newRequest()
234       .setParam("almSetting", almSetting.getKey())
235       .setParam("repositorySlug", "repo-slug-1")
236       .setParam(PARAM_NEW_CODE_DEFINITION_TYPE, "REFERENCE_BRANCH")
237       .executeProtobuf(Projects.CreateWsResponse.class);
238
239     Projects.CreateWsResponse.Project result = response.getProject();
240     assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
241     assertThat(result.getName()).isEqualTo(repo.getName());
242
243     Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
244     assertThat(projectDto).isPresent();
245
246     assertThat(db.getDbClient().newCodePeriodDao().selectByProject(db.getSession(),  projectDto.get().getUuid()))
247       .isPresent()
248       .get()
249       .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue)
250       .containsExactly(REFERENCE_BRANCH, "default-branch");
251   }
252
253   @Test
254   public void import_project_reference_branch_NCD() {
255     when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.DEVELOPER));
256
257     UserDto user = db.users().insertUser();
258     userSession.logIn(user).addPermission(PROVISION_PROJECTS);
259     AlmSettingDto almSetting = db.almSettings().insertBitbucketCloudAlmSetting();
260     db.almPats().insert(dto -> {
261       dto.setAlmSettingUuid(almSetting.getUuid());
262       dto.setUserUuid(user.getUuid());
263     });
264     Repository repo = getGsonBBCRepo();
265     when(bitbucketCloudRestClient.getRepo(any(), any(), any())).thenReturn(repo);
266
267     Projects.CreateWsResponse response = ws.newRequest()
268       .setParam("almSetting", almSetting.getKey())
269       .setParam("repositorySlug", "repo-slug-1")
270       .setParam(PARAM_NEW_CODE_DEFINITION_TYPE, "REFERENCE_BRANCH")
271       .executeProtobuf(Projects.CreateWsResponse.class);
272
273     Projects.CreateWsResponse.Project result = response.getProject();
274     assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
275     assertThat(result.getName()).isEqualTo(repo.getName());
276
277     Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
278     assertThat(projectDto).isPresent();
279
280     assertThat(db.getDbClient().newCodePeriodDao().selectByProject(db.getSession(),  projectDto.get().getUuid()))
281       .isPresent()
282       .get()
283       .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue)
284       .containsExactly(REFERENCE_BRANCH, "develop");
285   }
286
287   @Test
288   public void import_project_throw_IAE_when_newCodeDefinitionValue_provided_and_no_newCodeDefinitionType() {
289     UserDto user = db.users().insertUser();
290     userSession.logIn(user).addPermission(PROVISION_PROJECTS);
291     AlmSettingDto almSetting = db.almSettings().insertBitbucketCloudAlmSetting();
292     db.almPats().insert(dto -> {
293       dto.setAlmSettingUuid(almSetting.getUuid());
294       dto.setUserUuid(user.getUuid());
295     });
296     Repository repo = getGsonBBCRepo();
297     when(bitbucketCloudRestClient.getRepo(any(), any(), any())).thenReturn(repo);
298
299     TestRequest request = ws.newRequest()
300       .setParam("almSetting", almSetting.getKey())
301       .setParam("repositorySlug", "repo-slug-1")
302       .setParam(PARAM_NEW_CODE_DEFINITION_VALUE, "30");
303
304     assertThatThrownBy(() -> request.executeProtobuf(Projects.CreateWsResponse.class))
305       .isInstanceOf(IllegalArgumentException.class)
306       .hasMessage("New code definition type is required when new code definition value is provided");
307   }
308
309   @Test
310   public void fail_project_already_exist() {
311     UserDto user = db.users().insertUser();
312     userSession.logIn(user).addPermission(PROVISION_PROJECTS);
313     AlmSettingDto almSetting = db.almSettings().insertGitHubAlmSetting();
314     db.almPats().insert(dto -> {
315       dto.setAlmSettingUuid(almSetting.getUuid());
316       dto.setUserUuid(user.getUuid());
317     });
318     Repository repo = getGsonBBCRepo();
319     db.components().insertPublicProject(p -> p.setKey(GENERATED_PROJECT_KEY)).getMainBranchComponent();
320
321     when(bitbucketCloudRestClient.getRepo(any(), any(), any())).thenReturn(repo);
322
323     TestRequest request = ws.newRequest()
324       .setParam("almSetting", almSetting.getKey())
325       .setParam("repositorySlug", "repo-slug-1");
326
327     assertThatThrownBy(request::execute)
328       .isInstanceOf(BadRequestException.class)
329       .hasMessage("Could not create Project with key: \"%s\". A similar key already exists: \"%s\"", GENERATED_PROJECT_KEY, GENERATED_PROJECT_KEY);
330   }
331
332   @Test
333   public void fail_when_not_logged_in() {
334     TestRequest request = ws.newRequest()
335       .setParam("almSetting", "sdgfdshfjztutz")
336       .setParam("projectKey", "projectKey")
337       .setParam("repositorySlug", "repo-slug");
338
339     assertThatThrownBy(request::execute)
340       .isInstanceOf(UnauthorizedException.class);
341   }
342
343   @Test
344   public void fail_when_missing_project_creator_permission() {
345     UserDto user = db.users().insertUser();
346     userSession.logIn(user).addPermission(SCAN);
347
348     TestRequest request = ws.newRequest()
349       .setParam("almSetting", "sdgfdshfjztutz")
350       .setParam("projectKey", "projectKey")
351       .setParam("repositorySlug", "repo-slug");
352
353     assertThatThrownBy(request::execute)
354       .isInstanceOf(ForbiddenException.class)
355       .hasMessageContaining("Insufficient privileges");
356   }
357
358   @Test
359   public void check_pat_is_missing() {
360     UserDto user = db.users().insertUser();
361     userSession.logIn(user).addPermission(PROVISION_PROJECTS);
362     AlmSettingDto almSetting = db.almSettings().insertGitHubAlmSetting();
363
364     TestRequest request = ws.newRequest()
365       .setParam("almSetting", almSetting.getKey())
366       .setParam("repositorySlug", "repo");
367
368     assertThatThrownBy(request::execute)
369       .isInstanceOf(IllegalArgumentException.class)
370       .hasMessageContaining("Username and App Password for '" + almSetting.getKey() + "' is missing");
371   }
372
373   @Test
374   public void fail_check_alm_setting_not_found() {
375     UserDto user = db.users().insertUser();
376     userSession.logIn(user).addPermission(PROVISION_PROJECTS);
377     AlmPatDto almPatDto = newAlmPatDto();
378     db.getDbClient().almPatDao().insert(db.getSession(), almPatDto, user.getLogin(), null);
379
380     TestRequest request = ws.newRequest()
381       .setParam("almSetting", "testKey")
382       .setParam("repositorySlug", "repo");
383
384     assertThatThrownBy(request::execute)
385       .isInstanceOf(NotFoundException.class)
386       .hasMessageContaining("DevOps Platform Setting 'testKey' not found");
387   }
388
389   @Test
390   public void fail_when_no_creation_project_permission() {
391     UserDto user = db.users().insertUser();
392     userSession.logIn(user);
393
394     TestRequest request = ws.newRequest()
395       .setParam("almSetting", "anyvalue");
396
397     assertThatThrownBy(request::execute)
398       .isInstanceOf(ForbiddenException.class)
399       .hasMessageContaining("Insufficient privileges");
400   }
401
402   @Test
403   public void definition() {
404     WebService.Action def = ws.getDef();
405
406     assertThat(def.since()).isEqualTo("9.0");
407     assertThat(def.isPost()).isTrue();
408     assertThat(def.params())
409       .extracting(WebService.Param::key, WebService.Param::isRequired)
410       .containsExactlyInAnyOrder(
411         tuple("almSetting", true),
412         tuple("repositorySlug", true),
413         tuple(PARAM_NEW_CODE_DEFINITION_TYPE, false),
414         tuple(PARAM_NEW_CODE_DEFINITION_VALUE, false));
415   }
416
417   private Repository getGsonBBCRepo() {
418     Project project1 = new Project("PROJECT-UUID-ONE", "projectKey1", "projectName1");
419     MainBranch mainBranch = new MainBranch("branch", "develop");
420     return new Repository("REPO-UUID-ONE", "repo-slug-1", "repoName1", project1, mainBranch);
421   }
422
423   private Repository getGsonBBCRepoWithNoMainBranchName() {
424     Project project1 = new Project("PROJECT-UUID-ONE", "projectKey1", "projectName1");
425     MainBranch mainBranch = new MainBranch("branch", null);
426     return new Repository("REPO-UUID-ONE", "repo-slug-1", "repoName1", project1, mainBranch);
427   }
428
429 }