]> source.dussan.org Git - sonarqube.git/blob
8e339b42eef69e35b7614cf59f424b554dad5a5c
[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.qualitygate.ws;
21
22 import java.io.IOException;
23 import org.apache.commons.io.IOUtils;
24 import org.apache.commons.lang3.RandomStringUtils;
25 import org.junit.Before;
26 import org.junit.Rule;
27 import org.junit.Test;
28 import org.sonar.api.measures.CoreMetrics;
29 import org.sonar.api.server.ws.WebService;
30 import org.sonar.api.utils.System2;
31 import org.sonar.api.web.UserRole;
32 import org.sonar.db.DbClient;
33 import org.sonar.db.DbSession;
34 import org.sonar.db.DbTester;
35 import org.sonar.db.component.BranchType;
36 import org.sonar.db.component.ComponentDto;
37 import org.sonar.db.component.ProjectData;
38 import org.sonar.db.component.SnapshotDto;
39 import org.sonar.db.metric.MetricDto;
40 import org.sonar.db.permission.GlobalPermission;
41 import org.sonar.server.component.TestComponentFinder;
42 import org.sonar.server.exceptions.BadRequestException;
43 import org.sonar.server.exceptions.ForbiddenException;
44 import org.sonar.server.exceptions.NotFoundException;
45 import org.sonar.server.qualitygate.QualityGateCaycChecker;
46 import org.sonar.server.tester.UserSessionRule;
47 import org.sonar.server.ws.WsActionTester;
48 import org.sonarqube.ws.Qualitygates.ProjectStatusResponse;
49 import org.sonarqube.ws.Qualitygates.ProjectStatusResponse.Status;
50
51 import static java.lang.String.format;
52 import static java.nio.charset.StandardCharsets.UTF_8;
53 import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric;
54 import static org.assertj.core.api.Assertions.assertThat;
55 import static org.assertj.core.api.Assertions.assertThatThrownBy;
56 import static org.assertj.core.api.Assertions.tuple;
57 import static org.junit.Assert.assertEquals;
58 import static org.mockito.ArgumentMatchers.any;
59 import static org.mockito.ArgumentMatchers.eq;
60 import static org.mockito.Mockito.mock;
61 import static org.mockito.Mockito.when;
62 import static org.sonar.db.component.SnapshotTesting.newAnalysis;
63 import static org.sonar.db.measure.MeasureTesting.newMeasure;
64 import static org.sonar.db.measure.MeasureTesting.newProjectMeasureDto;
65 import static org.sonar.db.metric.MetricTesting.newMetricDto;
66 import static org.sonar.server.qualitygate.QualityGateCaycStatus.COMPLIANT;
67 import static org.sonar.server.qualitygate.QualityGateCaycStatus.NON_COMPLIANT;
68 import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_ANALYSIS_ID;
69 import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_BRANCH;
70 import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PROJECT_ID;
71 import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PROJECT_KEY;
72 import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PULL_REQUEST;
73 import static org.sonar.test.JsonAssert.assertJson;
74
75 public class ProjectStatusActionIT {
76   private static final String ANALYSIS_ID = "task-uuid";
77
78   @Rule
79   public UserSessionRule userSession = UserSessionRule.standalone();
80   @Rule
81   public DbTester db = DbTester.create(System2.INSTANCE);
82
83   private final DbClient dbClient = db.getDbClient();
84   private final DbSession dbSession = db.getSession();
85   private final QualityGateCaycChecker qualityGateCaycChecker = mock(QualityGateCaycChecker.class);
86
87   private final WsActionTester ws = new WsActionTester(new ProjectStatusAction(dbClient, TestComponentFinder.from(db), userSession, qualityGateCaycChecker));
88
89   @Before
90   public void setUp() {
91     when(qualityGateCaycChecker.checkCaycCompliantFromProject(any(), any())).thenReturn(NON_COMPLIANT);
92   }
93
94   @Test
95   public void test_definition() {
96     WebService.Action action = ws.getDef();
97     assertThat(action.params())
98       .extracting(WebService.Param::key, WebService.Param::isRequired)
99       .containsExactlyInAnyOrder(
100         tuple("analysisId", false),
101         tuple("projectKey", false),
102         tuple("projectId", false),
103         tuple("branch", false),
104         tuple("pullRequest", false));
105   }
106
107   @Test
108   public void test_json_example() throws IOException {
109     ProjectData projectData = db.components().insertPrivateProject();
110     ComponentDto mainBranch = projectData.getMainBranchComponent();
111     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
112     MetricDto gateDetailsMetric = insertGateDetailMetric();
113
114     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch)
115       .setPeriodMode("last_version")
116       .setPeriodParam("2015-12-07")
117       .setPeriodDate(956789123987L));
118     dbClient.projectMeasureDao().insert(dbSession,
119       newProjectMeasureDto(gateDetailsMetric, mainBranch, snapshot)
120         .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionIT/measure_data.json"), UTF_8)));
121     dbSession.commit();
122
123     String response = ws.newRequest()
124       .setParam("analysisId", snapshot.getUuid())
125       .execute().getInput();
126
127     assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
128   }
129
130   @Test
131   public void return_past_status_when_project_is_referenced_by_past_analysis_id() throws IOException {
132     ProjectData projectData = db.components().insertPrivateProject();
133     ComponentDto mainBranch = projectData.getMainBranchComponent();
134     SnapshotDto pastAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch)
135       .setLast(false)
136       .setPeriodMode("last_version")
137       .setPeriodParam("2015-12-07")
138       .setPeriodDate(956789123987L));
139     SnapshotDto lastAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch)
140       .setLast(true)
141       .setPeriodMode("last_version")
142       .setPeriodParam("2016-12-07")
143       .setPeriodDate(1_500L));
144     MetricDto gateDetailsMetric = insertGateDetailMetric();
145     dbClient.projectMeasureDao().insert(dbSession,
146       newProjectMeasureDto(gateDetailsMetric, mainBranch, pastAnalysis)
147         .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionIT/measure_data.json"), UTF_8)));
148     dbClient.projectMeasureDao().insert(dbSession,
149       newProjectMeasureDto(gateDetailsMetric, mainBranch, lastAnalysis)
150         .setData("not_used"));
151     dbSession.commit();
152     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
153
154     String response = ws.newRequest()
155       .setParam(PARAM_ANALYSIS_ID, pastAnalysis.getUuid())
156       .execute().getInput();
157
158     assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
159   }
160
161   @Test
162   public void return_live_status_when_project_is_referenced_by_its_id() throws IOException {
163     ProjectData projectData = db.components().insertPrivateProject();
164     ComponentDto mainBranch = projectData.getMainBranchComponent();
165     dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch)
166       .setPeriodMode("last_version")
167       .setPeriodParam("2015-12-07")
168       .setPeriodDate(956789123987L));
169     MetricDto gateDetailsMetric = insertGateDetailMetric();
170     dbClient.measureDao().insert(dbSession,
171       newMeasure(mainBranch, gateDetailsMetric, IOUtils.toString(getClass().getResource("ProjectStatusActionIT/measure_data.json"), UTF_8)));
172     dbSession.commit();
173     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
174
175     String response = ws.newRequest()
176       .setParam(PARAM_PROJECT_ID, projectData.projectUuid())
177       .execute().getInput();
178
179     assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
180   }
181
182   @Test
183   public void return_past_status_when_branch_is_referenced_by_past_analysis_id() throws IOException {
184     ProjectData projectData = db.components().insertPrivateProject();
185     ComponentDto mainBranch = projectData.getMainBranchComponent();
186     ComponentDto branch = db.components().insertProjectBranch(mainBranch);
187     SnapshotDto pastAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(branch)
188       .setLast(false)
189       .setPeriodMode("last_version")
190       .setPeriodParam("2015-12-07")
191       .setPeriodDate(956789123987L));
192     SnapshotDto lastAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(branch)
193       .setLast(true)
194       .setPeriodMode("last_version")
195       .setPeriodParam("2016-12-07")
196       .setPeriodDate(1_500L));
197     MetricDto gateDetailsMetric = insertGateDetailMetric();
198     dbClient.projectMeasureDao().insert(dbSession,
199       newProjectMeasureDto(gateDetailsMetric, branch, pastAnalysis)
200         .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionIT/measure_data.json"), UTF_8)));
201     dbClient.projectMeasureDao().insert(dbSession,
202       newProjectMeasureDto(gateDetailsMetric, branch, lastAnalysis)
203         .setData("not_used"));
204     dbSession.commit();
205     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
206
207     String response = ws.newRequest()
208       .setParam(PARAM_ANALYSIS_ID, pastAnalysis.getUuid())
209       .execute().getInput();
210
211     assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
212   }
213
214   @Test
215   public void return_live_status_when_project_is_referenced_by_its_key() throws IOException {
216     ProjectData projectData = db.components().insertPrivateProject();
217     ComponentDto project = projectData.getMainBranchComponent();
218     dbClient.snapshotDao().insert(dbSession, newAnalysis(project)
219       .setPeriodMode("last_version")
220       .setPeriodParam("2015-12-07")
221       .setPeriodDate(956789123987L));
222     MetricDto gateDetailsMetric = insertGateDetailMetric();
223     dbClient.measureDao().insert(dbSession,
224       newMeasure(project, gateDetailsMetric, IOUtils.toString(getClass().getResource("ProjectStatusActionIT/measure_data.json"), UTF_8)));
225     dbSession.commit();
226     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
227
228     String response = ws.newRequest()
229       .setParam(PARAM_PROJECT_KEY, project.getKey())
230       .execute().getInput();
231
232     assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
233   }
234
235   @Test
236   public void return_live_status_when_branch_is_referenced_by_its_key() throws IOException {
237     ProjectData projectData = db.components().insertPrivateProject();
238     ComponentDto mainBranch = projectData.getMainBranchComponent();
239     String branchName = randomAlphanumeric(248);
240     ComponentDto branch = db.components().insertProjectBranch(mainBranch, b -> b.setKey(branchName));
241
242     dbClient.snapshotDao().insert(dbSession, newAnalysis(branch)
243       .setPeriodMode("last_version")
244       .setPeriodParam("2015-12-07")
245       .setPeriodDate(956789123987L));
246     MetricDto gateDetailsMetric = insertGateDetailMetric();
247     dbClient.measureDao().insert(dbSession,
248       newMeasure(branch, gateDetailsMetric, IOUtils.toString(getClass().getResource("ProjectStatusActionIT/measure_data.json"), UTF_8)));
249     dbSession.commit();
250     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
251
252     String response = ws.newRequest()
253       .setParam(PARAM_PROJECT_KEY, mainBranch.getKey())
254       .setParam(PARAM_BRANCH, branchName)
255       .execute().getInput();
256
257     assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
258   }
259
260   @Test
261   public void return_live_status_when_pull_request_is_referenced_by_its_key() throws IOException {
262     ProjectData projectData = db.components().insertPrivateProject();
263     ComponentDto mainBranch = projectData.getMainBranchComponent();
264     String pullRequestKey = RandomStringUtils.randomAlphanumeric(100);
265     ComponentDto pr = db.components().insertProjectBranch(mainBranch, branch -> branch.setBranchType(BranchType.PULL_REQUEST)
266       .setKey(pullRequestKey));
267
268     dbClient.snapshotDao().insert(dbSession, newAnalysis(pr)
269       .setPeriodMode("last_version")
270       .setPeriodParam("2015-12-07")
271       .setPeriodDate(956789123987L));
272     MetricDto gateDetailsMetric = insertGateDetailMetric();
273     dbClient.measureDao().insert(dbSession,
274       newMeasure(pr, gateDetailsMetric, IOUtils.toString(getClass().getResource("ProjectStatusActionIT/measure_data.json"), UTF_8)));
275     dbSession.commit();
276     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
277
278     String response = ws.newRequest()
279       .setParam(PARAM_PROJECT_KEY, mainBranch.getKey())
280       .setParam(PARAM_PULL_REQUEST, pullRequestKey)
281       .execute().getInput();
282
283     assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
284   }
285
286   @Test
287   public void return_undefined_status_if_specified_analysis_is_not_found() {
288     ProjectData projectData = db.components().insertPrivateProject();
289     ComponentDto mainBranch = projectData.getMainBranchComponent();
290     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch));
291     dbSession.commit();
292     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
293
294     ProjectStatusResponse result = ws.newRequest()
295       .setParam(PARAM_ANALYSIS_ID, snapshot.getUuid())
296       .executeProtobuf(ProjectStatusResponse.class);
297
298     assertThat(result.getProjectStatus().getStatus()).isEqualTo(Status.NONE);
299     assertThat(result.getProjectStatus().getConditionsCount()).isZero();
300   }
301
302   @Test
303   public void return_undefined_status_if_project_is_not_analyzed() {
304     ProjectData projectData = db.components().insertPrivateProject();
305     ComponentDto mainBranch = projectData.getMainBranchComponent();
306     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
307
308     ProjectStatusResponse result = ws.newRequest()
309       .setParam(PARAM_PROJECT_ID, projectData.projectUuid())
310       .executeProtobuf(ProjectStatusResponse.class);
311
312     assertThat(result.getProjectStatus().getStatus()).isEqualTo(Status.NONE);
313     assertThat(result.getProjectStatus().getConditionsCount()).isZero();
314   }
315
316   @Test
317   public void project_administrator_is_allowed_to_get_project_status() {
318     ProjectData projectData = db.components().insertPrivateProject();
319     ComponentDto mainBranch = projectData.getMainBranchComponent();
320     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch));
321     dbSession.commit();
322     userSession.addProjectPermission(UserRole.ADMIN, projectData.getProjectDto());
323
324     ws.newRequest()
325       .setParam(PARAM_ANALYSIS_ID, snapshot.getUuid())
326       .executeProtobuf(ProjectStatusResponse.class);
327   }
328
329   @Test
330   public void project_user_is_allowed_to_get_project_status() {
331     ProjectData projectData = db.components().insertPrivateProject();
332     ComponentDto mainBranch = projectData.getMainBranchComponent();
333     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch));
334     dbSession.commit();
335     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
336
337     ws.newRequest()
338       .setParam(PARAM_ANALYSIS_ID, snapshot.getUuid())
339       .executeProtobuf(ProjectStatusResponse.class);
340   }
341
342   @Test
343   public void check_cayc_compliant_flag() {
344     ProjectData projectData = db.components().insertPrivateProject();
345     ComponentDto mainBranch = projectData.getMainBranchComponent();
346     var qg = db.qualityGates().insertBuiltInQualityGate();
347     db.qualityGates().setDefaultQualityGate(qg);
348     when(qualityGateCaycChecker.checkCaycCompliantFromProject(any(DbSession.class), eq(projectData.projectUuid()))).thenReturn(COMPLIANT);
349     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch));
350     dbSession.commit();
351     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
352
353     ProjectStatusResponse result = ws.newRequest()
354       .setParam(PARAM_ANALYSIS_ID, snapshot.getUuid())
355       .executeProtobuf(ProjectStatusResponse.class);
356
357     assertEquals(COMPLIANT.toString(), result.getProjectStatus().getCaycStatus());
358   }
359
360   @Test
361   public void user_with_project_scan_permission_is_allowed_to_get_project_status() {
362     ProjectData projectData = db.components().insertPrivateProject();
363     ComponentDto mainBranch = projectData.getMainBranchComponent();
364     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch));
365     dbSession.commit();
366     userSession.addProjectPermission(UserRole.SCAN, projectData.getProjectDto());
367
368     var response = ws.newRequest()
369       .setParam(PARAM_ANALYSIS_ID, snapshot.getUuid()).execute();
370
371     assertThat(response.getStatus()).isEqualTo(200);
372   }
373
374   @Test
375   public void user_with_global_scan_permission_is_allowed_to_get_project_status() {
376     ProjectData projectData = db.components().insertPrivateProject();
377     ComponentDto mainBranch = projectData.getMainBranchComponent();
378     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch));
379     dbSession.commit();
380     userSession.addPermission(GlobalPermission.SCAN);
381
382     var response = ws.newRequest()
383       .setParam(PARAM_ANALYSIS_ID, snapshot.getUuid()).execute();
384
385     assertThat(response.getStatus()).isEqualTo(200);
386   }
387
388   @Test
389   public void fail_if_no_snapshot_id_found() {
390     logInAsSystemAdministrator();
391
392     assertThatThrownBy(() -> ws.newRequest()
393       .setParam(PARAM_ANALYSIS_ID, ANALYSIS_ID)
394       .executeProtobuf(ProjectStatusResponse.class))
395       .isInstanceOf(NotFoundException.class)
396       .hasMessageContaining("Analysis with id 'task-uuid' is not found");
397   }
398
399   @Test
400   public void fail_if_insufficient_privileges() {
401     ProjectData projectData = db.components().insertPrivateProject();
402     ComponentDto mainBranch = projectData.getMainBranchComponent();
403     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch));
404     dbSession.commit();
405     userSession.logIn();
406
407     assertThatThrownBy(() -> ws.newRequest()
408       .setParam(PARAM_ANALYSIS_ID, snapshot.getUuid())
409       .executeProtobuf(ProjectStatusResponse.class))
410       .isInstanceOf(ForbiddenException.class);
411   }
412
413   @Test
414   public void fail_if_project_id_and_ce_task_id_provided() {
415     ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
416     logInAsSystemAdministrator();
417
418     assertThatThrownBy(() -> ws.newRequest()
419       .setParam(PARAM_ANALYSIS_ID, "analysis-id")
420       .setParam(PARAM_PROJECT_ID, "project-uuid")
421       .execute().getInput())
422       .isInstanceOf(BadRequestException.class)
423       .hasMessageContaining("Either 'analysisId', 'projectId' or 'projectKey' must be provided");
424   }
425
426   @Test
427   public void fail_if_branch_key_and_pull_request_id_provided() {
428     ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
429     logInAsSystemAdministrator();
430
431     assertThatThrownBy(() -> ws.newRequest()
432       .setParam(PARAM_PROJECT_KEY, "key")
433       .setParam(PARAM_BRANCH, "branch")
434       .setParam(PARAM_PULL_REQUEST, "pr")
435       .execute().getInput())
436       .isInstanceOf(BadRequestException.class)
437       .hasMessageContaining("Either 'branch' or 'pullRequest' can be provided, not both");
438   }
439
440   @Test
441   public void fail_if_no_parameter_provided() {
442     logInAsSystemAdministrator();
443
444     assertThatThrownBy(() -> ws.newRequest()
445       .execute().getInput())
446       .isInstanceOf(BadRequestException.class)
447       .hasMessageContaining("Either 'analysisId', 'projectId' or 'projectKey' must be provided");
448   }
449
450   @Test
451   public void fail_when_using_branch_uuid() {
452     ProjectData projectData = db.components().insertPublicProject();
453     ComponentDto mainBranch = projectData.getMainBranchComponent();
454     userSession.logIn().addProjectPermission(UserRole.ADMIN, projectData.getProjectDto());
455     ComponentDto branch = db.components().insertProjectBranch(mainBranch);
456     SnapshotDto snapshot = db.components().insertSnapshot(branch);
457
458     assertThatThrownBy(() -> ws.newRequest()
459       .setParam("projectId", branch.uuid())
460       .execute())
461       .isInstanceOf(NotFoundException.class)
462       .hasMessageContaining(format("Project '%s' not found", branch.uuid()));
463   }
464
465   private void logInAsSystemAdministrator() {
466     userSession.logIn().setSystemAdministrator();
467   }
468
469   private MetricDto insertGateDetailMetric() {
470     return dbClient.metricDao().insert(dbSession, newMetricDto()
471       .setEnabled(true)
472       .setKey(CoreMetrics.QUALITY_GATE_DETAILS_KEY));
473   }
474
475 }