]> source.dussan.org Git - sonarqube.git/blob
bad2353ba3fd2c5fa93b175da48d47c8a986a9c7
[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 java.nio.charset.StandardCharsets;
24 import org.apache.commons.io.IOUtils;
25 import org.apache.commons.lang3.RandomStringUtils;
26 import org.junit.Before;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.sonar.api.measures.CoreMetrics;
30 import org.sonar.api.server.ws.WebService;
31 import org.sonar.api.utils.System2;
32 import org.sonar.api.web.UserRole;
33 import org.sonar.db.DbClient;
34 import org.sonar.db.DbSession;
35 import org.sonar.db.DbTester;
36 import org.sonar.db.component.BranchType;
37 import org.sonar.db.component.ComponentDto;
38 import org.sonar.db.component.ProjectData;
39 import org.sonar.db.component.SnapshotDto;
40 import org.sonar.db.metric.MetricDto;
41 import org.sonar.db.permission.GlobalPermission;
42 import org.sonar.server.component.TestComponentFinder;
43 import org.sonar.server.exceptions.BadRequestException;
44 import org.sonar.server.exceptions.ForbiddenException;
45 import org.sonar.server.exceptions.NotFoundException;
46 import org.sonar.server.qualitygate.QualityGateCaycChecker;
47 import org.sonar.server.tester.UserSessionRule;
48 import org.sonar.server.ws.WsActionTester;
49 import org.sonarqube.ws.Qualitygates.ProjectStatusResponse;
50 import org.sonarqube.ws.Qualitygates.ProjectStatusResponse.Status;
51
52 import static java.lang.String.format;
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.newLiveMeasure;
64 import static org.sonar.db.measure.MeasureTesting.newMeasureDto;
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.measureDao().insert(dbSession,
119       newMeasureDto(gateDetailsMetric, mainBranch, snapshot)
120         .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionIT/measure_data.json"), StandardCharsets.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.measureDao().insert(dbSession,
146       newMeasureDto(gateDetailsMetric, mainBranch, pastAnalysis)
147         .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionIT/measure_data.json"))));
148     dbClient.measureDao().insert(dbSession,
149       newMeasureDto(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.liveMeasureDao().insert(dbSession,
171       newLiveMeasure(mainBranch, gateDetailsMetric)
172         .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionIT/measure_data.json"))));
173     dbSession.commit();
174     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
175
176     String response = ws.newRequest()
177       .setParam(PARAM_PROJECT_ID, projectData.projectUuid())
178       .execute().getInput();
179
180     assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
181   }
182
183   @Test
184   public void return_past_status_when_branch_is_referenced_by_past_analysis_id() throws IOException {
185     ProjectData projectData = db.components().insertPrivateProject();
186     ComponentDto mainBranch = projectData.getMainBranchComponent();
187     ComponentDto branch = db.components().insertProjectBranch(mainBranch);
188     SnapshotDto pastAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(branch)
189       .setLast(false)
190       .setPeriodMode("last_version")
191       .setPeriodParam("2015-12-07")
192       .setPeriodDate(956789123987L));
193     SnapshotDto lastAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(branch)
194       .setLast(true)
195       .setPeriodMode("last_version")
196       .setPeriodParam("2016-12-07")
197       .setPeriodDate(1_500L));
198     MetricDto gateDetailsMetric = insertGateDetailMetric();
199     dbClient.measureDao().insert(dbSession,
200       newMeasureDto(gateDetailsMetric, branch, pastAnalysis)
201         .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionIT/measure_data.json"))));
202     dbClient.measureDao().insert(dbSession,
203       newMeasureDto(gateDetailsMetric, branch, lastAnalysis)
204         .setData("not_used"));
205     dbSession.commit();
206     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
207
208     String response = ws.newRequest()
209       .setParam(PARAM_ANALYSIS_ID, pastAnalysis.getUuid())
210       .execute().getInput();
211
212     assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
213   }
214
215   @Test
216   public void return_live_status_when_project_is_referenced_by_its_key() throws IOException {
217     ProjectData projectData = db.components().insertPrivateProject();
218     ComponentDto project = projectData.getMainBranchComponent();
219     dbClient.snapshotDao().insert(dbSession, newAnalysis(project)
220       .setPeriodMode("last_version")
221       .setPeriodParam("2015-12-07")
222       .setPeriodDate(956789123987L));
223     MetricDto gateDetailsMetric = insertGateDetailMetric();
224     dbClient.liveMeasureDao().insert(dbSession,
225       newLiveMeasure(project, gateDetailsMetric)
226         .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionIT/measure_data.json"))));
227     dbSession.commit();
228     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
229
230     String response = ws.newRequest()
231       .setParam(PARAM_PROJECT_KEY, project.getKey())
232       .execute().getInput();
233
234     assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
235   }
236
237   @Test
238   public void return_live_status_when_branch_is_referenced_by_its_key() throws IOException {
239     ProjectData projectData = db.components().insertPrivateProject();
240     ComponentDto mainBranch = projectData.getMainBranchComponent();
241     String branchName = randomAlphanumeric(248);
242     ComponentDto branch = db.components().insertProjectBranch(mainBranch, b -> b.setKey(branchName));
243
244     dbClient.snapshotDao().insert(dbSession, newAnalysis(branch)
245       .setPeriodMode("last_version")
246       .setPeriodParam("2015-12-07")
247       .setPeriodDate(956789123987L));
248     MetricDto gateDetailsMetric = insertGateDetailMetric();
249     dbClient.liveMeasureDao().insert(dbSession,
250       newLiveMeasure(branch, gateDetailsMetric)
251         .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionIT/measure_data.json"))));
252     dbSession.commit();
253     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
254
255     String response = ws.newRequest()
256       .setParam(PARAM_PROJECT_KEY, mainBranch.getKey())
257       .setParam(PARAM_BRANCH, branchName)
258       .execute().getInput();
259
260     assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
261   }
262
263   @Test
264   public void return_live_status_when_pull_request_is_referenced_by_its_key() throws IOException {
265     ProjectData projectData = db.components().insertPrivateProject();
266     ComponentDto mainBranch = projectData.getMainBranchComponent();
267     String pullRequestKey = RandomStringUtils.randomAlphanumeric(100);
268     ComponentDto pr = db.components().insertProjectBranch(mainBranch, branch -> branch.setBranchType(BranchType.PULL_REQUEST)
269       .setKey(pullRequestKey));
270
271     dbClient.snapshotDao().insert(dbSession, newAnalysis(pr)
272       .setPeriodMode("last_version")
273       .setPeriodParam("2015-12-07")
274       .setPeriodDate(956789123987L));
275     MetricDto gateDetailsMetric = insertGateDetailMetric();
276     dbClient.liveMeasureDao().insert(dbSession,
277       newLiveMeasure(pr, gateDetailsMetric)
278         .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionIT/measure_data.json"))));
279     dbSession.commit();
280     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
281
282     String response = ws.newRequest()
283       .setParam(PARAM_PROJECT_KEY, mainBranch.getKey())
284       .setParam(PARAM_PULL_REQUEST, pullRequestKey)
285       .execute().getInput();
286
287     assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
288   }
289
290   @Test
291   public void return_undefined_status_if_specified_analysis_is_not_found() {
292     ProjectData projectData = db.components().insertPrivateProject();
293     ComponentDto mainBranch = projectData.getMainBranchComponent();
294     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch));
295     dbSession.commit();
296     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
297
298     ProjectStatusResponse result = ws.newRequest()
299       .setParam(PARAM_ANALYSIS_ID, snapshot.getUuid())
300       .executeProtobuf(ProjectStatusResponse.class);
301
302     assertThat(result.getProjectStatus().getStatus()).isEqualTo(Status.NONE);
303     assertThat(result.getProjectStatus().getConditionsCount()).isZero();
304   }
305
306   @Test
307   public void return_undefined_status_if_project_is_not_analyzed() {
308     ProjectData projectData = db.components().insertPrivateProject();
309     ComponentDto mainBranch = projectData.getMainBranchComponent();
310     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
311
312     ProjectStatusResponse result = ws.newRequest()
313       .setParam(PARAM_PROJECT_ID, projectData.projectUuid())
314       .executeProtobuf(ProjectStatusResponse.class);
315
316     assertThat(result.getProjectStatus().getStatus()).isEqualTo(Status.NONE);
317     assertThat(result.getProjectStatus().getConditionsCount()).isZero();
318   }
319
320   @Test
321   public void project_administrator_is_allowed_to_get_project_status() {
322     ProjectData projectData = db.components().insertPrivateProject();
323     ComponentDto mainBranch = projectData.getMainBranchComponent();
324     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch));
325     dbSession.commit();
326     userSession.addProjectPermission(UserRole.ADMIN, projectData.getProjectDto());
327
328     ws.newRequest()
329       .setParam(PARAM_ANALYSIS_ID, snapshot.getUuid())
330       .executeProtobuf(ProjectStatusResponse.class);
331   }
332
333   @Test
334   public void project_user_is_allowed_to_get_project_status() {
335     ProjectData projectData = db.components().insertPrivateProject();
336     ComponentDto mainBranch = projectData.getMainBranchComponent();
337     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch));
338     dbSession.commit();
339     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
340
341     ws.newRequest()
342       .setParam(PARAM_ANALYSIS_ID, snapshot.getUuid())
343       .executeProtobuf(ProjectStatusResponse.class);
344   }
345
346   @Test
347   public void check_cayc_compliant_flag() {
348     ProjectData projectData = db.components().insertPrivateProject();
349     ComponentDto mainBranch = projectData.getMainBranchComponent();
350     var qg = db.qualityGates().insertBuiltInQualityGate();
351     db.qualityGates().setDefaultQualityGate(qg);
352     when(qualityGateCaycChecker.checkCaycCompliantFromProject(any(DbSession.class), eq(projectData.projectUuid()))).thenReturn(COMPLIANT);
353     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch));
354     dbSession.commit();
355     userSession.addProjectPermission(UserRole.USER, projectData.getProjectDto());
356
357     ProjectStatusResponse result = ws.newRequest()
358       .setParam(PARAM_ANALYSIS_ID, snapshot.getUuid())
359       .executeProtobuf(ProjectStatusResponse.class);
360
361     assertEquals(COMPLIANT.toString(), result.getProjectStatus().getCaycStatus());
362   }
363
364   @Test
365   public void user_with_project_scan_permission_is_allowed_to_get_project_status() {
366     ProjectData projectData = db.components().insertPrivateProject();
367     ComponentDto mainBranch = projectData.getMainBranchComponent();
368     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch));
369     dbSession.commit();
370     userSession.addProjectPermission(UserRole.SCAN, projectData.getProjectDto());
371
372     var response = ws.newRequest()
373       .setParam(PARAM_ANALYSIS_ID, snapshot.getUuid()).execute();
374
375     assertThat(response.getStatus()).isEqualTo(200);
376   }
377
378   @Test
379   public void user_with_global_scan_permission_is_allowed_to_get_project_status() {
380     ProjectData projectData = db.components().insertPrivateProject();
381     ComponentDto mainBranch = projectData.getMainBranchComponent();
382     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch));
383     dbSession.commit();
384     userSession.addPermission(GlobalPermission.SCAN);
385
386     var response = ws.newRequest()
387       .setParam(PARAM_ANALYSIS_ID, snapshot.getUuid()).execute();
388
389     assertThat(response.getStatus()).isEqualTo(200);
390   }
391
392   @Test
393   public void fail_if_no_snapshot_id_found() {
394     logInAsSystemAdministrator();
395
396     assertThatThrownBy(() -> ws.newRequest()
397       .setParam(PARAM_ANALYSIS_ID, ANALYSIS_ID)
398       .executeProtobuf(ProjectStatusResponse.class))
399       .isInstanceOf(NotFoundException.class)
400       .hasMessageContaining("Analysis with id 'task-uuid' is not found");
401   }
402
403   @Test
404   public void fail_if_insufficient_privileges() {
405     ProjectData projectData = db.components().insertPrivateProject();
406     ComponentDto mainBranch = projectData.getMainBranchComponent();
407     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(mainBranch));
408     dbSession.commit();
409     userSession.logIn();
410
411     assertThatThrownBy(() -> ws.newRequest()
412       .setParam(PARAM_ANALYSIS_ID, snapshot.getUuid())
413       .executeProtobuf(ProjectStatusResponse.class))
414       .isInstanceOf(ForbiddenException.class);
415   }
416
417   @Test
418   public void fail_if_project_id_and_ce_task_id_provided() {
419     ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
420     logInAsSystemAdministrator();
421
422     assertThatThrownBy(() -> ws.newRequest()
423       .setParam(PARAM_ANALYSIS_ID, "analysis-id")
424       .setParam(PARAM_PROJECT_ID, "project-uuid")
425       .execute().getInput())
426       .isInstanceOf(BadRequestException.class)
427       .hasMessageContaining("Either 'analysisId', 'projectId' or 'projectKey' must be provided");
428   }
429
430   @Test
431   public void fail_if_branch_key_and_pull_request_id_provided() {
432     ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
433     logInAsSystemAdministrator();
434
435     assertThatThrownBy(() -> ws.newRequest()
436       .setParam(PARAM_PROJECT_KEY, "key")
437       .setParam(PARAM_BRANCH, "branch")
438       .setParam(PARAM_PULL_REQUEST, "pr")
439       .execute().getInput())
440       .isInstanceOf(BadRequestException.class)
441       .hasMessageContaining("Either 'branch' or 'pullRequest' can be provided, not both");
442   }
443
444   @Test
445   public void fail_if_no_parameter_provided() {
446     logInAsSystemAdministrator();
447
448     assertThatThrownBy(() -> ws.newRequest()
449       .execute().getInput())
450       .isInstanceOf(BadRequestException.class)
451       .hasMessageContaining("Either 'analysisId', 'projectId' or 'projectKey' must be provided");
452   }
453
454   @Test
455   public void fail_when_using_branch_uuid() {
456     ProjectData projectData = db.components().insertPublicProject();
457     ComponentDto mainBranch = projectData.getMainBranchComponent();
458     userSession.logIn().addProjectPermission(UserRole.ADMIN, projectData.getProjectDto());
459     ComponentDto branch = db.components().insertProjectBranch(mainBranch);
460     SnapshotDto snapshot = db.components().insertSnapshot(branch);
461
462     assertThatThrownBy(() -> ws.newRequest()
463       .setParam("projectId", branch.uuid())
464       .execute())
465       .isInstanceOf(NotFoundException.class)
466       .hasMessageContaining(format("Project '%s' not found", branch.uuid()));
467   }
468
469   private void logInAsSystemAdministrator() {
470     userSession.logIn().setSystemAdministrator();
471   }
472
473   private MetricDto insertGateDetailMetric() {
474     return dbClient.metricDao().insert(dbSession, newMetricDto()
475       .setEnabled(true)
476       .setKey(CoreMetrics.QUALITY_GATE_DETAILS_KEY));
477   }
478
479 }