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