]> source.dussan.org Git - sonarqube.git/blob
7918962abd5f6bb1abaa611658fff2bce064edae
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2021 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.azure;
21
22 import com.google.common.collect.ImmutableList;
23 import org.jetbrains.annotations.NotNull;
24 import org.junit.Before;
25 import org.junit.Rule;
26 import org.junit.Test;
27 import org.sonar.alm.client.azure.AzureDevOpsHttpClient;
28 import org.sonar.alm.client.azure.GsonAzureProject;
29 import org.sonar.alm.client.azure.GsonAzureRepo;
30 import org.sonar.alm.client.azure.GsonAzureRepoList;
31 import org.sonar.api.server.ws.WebService;
32 import org.sonar.db.DbTester;
33 import org.sonar.db.alm.pat.AlmPatDto;
34 import org.sonar.db.alm.setting.AlmSettingDto;
35 import org.sonar.db.project.ProjectDto;
36 import org.sonar.db.user.UserDto;
37 import org.sonar.server.exceptions.ForbiddenException;
38 import org.sonar.server.exceptions.NotFoundException;
39 import org.sonar.server.exceptions.UnauthorizedException;
40 import org.sonar.server.tester.UserSessionRule;
41 import org.sonar.server.ws.TestRequest;
42 import org.sonar.server.ws.WsActionTester;
43
44 import static java.util.Collections.emptyList;
45 import static org.assertj.core.api.Assertions.assertThat;
46 import static org.assertj.core.api.Assertions.assertThatThrownBy;
47 import static org.assertj.core.api.Assertions.tuple;
48 import static org.mockito.ArgumentMatchers.any;
49 import static org.mockito.Mockito.mock;
50 import static org.mockito.Mockito.when;
51 import static org.sonar.db.alm.integration.pat.AlmPatsTesting.newAlmPatDto;
52 import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS;
53 import static org.sonarqube.ws.AlmIntegrations.AzureRepo;
54 import static org.sonarqube.ws.AlmIntegrations.SearchAzureReposWsResponse;
55
56 public class SearchAzureReposActionTest {
57
58   @Rule
59   public UserSessionRule userSession = UserSessionRule.standalone();
60   @Rule
61   public DbTester db = DbTester.create();
62
63   private AzureDevOpsHttpClient azureDevOpsHttpClient = mock(AzureDevOpsHttpClient.class);
64   private WsActionTester ws = new WsActionTester(new SearchAzureReposAction(db.getDbClient(), userSession, azureDevOpsHttpClient));
65
66   @Before
67   public void before() {
68     mockClient(new GsonAzureRepoList(ImmutableList.of(getGsonAzureRepo("project-1", "repoName-1"),
69       getGsonAzureRepo("project-2", "repoName-2"))));
70   }
71
72   @Test
73   public void define() {
74     WebService.Action def = ws.getDef();
75
76     assertThat(def.since()).isEqualTo("8.6");
77     assertThat(def.isPost()).isFalse();
78     assertThat(def.params())
79       .extracting(WebService.Param::key, WebService.Param::isRequired)
80       .containsExactlyInAnyOrder(
81         tuple("almSetting", true),
82         tuple("projectName", false),
83         tuple("searchQuery", false));
84   }
85
86   @Test
87   public void search_repos() {
88     AlmSettingDto almSetting = insertAlmSetting();
89
90     SearchAzureReposWsResponse response = ws.newRequest()
91       .setParam("almSetting", almSetting.getKey())
92       .executeProtobuf(SearchAzureReposWsResponse.class);
93
94     assertThat(response.getRepositoriesList())
95       .extracting(AzureRepo::getName, AzureRepo::getProjectName)
96       .containsExactlyInAnyOrder(
97         tuple("repoName-1", "project-1"), tuple("repoName-2", "project-2"));
98   }
99
100   @Test
101   public void search_repos_alphabetically_sorted() {
102     mockClient(new GsonAzureRepoList(ImmutableList.of(getGsonAzureRepo("project-1", "Z-repo"),
103       getGsonAzureRepo("project-1", "A-repo-1"), getGsonAzureRepo("project-1", "a-repo"),
104       getGsonAzureRepo("project-1", "b-repo"))));
105
106     AlmSettingDto almSetting = insertAlmSetting();
107
108     SearchAzureReposWsResponse response = ws.newRequest()
109       .setParam("almSetting", almSetting.getKey())
110       .executeProtobuf(SearchAzureReposWsResponse.class);
111
112     assertThat(response.getRepositoriesList())
113       .extracting(AzureRepo::getName, AzureRepo::getProjectName)
114       .containsExactly(
115         tuple("a-repo", "project-1"), tuple("A-repo-1", "project-1"),
116         tuple("b-repo", "project-1"), tuple("Z-repo", "project-1"));
117   }
118
119   @Test
120   public void search_repos_with_project_already_set_up() {
121     AlmSettingDto almSetting = insertAlmSetting();
122
123     ProjectDto projectDto2 = insertProject(almSetting, "repoName-2", "project-2");
124
125     SearchAzureReposWsResponse response = ws.newRequest()
126       .setParam("almSetting", almSetting.getKey())
127       .executeProtobuf(SearchAzureReposWsResponse.class);
128
129     assertThat(response.getRepositoriesCount()).isEqualTo(2);
130
131     assertThat(response.getRepositoriesList())
132       .extracting(AzureRepo::getName, AzureRepo::getProjectName,
133         AzureRepo::getSqProjectKey, AzureRepo::getSqProjectName)
134       .containsExactlyInAnyOrder(
135         tuple("repoName-1", "project-1", "", ""),
136         tuple("repoName-2", "project-2", projectDto2.getKey(), projectDto2.getName()));
137   }
138
139   @Test
140   public void search_repos_with_project_already_set_u_and_collision_is_handled() {
141     AlmSettingDto almSetting = insertAlmSetting();
142
143     ProjectDto projectDto2 = insertProject(almSetting, "repoName-2", "project-2");
144     insertProject(almSetting, "repoName-2", "project-2");
145
146     SearchAzureReposWsResponse response = ws.newRequest()
147       .setParam("almSetting", almSetting.getKey())
148       .executeProtobuf(SearchAzureReposWsResponse.class);
149
150     assertThat(response.getRepositoriesCount()).isEqualTo(2);
151
152     assertThat(response.getRepositoriesList())
153       .extracting(AzureRepo::getName, AzureRepo::getProjectName,
154         AzureRepo::getSqProjectKey, AzureRepo::getSqProjectName)
155       .containsExactlyInAnyOrder(
156         tuple("repoName-1", "project-1", "", ""),
157         tuple("repoName-2", "project-2", projectDto2.getKey(), projectDto2.getName()));
158   }
159
160   @Test
161   public void search_repos_with_projects_already_set_up_and_no_collision() {
162     mockClient(new GsonAzureRepoList(ImmutableList.of(getGsonAzureRepo("project-1", "repoName-1"),
163       getGsonAzureRepo("project", "1-repoName-1"))));
164     AlmSettingDto almSetting = insertAlmSetting();
165
166     ProjectDto projectDto1 = insertProject(almSetting, "repoName-1", "project-1");
167     ProjectDto projectDto2 = insertProject(almSetting, "1-repoName-1", "project");
168
169     SearchAzureReposWsResponse response = ws.newRequest()
170       .setParam("almSetting", almSetting.getKey())
171       .executeProtobuf(SearchAzureReposWsResponse.class);
172
173     assertThat(response.getRepositoriesCount()).isEqualTo(2);
174
175     assertThat(response.getRepositoriesList())
176       .extracting(AzureRepo::getName, AzureRepo::getProjectName,
177         AzureRepo::getSqProjectKey, AzureRepo::getSqProjectName)
178       .containsExactlyInAnyOrder(
179         tuple("repoName-1", "project-1", projectDto1.getKey(), projectDto1.getName()),
180         tuple("1-repoName-1", "project", projectDto2.getKey(), projectDto2.getName()));
181   }
182
183   @Test
184   public void search_repos_with_same_name_and_different_project() {
185     mockClient(new GsonAzureRepoList(ImmutableList.of(getGsonAzureRepo("project-1", "repoName-1"),
186       getGsonAzureRepo("project-2", "repoName-1"))));
187     AlmSettingDto almSetting = insertAlmSetting();
188
189     ProjectDto projectDto1 = insertProject(almSetting, "repoName-1", "project-1");
190     ProjectDto projectDto2 = insertProject(almSetting, "repoName-1", "project-2");
191
192     SearchAzureReposWsResponse response = ws.newRequest()
193       .setParam("almSetting", almSetting.getKey())
194       .executeProtobuf(SearchAzureReposWsResponse.class);
195
196     assertThat(response.getRepositoriesCount()).isEqualTo(2);
197
198     assertThat(response.getRepositoriesList())
199       .extracting(AzureRepo::getName, AzureRepo::getProjectName,
200         AzureRepo::getSqProjectKey, AzureRepo::getSqProjectName)
201       .containsExactlyInAnyOrder(
202         tuple("repoName-1", "project-1", projectDto1.getKey(), projectDto1.getName()),
203         tuple("repoName-1", "project-2", projectDto2.getKey(), projectDto2.getName()));
204   }
205
206   @Test
207   public void search_repos_with_project_name() {
208     AlmSettingDto almSetting = insertAlmSetting();
209
210     SearchAzureReposWsResponse response = ws.newRequest()
211       .setParam("almSetting", almSetting.getKey())
212       .setParam("projectName", "project-1")
213       .executeProtobuf(SearchAzureReposWsResponse.class);
214
215     assertThat(response.getRepositoriesList())
216       .extracting(AzureRepo::getName, AzureRepo::getProjectName)
217       .containsExactlyInAnyOrder(
218         tuple("repoName-1", "project-1"), tuple("repoName-2", "project-2"));
219   }
220
221   @Test
222   public void search_repos_with_project_name_and_empty_criteria() {
223     AlmSettingDto almSetting = insertAlmSetting();
224
225     SearchAzureReposWsResponse response = ws.newRequest()
226       .setParam("almSetting", almSetting.getKey())
227       .setParam("projectName", "project-1")
228       .setParam("searchQuery", "")
229       .executeProtobuf(SearchAzureReposWsResponse.class);
230
231     assertThat(response.getRepositoriesList())
232       .extracting(AzureRepo::getName, AzureRepo::getProjectName)
233       .containsExactlyInAnyOrder(
234         tuple("repoName-1", "project-1"), tuple("repoName-2", "project-2"));
235   }
236
237   @Test
238   public void search_and_filter_repos_with_repo_name() {
239     AlmSettingDto almSetting = insertAlmSetting();
240
241     SearchAzureReposWsResponse response = ws.newRequest()
242       .setParam("almSetting", almSetting.getKey())
243       .setParam("searchQuery", "repoName-2")
244       .executeProtobuf(SearchAzureReposWsResponse.class);
245
246     assertThat(response.getRepositoriesList())
247       .extracting(AzureRepo::getName, AzureRepo::getProjectName)
248       .containsExactlyInAnyOrder(tuple("repoName-2", "project-2"));
249   }
250
251   @Test
252   public void search_and_filter_repos_with_matching_repo_and_project_name() {
253     mockClient(new GsonAzureRepoList(ImmutableList.of(getGsonAzureRepo("big-project", "repo-1"),
254       getGsonAzureRepo("big-project", "repo-2"),
255       getGsonAzureRepo("big-project", "big-repo"),
256       getGsonAzureRepo("project", "big-repo"),
257       getGsonAzureRepo("project", "small-repo"))));
258     AlmSettingDto almSetting = insertAlmSetting();
259
260     SearchAzureReposWsResponse response = ws.newRequest()
261       .setParam("almSetting", almSetting.getKey())
262       .setParam("searchQuery", "big")
263       .executeProtobuf(SearchAzureReposWsResponse.class);
264
265     assertThat(response.getRepositoriesList())
266       .extracting(AzureRepo::getName, AzureRepo::getProjectName)
267       .containsExactlyInAnyOrder(tuple("repo-1", "big-project"), tuple("repo-2", "big-project"),
268         tuple("big-repo", "big-project"), tuple("big-repo", "project"));
269   }
270
271   @Test
272   public void return_empty_list_when_there_are_no_azure_repos() {
273     when(azureDevOpsHttpClient.getRepos(any(), any(), any())).thenReturn(new GsonAzureRepoList(emptyList()));
274
275     AlmSettingDto almSetting = insertAlmSetting();
276
277     SearchAzureReposWsResponse response = ws.newRequest()
278       .setParam("almSetting", almSetting.getKey())
279       .executeProtobuf(SearchAzureReposWsResponse.class);
280
281     assertThat(response.getRepositoriesList()).isEmpty();
282   }
283
284   @Test
285   public void check_pat_is_missing() {
286     insertUser();
287     AlmSettingDto almSetting = db.almSettings().insertAzureAlmSetting();
288
289     TestRequest request = ws.newRequest()
290       .setParam("almSetting", almSetting.getKey());
291
292     assertThatThrownBy(request::execute)
293       .isInstanceOf(IllegalArgumentException.class)
294       .hasMessage("No personal access token found");
295   }
296
297   @Test
298   public void fail_check_pat_alm_setting_not_found() {
299     UserDto user = insertUser();
300     AlmPatDto almPatDto = newAlmPatDto();
301     db.getDbClient().almPatDao().insert(db.getSession(), almPatDto, user.getLogin(), null);
302
303     TestRequest request = ws.newRequest()
304       .setParam("almSetting", "testKey");
305
306     assertThatThrownBy(request::execute)
307       .isInstanceOf(NotFoundException.class)
308       .hasMessage("ALM Setting 'testKey' not found");
309   }
310
311   @Test
312   public void fail_when_not_logged_in() {
313     TestRequest request = ws.newRequest()
314       .setParam("almSetting", "anyvalue");
315
316     assertThatThrownBy(request::execute)
317       .isInstanceOf(UnauthorizedException.class);
318   }
319
320   @Test
321   public void fail_when_no_creation_project_permission() {
322     UserDto user = db.users().insertUser();
323     userSession.logIn(user);
324
325     TestRequest request = ws.newRequest()
326       .setParam("almSetting", "anyvalue");
327
328     assertThatThrownBy(request::execute)
329       .isInstanceOf(ForbiddenException.class)
330       .hasMessage("Insufficient privileges");
331   }
332
333   private ProjectDto insertProject(AlmSettingDto almSetting, String repoName, String projectName) {
334     ProjectDto projectDto1 = db.components().insertPrivateProjectDto();
335     db.almSettings().insertAzureProjectAlmSetting(almSetting, projectDto1, projectAlmSettingDto -> projectAlmSettingDto.setAlmRepo(repoName),
336       projectAlmSettingDto -> projectAlmSettingDto.setAlmSlug(projectName));
337     return projectDto1;
338   }
339
340   private void mockClient(GsonAzureRepoList repoList) {
341     when(azureDevOpsHttpClient.getRepos(any(), any(), any())).thenReturn(repoList);
342   }
343
344   private AlmSettingDto insertAlmSetting() {
345     UserDto user = insertUser();
346     AlmSettingDto almSetting = db.almSettings().insertAzureAlmSetting();
347     db.almPats().insert(dto -> {
348       dto.setAlmSettingUuid(almSetting.getUuid());
349       dto.setUserUuid(user.getUuid());
350       dto.setPersonalAccessToken(almSetting.getPersonalAccessToken());
351     });
352     return almSetting;
353   }
354
355   @NotNull
356   private UserDto insertUser() {
357     UserDto user = db.users().insertUser();
358     userSession.logIn(user).addPermission(PROVISION_PROJECTS);
359     return user;
360   }
361
362   private GsonAzureRepo getGsonAzureRepo(String projectName, String repoName) {
363     GsonAzureProject project = new GsonAzureProject(projectName, "the best project ever");
364     GsonAzureRepo gsonAzureRepo = new GsonAzureRepo("repo-id", repoName, "url", project, "repo-default-branch");
365     return gsonAzureRepo;
366   }
367 }