]> source.dussan.org Git - sonarqube.git/blob
685b80665801efbd8681f141cb93a917f8044161
[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.github;
21
22 import java.util.Optional;
23 import java.util.Set;
24 import org.junit.Before;
25 import org.junit.Rule;
26 import org.junit.Test;
27 import org.mockito.ArgumentCaptor;
28 import org.sonar.alm.client.github.AppInstallationToken;
29 import org.sonar.alm.client.github.GithubApplicationClient;
30 import org.sonar.alm.client.github.GithubApplicationClientImpl;
31 import org.sonar.alm.client.github.api.GsonRepositoryCollaborator;
32 import org.sonar.api.resources.Qualifiers;
33 import org.sonar.api.server.ws.WebService;
34 import org.sonar.api.utils.System2;
35 import org.sonar.auth.github.GitHubSettings;
36 import org.sonar.alm.client.github.GithubPermissionConverter;
37 import org.sonar.auth.github.GsonRepositoryPermissions;
38 import org.sonar.core.i18n.I18n;
39 import org.sonar.core.platform.EditionProvider;
40 import org.sonar.core.platform.PlatformEditionProvider;
41 import org.sonar.core.util.SequenceUuidFactory;
42 import org.sonar.db.DbSession;
43 import org.sonar.db.DbTester;
44 import org.sonar.db.alm.setting.AlmSettingDto;
45 import org.sonar.db.component.BranchDto;
46 import org.sonar.db.component.ResourceTypesRule;
47 import org.sonar.db.entity.EntityDto;
48 import org.sonar.db.newcodeperiod.NewCodePeriodDto;
49 import org.sonar.db.permission.GlobalPermission;
50 import org.sonar.db.project.CreationMethod;
51 import org.sonar.db.project.ProjectDto;
52 import org.sonar.db.user.UserDto;
53 import org.sonar.server.almintegration.ws.ImportHelper;
54 import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
55 import org.sonar.server.almsettings.ws.GithubProjectCreatorFactory;
56 import org.sonar.server.component.ComponentUpdater;
57 import org.sonar.server.es.EsTester;
58 import org.sonar.server.es.IndexersImpl;
59 import org.sonar.server.es.TestIndexers;
60 import org.sonar.server.exceptions.NotFoundException;
61 import org.sonar.server.exceptions.UnauthorizedException;
62 import org.sonar.server.favorite.FavoriteUpdater;
63 import org.sonar.server.management.ManagedProjectService;
64 import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver;
65 import org.sonar.server.permission.GroupPermissionChanger;
66 import org.sonar.server.permission.PermissionService;
67 import org.sonar.server.permission.PermissionServiceImpl;
68 import org.sonar.server.permission.PermissionTemplateService;
69 import org.sonar.server.permission.PermissionUpdater;
70 import org.sonar.server.permission.UserPermissionChange;
71 import org.sonar.server.permission.UserPermissionChanger;
72 import org.sonar.server.permission.index.FooIndexDefinition;
73 import org.sonar.server.permission.index.PermissionIndexer;
74 import org.sonar.server.project.DefaultBranchNameResolver;
75 import org.sonar.server.project.ProjectDefaultVisibility;
76 import org.sonar.server.project.Visibility;
77 import org.sonar.server.tester.UserSessionRule;
78 import org.sonar.server.ws.TestRequest;
79 import org.sonar.server.ws.WsActionTester;
80 import org.sonarqube.ws.Projects;
81
82 import static java.util.Objects.requireNonNull;
83 import static org.assertj.core.api.Assertions.assertThat;
84 import static org.assertj.core.api.Assertions.assertThatNoException;
85 import static org.assertj.core.api.Assertions.assertThatThrownBy;
86 import static org.assertj.core.api.Assertions.tuple;
87 import static org.mockito.ArgumentMatchers.any;
88 import static org.mockito.ArgumentMatchers.eq;
89 import static org.mockito.Mockito.mock;
90 import static org.mockito.Mockito.never;
91 import static org.mockito.Mockito.verify;
92 import static org.mockito.Mockito.when;
93 import static org.sonar.db.component.BranchDto.DEFAULT_MAIN_BRANCH_NAME;
94 import static org.sonar.db.newcodeperiod.NewCodePeriodType.NUMBER_OF_DAYS;
95 import static org.sonar.db.newcodeperiod.NewCodePeriodType.REFERENCE_BRANCH;
96 import static org.sonar.server.almintegration.ws.ImportHelper.PARAM_ALM_SETTING;
97 import static org.sonar.server.almintegration.ws.github.ImportGithubProjectAction.PARAM_REPOSITORY_KEY;
98 import static org.sonar.server.tester.UserSessionRule.standalone;
99 import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_CODE_DEFINITION_TYPE;
100 import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_CODE_DEFINITION_VALUE;
101
102 public class ImportGithubProjectActionIT {
103
104   private static final String PROJECT_KEY_NAME = "PROJECT_NAME";
105   private static final String GENERATED_PROJECT_KEY = "generated_" + PROJECT_KEY_NAME;
106
107   @Rule
108   public UserSessionRule userSession = standalone();
109
110   private final System2 system2 = mock(System2.class);
111   private final GithubApplicationClientImpl appClient = mock(GithubApplicationClientImpl.class);
112   private final DefaultBranchNameResolver defaultBranchNameResolver = mock(DefaultBranchNameResolver.class);
113
114   @Rule
115   public DbTester db = DbTester.create(system2);
116   private final PermissionTemplateService permissionTemplateService = mock(PermissionTemplateService.class);
117   public EsTester es = EsTester.createCustom(new FooIndexDefinition());
118   private final PermissionUpdater<UserPermissionChange> userPermissionUpdater = new PermissionUpdater(
119     new IndexersImpl(new PermissionIndexer(db.getDbClient(), es.client())),
120     Set.of(new UserPermissionChanger(db.getDbClient(), new SequenceUuidFactory()),
121       new GroupPermissionChanger(db.getDbClient(), new SequenceUuidFactory())));
122   private final PermissionService permissionService = new PermissionServiceImpl(new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT));
123   private final ComponentUpdater componentUpdater = new ComponentUpdater(db.getDbClient(), mock(I18n.class), System2.INSTANCE,
124     permissionTemplateService, new FavoriteUpdater(db.getDbClient()), new TestIndexers(), new SequenceUuidFactory(),
125     defaultBranchNameResolver, userPermissionUpdater, permissionService);
126
127   private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession);
128   private final ProjectKeyGenerator projectKeyGenerator = mock(ProjectKeyGenerator.class);
129   private final ProjectDefaultVisibility projectDefaultVisibility = mock(ProjectDefaultVisibility.class);
130   private final PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class);
131
132   private final GitHubSettings gitHubSettings = mock(GitHubSettings.class);
133   private final NewCodeDefinitionResolver newCodeDefinitionResolver = new NewCodeDefinitionResolver(db.getDbClient(), editionProvider);
134
135   private final ManagedProjectService managedProjectService = mock(ManagedProjectService.class);
136
137   private final GithubPermissionConverter githubPermissionConverter = mock();
138
139   private final GithubProjectCreatorFactory gitHubProjectCreatorFactory = new GithubProjectCreatorFactory(db.getDbClient(),
140     null, appClient, projectDefaultVisibility, projectKeyGenerator, userSession, componentUpdater, gitHubSettings, githubPermissionConverter, userPermissionUpdater, permissionService,
141     managedProjectService);
142   private final WsActionTester ws = new WsActionTester(new ImportGithubProjectAction(db.getDbClient(), userSession,
143     componentUpdater, importHelper, newCodeDefinitionResolver, defaultBranchNameResolver, gitHubProjectCreatorFactory));
144
145   @Before
146   public void before() {
147     when(projectDefaultVisibility.get(any())).thenReturn(Visibility.PRIVATE);
148     when(defaultBranchNameResolver.getEffectiveMainBranchName()).thenReturn(DEFAULT_MAIN_BRANCH_NAME);
149   }
150
151   @Test
152   public void importProject_ifProjectWithSameNameDoesNotExist_importSucceed() {
153     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
154
155     GithubApplicationClient.Repository repository = mockGithubDevOpsAppInteractions();
156
157     Projects.CreateWsResponse response = callWebService(githubAlmSetting);
158
159     Projects.CreateWsResponse.Project result = response.getProject();
160     assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
161     assertThat(result.getName()).isEqualTo(repository.getName());
162
163     Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
164     assertThat(projectDto).isPresent();
165     assertThat(db.getDbClient().projectAlmSettingDao().selectByProject(db.getSession(), projectDto.get())).isPresent();
166     Optional<BranchDto> mainBranch = db.getDbClient().branchDao().selectByProject(db.getSession(), projectDto.get()).stream().filter(BranchDto::isMain).findAny();
167     assertThat(mainBranch).isPresent();
168     assertThat(mainBranch.get().getKey()).isEqualTo("default-branch");
169
170     verify(managedProjectService).queuePermissionSyncTask(userSession.getUuid(), mainBranch.get().getUuid(), projectDto.get().getUuid());
171   }
172
173   @Test
174   public void importProject_withNCD_developer_edition() {
175     when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.DEVELOPER));
176
177     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
178
179     mockGithubDevOpsAppInteractions();
180
181     Projects.CreateWsResponse response = ws.newRequest()
182       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
183       .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME)
184       .setParam(PARAM_NEW_CODE_DEFINITION_TYPE, "NUMBER_OF_DAYS")
185       .setParam(PARAM_NEW_CODE_DEFINITION_VALUE, "30")
186       .executeProtobuf(Projects.CreateWsResponse.class);
187
188     Projects.CreateWsResponse.Project result = response.getProject();
189     assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
190
191     Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
192     assertThat(projectDto).isPresent();
193
194     assertThat(db.getDbClient().newCodePeriodDao().selectByProject(db.getSession(), projectDto.get().getUuid()))
195       .isPresent()
196       .get()
197       .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue, NewCodePeriodDto::getBranchUuid)
198       .containsExactly(NUMBER_OF_DAYS, "30", null);
199   }
200
201   @Test
202   public void importProject_withNCD_community_edition() {
203     when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.COMMUNITY));
204
205     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
206
207     mockGithubDevOpsAppInteractions();
208
209     Projects.CreateWsResponse response = ws.newRequest()
210       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
211       .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME)
212       .setParam(PARAM_NEW_CODE_DEFINITION_TYPE, "NUMBER_OF_DAYS")
213       .setParam(PARAM_NEW_CODE_DEFINITION_VALUE, "30")
214       .executeProtobuf(Projects.CreateWsResponse.class);
215
216     Projects.CreateWsResponse.Project result = response.getProject();
217     assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
218
219     Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
220     assertThat(projectDto).isPresent();
221     BranchDto branchDto = db.getDbClient().branchDao().selectMainBranchByProjectUuid(db.getSession(), projectDto.get().getUuid()).orElseThrow();
222
223     String projectUuid = projectDto.get().getUuid();
224     assertThat(db.getDbClient().newCodePeriodDao().selectByBranch(db.getSession(), projectUuid, branchDto.getUuid()))
225       .isPresent()
226       .get()
227       .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue, NewCodePeriodDto::getBranchUuid)
228       .containsExactly(NUMBER_OF_DAYS, "30", branchDto.getUuid());
229   }
230
231   @Test
232   public void importProject_reference_branch_ncd_no_default_branch() {
233     when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.DEVELOPER));
234     when(defaultBranchNameResolver.getEffectiveMainBranchName()).thenReturn("default-branch");
235
236     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
237
238     mockGithubDevOpsAppInteractions();
239
240     Projects.CreateWsResponse response = ws.newRequest()
241       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
242       .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME)
243       .setParam(PARAM_NEW_CODE_DEFINITION_TYPE, "reference_branch")
244       .executeProtobuf(Projects.CreateWsResponse.class);
245
246     Projects.CreateWsResponse.Project result = response.getProject();
247     assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
248
249     Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
250     assertThat(projectDto).isPresent();
251
252     assertThat(db.getDbClient().newCodePeriodDao().selectByProject(db.getSession(), projectDto.get().getUuid()))
253       .isPresent()
254       .get()
255       .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue)
256       .containsExactly(REFERENCE_BRANCH, "default-branch");
257   }
258
259   @Test
260   public void importProject_reference_branch_ncd() {
261     when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.DEVELOPER));
262
263     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
264
265     mockGithubDevOpsAppInteractions();
266
267     Projects.CreateWsResponse response = ws.newRequest()
268       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
269       .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME)
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
276     Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
277     assertThat(projectDto).isPresent();
278
279     assertThat(db.getDbClient().newCodePeriodDao().selectByProject(db.getSession(), projectDto.get().getUuid()))
280       .isPresent()
281       .get()
282       .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue)
283       .containsExactly(REFERENCE_BRANCH, "default-branch");
284   }
285
286   @Test
287   public void importProject_ifProjectWithSameNameAlreadyExists_importSucceed() {
288     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
289     db.components().insertPublicProject(p -> p.setKey("Hello-World")).getMainBranchComponent();
290
291     GithubApplicationClient.Repository repository = mockGithubDevOpsAppInteractions();
292
293     Projects.CreateWsResponse response = ws.newRequest()
294       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
295       .setParam(PARAM_REPOSITORY_KEY, "Hello-World")
296       .executeProtobuf(Projects.CreateWsResponse.class);
297
298     Projects.CreateWsResponse.Project result = response.getProject();
299     assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
300     assertThat(result.getName()).isEqualTo(repository.getName());
301   }
302
303   @Test
304   public void importProject_whenGithubProvisioningIsDisabled_shouldApplyPermissionTemplate() {
305     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
306
307     mockGithubDevOpsAppInteractions();
308     when(gitHubSettings.isProvisioningEnabled()).thenReturn(false);
309
310     ws.newRequest()
311       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
312       .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME)
313       .executeProtobuf(Projects.CreateWsResponse.class);
314
315     ArgumentCaptor<EntityDto> projectDtoArgumentCaptor = ArgumentCaptor.forClass(EntityDto.class);
316     verify(permissionTemplateService).applyDefaultToNewComponent(any(DbSession.class), projectDtoArgumentCaptor.capture(), eq(userSession.getUuid()));
317     String projectKey = projectDtoArgumentCaptor.getValue().getKey();
318     assertThat(projectKey).isEqualTo(GENERATED_PROJECT_KEY);
319
320   }
321
322   @Test
323   public void importProject_whenGithubProvisioningIsEnabled_shouldNotApplyPermissionTemplate() {
324     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
325
326     when(gitHubSettings.isProvisioningEnabled()).thenReturn(true);
327     mockGithubDevOpsAppInteractions();
328     mockGithubAuthAppInteractions();
329
330     ws.newRequest()
331       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
332       .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME)
333       .executeProtobuf(Projects.CreateWsResponse.class);
334
335     verify(permissionTemplateService, never()).applyDefaultToNewComponent(any(), any(), any());
336
337   }
338
339   private void mockGithubAuthAppInteractions() {
340     when(gitHubSettings.appId()).thenReturn("432");
341     when(gitHubSettings.privateKey()).thenReturn("private key");
342     when(gitHubSettings.apiURL()).thenReturn("http://www.url.com");
343
344     AppInstallationToken appInstallationToken = mock();
345
346     when(appClient.getInstallationId(any(), any())).thenReturn(Optional.of(321L));
347     when(appClient.createAppInstallationToken(any(), eq(321L))).thenReturn(Optional.of(appInstallationToken));
348
349     GsonRepositoryCollaborator gsonRepositoryCollaborator = new GsonRepositoryCollaborator("toto", 2, "admin", new GsonRepositoryPermissions(true, true, true, true, true));
350     when(appClient.getRepositoryCollaborators(gitHubSettings.apiURL(), appInstallationToken, "octocat", PROJECT_KEY_NAME)).thenReturn(Set.of(gsonRepositoryCollaborator));
351
352     String role = gsonRepositoryCollaborator.roleName();
353     GsonRepositoryPermissions permissions = gsonRepositoryCollaborator.permissions();
354     when(githubPermissionConverter.toSonarqubeRolesWithFallbackOnRepositoryPermissions(Set.of(), role, permissions)).thenReturn(Set.of("scan"));
355
356   }
357
358   @Test
359   public void importProject_shouldSetCreationMethodToApi_ifNonBrowserRequest() {
360     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
361     mockGithubDevOpsAppInteractions();
362
363     Projects.CreateWsResponse response = callWebService(githubAlmSetting);
364
365     assertThat(response.getProject().getKey()).isEqualTo(GENERATED_PROJECT_KEY);
366     Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), response.getProject().getKey());
367     assertThat(projectDto.orElseThrow().getCreationMethod()).isEqualTo(CreationMethod.ALM_IMPORT_API);
368   }
369
370   @Test
371   public void importProject_shouldSetCreationMethodToBrowser_ifBrowserRequest() {
372     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
373     userSession.flagSessionAsGui();
374     mockGithubDevOpsAppInteractions();
375
376     Projects.CreateWsResponse response = callWebService(githubAlmSetting);
377
378     assertThat(response.getProject().getKey()).isEqualTo(GENERATED_PROJECT_KEY);
379     Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), response.getProject().getKey());
380     assertThat(projectDto.orElseThrow().getCreationMethod()).isEqualTo(CreationMethod.ALM_IMPORT_BROWSER);
381   }
382
383   @Test
384   public void importProject_whenAlmSettingKeyDoesNotExist_shouldThrow() {
385     UserDto user = db.users().insertUser();
386     userSession.logIn(user).addPermission(GlobalPermission.PROVISION_PROJECTS);
387
388     TestRequest request = ws.newRequest()
389       .setParam(PARAM_ALM_SETTING, "unknown")
390       .setParam(PARAM_REPOSITORY_KEY, "test/repo");
391     assertThatThrownBy(request::execute)
392       .isInstanceOf(NotFoundException.class)
393       .hasMessage("DevOps Platform configuration 'unknown' not found.");
394   }
395
396   @Test
397   public void importProject_whenNoAlmSettingKeyAndNoConfig_shouldThrow() {
398     UserDto user = db.users().insertUser();
399     userSession.logIn(user).addPermission(GlobalPermission.PROVISION_PROJECTS);
400
401     TestRequest request = ws.newRequest()
402       .setParam(PARAM_REPOSITORY_KEY, "test/repo");
403     assertThatThrownBy(request::execute)
404       .isInstanceOf(NotFoundException.class)
405       .hasMessage("There is no GITHUB configuration for DevOps Platform. Please add one.");
406   }
407
408   @Test
409   public void importProject_whenNoAlmSettingKeyAndMultipleConfigs_shouldThrow() {
410     UserDto user = db.users().insertUser();
411     userSession.logIn(user).addPermission(GlobalPermission.PROVISION_PROJECTS);
412     db.almSettings().insertGitHubAlmSetting();
413     db.almSettings().insertGitHubAlmSetting();
414
415     TestRequest request = ws.newRequest()
416       .setParam(PARAM_REPOSITORY_KEY, "test/repo");
417     assertThatThrownBy(request::execute)
418       .isInstanceOf(IllegalArgumentException.class)
419       .hasMessage("Parameter almSetting is required as there are multiple DevOps Platform configurations.");
420   }
421
422   @Test
423   public void importProject_whenNoAlmSettingKeyAndOnlyOneConfig_shouldImport() {
424     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
425
426     mockGithubDevOpsAppInteractions();
427
428     TestRequest request = ws.newRequest()
429       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
430       .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME);
431
432     assertThatNoException().isThrownBy(request::execute);
433   }
434
435   private Projects.CreateWsResponse callWebService(AlmSettingDto githubAlmSetting) {
436     return ws.newRequest()
437       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
438       .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME)
439       .executeProtobuf(Projects.CreateWsResponse.class);
440   }
441
442   private GithubApplicationClient.Repository mockGithubDevOpsAppInteractions() {
443     GithubApplicationClient.Repository repository = new GithubApplicationClient.Repository(1L, PROJECT_KEY_NAME, false,
444       "octocat/" + PROJECT_KEY_NAME,
445       "https://github.sonarsource.com/api/v3/repos/octocat/" + PROJECT_KEY_NAME, "default-branch");
446     when(appClient.getRepository(any(), any(), any())).thenReturn(Optional.of(repository));
447     when(projectKeyGenerator.generateUniqueProjectKey(repository.getFullName())).thenReturn(GENERATED_PROJECT_KEY);
448     return repository;
449   }
450
451   @Test
452   public void fail_when_not_logged_in() {
453     TestRequest request = ws.newRequest()
454       .setParam(PARAM_ALM_SETTING, "asdfghjkl")
455       .setParam(PARAM_REPOSITORY_KEY, "test/repo");
456     assertThatThrownBy(request::execute)
457       .isInstanceOf(UnauthorizedException.class);
458   }
459
460   @Test
461   public void fail_when_missing_create_project_permission() {
462     TestRequest request = ws.newRequest();
463     assertThatThrownBy(request::execute)
464       .isInstanceOf(UnauthorizedException.class);
465   }
466
467   @Test
468   public void fail_when_personal_access_token_doesnt_exist() {
469     AlmSettingDto githubAlmSetting = setupUserAndAlmSettings();
470
471     TestRequest request = ws.newRequest()
472       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
473       .setParam(PARAM_REPOSITORY_KEY, "test/repo");
474     assertThatThrownBy(request::execute)
475       .isInstanceOf(IllegalArgumentException.class)
476       .hasMessage("No personal access token found");
477   }
478
479   @Test
480   public void definition() {
481     WebService.Action def = ws.getDef();
482
483     assertThat(def.since()).isEqualTo("8.4");
484     assertThat(def.isPost()).isTrue();
485     assertThat(def.params())
486       .extracting(WebService.Param::key, WebService.Param::isRequired)
487       .containsExactlyInAnyOrder(
488         tuple(PARAM_ALM_SETTING, false),
489         tuple(PARAM_REPOSITORY_KEY, true),
490         tuple(PARAM_NEW_CODE_DEFINITION_TYPE, false),
491         tuple(PARAM_NEW_CODE_DEFINITION_VALUE, false));
492   }
493
494   private AlmSettingDto setupUserWithPatAndAlmSettings() {
495     AlmSettingDto almSettings = setupUserAndAlmSettings();
496     db.almPats().insert(p -> p.setAlmSettingUuid(almSettings.getUuid()).setUserUuid(requireNonNull(userSession.getUuid())));
497     return almSettings;
498   }
499
500   private AlmSettingDto setupUserAndAlmSettings() {
501     UserDto user = db.users().insertUser();
502     userSession.logIn(user).addPermission(GlobalPermission.PROVISION_PROJECTS);
503     return db.almSettings().insertGitHubAlmSetting(alm -> alm.setClientId("client_123").setClientSecret("client_secret_123"));
504   }
505 }