You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ProjectStatusActionTest.java 19KB

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