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