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