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.

QualityGateCheckTest.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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.scanner.qualitygate;
  21. import java.util.stream.Stream;
  22. import org.junit.jupiter.api.BeforeEach;
  23. import org.junit.jupiter.api.Test;
  24. import org.junit.jupiter.api.extension.ExtendWith;
  25. import org.junit.jupiter.api.extension.RegisterExtension;
  26. import org.junit.jupiter.params.ParameterizedTest;
  27. import org.junit.jupiter.params.provider.MethodSource;
  28. import org.mockito.Answers;
  29. import org.mockito.ArgumentMatcher;
  30. import org.mockito.Mock;
  31. import org.mockito.junit.jupiter.MockitoExtension;
  32. import org.slf4j.event.Level;
  33. import org.sonar.api.testfixtures.log.LogTesterJUnit5;
  34. import org.sonar.api.utils.MessageException;
  35. import org.sonar.scanner.http.DefaultScannerWsClient;
  36. import org.sonar.scanner.bootstrap.GlobalAnalysisMode;
  37. import org.sonar.scanner.report.CeTaskReportDataHolder;
  38. import org.sonar.scanner.scan.ScanProperties;
  39. import org.sonarqube.ws.Ce;
  40. import org.sonarqube.ws.Ce.TaskStatus;
  41. import org.sonarqube.ws.Qualitygates;
  42. import org.sonarqube.ws.Qualitygates.ProjectStatusResponse.Status;
  43. import org.sonarqube.ws.client.HttpException;
  44. import org.sonarqube.ws.client.MockWsResponse;
  45. import org.sonarqube.ws.client.WsRequest;
  46. import static org.assertj.core.api.Assertions.assertThat;
  47. import static org.assertj.core.api.Assertions.assertThatThrownBy;
  48. import static org.mockito.ArgumentMatchers.argThat;
  49. import static org.mockito.Mockito.doReturn;
  50. import static org.mockito.Mockito.doThrow;
  51. import static org.mockito.Mockito.when;
  52. @ExtendWith(MockitoExtension.class)
  53. class QualityGateCheckTest {
  54. @Mock(answer = Answers.RETURNS_DEEP_STUBS)
  55. private DefaultScannerWsClient wsClient;
  56. @Mock
  57. private GlobalAnalysisMode analysisMode;
  58. @Mock
  59. private CeTaskReportDataHolder reportMetadataHolder;
  60. @Mock
  61. private ScanProperties properties;
  62. @RegisterExtension
  63. private final LogTesterJUnit5 logTester = new LogTesterJUnit5();
  64. private QualityGateCheck underTest;
  65. @BeforeEach
  66. void before() {
  67. underTest = new QualityGateCheck(wsClient, analysisMode, reportMetadataHolder, properties);
  68. logTester.setLevel(Level.DEBUG);
  69. }
  70. @Test
  71. void should_pass_if_quality_gate_ok() {
  72. when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
  73. when(reportMetadataHolder.getDashboardUrl()).thenReturn("http://dashboard-url.com");
  74. when(properties.shouldWaitForQualityGate()).thenReturn(true);
  75. when(properties.qualityGateWaitTimeout()).thenReturn(5);
  76. MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(TaskStatus.SUCCESS);
  77. doReturn(ceTaskWsResponse).when(wsClient).call(newGetCeTaskRequest());
  78. MockWsResponse qualityGateResponse = getQualityGateWsResponse(Status.OK);
  79. doReturn(qualityGateResponse).when(wsClient).call(newGetQualityGateRequest());
  80. underTest.start();
  81. underTest.await();
  82. underTest.stop();
  83. assertThat(logTester.logs(Level.INFO))
  84. .contains(
  85. "Waiting for the analysis report to be processed (max 5s)",
  86. "QUALITY GATE STATUS: PASSED - View details on http://dashboard-url.com");
  87. }
  88. @Test
  89. void should_wait_and_then_pass_if_quality_gate_ok() {
  90. when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
  91. when(reportMetadataHolder.getDashboardUrl()).thenReturn("http://dashboard-url.com");
  92. when(properties.shouldWaitForQualityGate()).thenReturn(true);
  93. when(properties.qualityGateWaitTimeout()).thenReturn(10);
  94. MockWsResponse pendingTask = getCeTaskWsResponse(TaskStatus.PENDING);
  95. MockWsResponse successTask = getCeTaskWsResponse(TaskStatus.SUCCESS);
  96. doReturn(pendingTask, successTask).when(wsClient).call(newGetCeTaskRequest());
  97. MockWsResponse qualityGateResponse = getQualityGateWsResponse(Status.OK);
  98. doReturn(qualityGateResponse).when(wsClient).call(newGetQualityGateRequest());
  99. underTest.start();
  100. underTest.await();
  101. assertThat(logTester.logs())
  102. .contains("QUALITY GATE STATUS: PASSED - View details on http://dashboard-url.com");
  103. }
  104. @Test
  105. void should_fail_if_quality_gate_none() {
  106. when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
  107. when(reportMetadataHolder.getDashboardUrl()).thenReturn("http://dashboard-url.com");
  108. when(properties.shouldWaitForQualityGate()).thenReturn(true);
  109. when(properties.qualityGateWaitTimeout()).thenReturn(5);
  110. MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(TaskStatus.SUCCESS);
  111. doReturn(ceTaskWsResponse).when(wsClient).call(newGetCeTaskRequest());
  112. MockWsResponse qualityGateResponse = getQualityGateWsResponse(Status.ERROR);
  113. doReturn(qualityGateResponse).when(wsClient).call(newGetQualityGateRequest());
  114. underTest.start();
  115. assertThatThrownBy(() -> underTest.await())
  116. .isInstanceOf(MessageException.class)
  117. .hasMessage("QUALITY GATE STATUS: FAILED - View details on http://dashboard-url.com");
  118. }
  119. @Test
  120. void should_fail_if_quality_gate_error() {
  121. when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
  122. when(reportMetadataHolder.getDashboardUrl()).thenReturn("http://dashboard-url.com");
  123. when(properties.shouldWaitForQualityGate()).thenReturn(true);
  124. when(properties.qualityGateWaitTimeout()).thenReturn(5);
  125. MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(TaskStatus.SUCCESS);
  126. doReturn(ceTaskWsResponse).when(wsClient).call(newGetCeTaskRequest());
  127. MockWsResponse qualityGateResponse = getQualityGateWsResponse(Status.ERROR);
  128. doReturn(qualityGateResponse).when(wsClient).call(newGetQualityGateRequest());
  129. underTest.start();
  130. assertThatThrownBy(() -> underTest.await())
  131. .isInstanceOf(MessageException.class)
  132. .hasMessage("QUALITY GATE STATUS: FAILED - View details on http://dashboard-url.com");
  133. }
  134. @Test
  135. void should_wait_and_then_fail_if_quality_gate_error() {
  136. when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
  137. when(reportMetadataHolder.getDashboardUrl()).thenReturn("http://dashboard-url.com");
  138. when(properties.shouldWaitForQualityGate()).thenReturn(true);
  139. when(properties.qualityGateWaitTimeout()).thenReturn(10);
  140. MockWsResponse pendingTask = getCeTaskWsResponse(TaskStatus.PENDING);
  141. MockWsResponse successTask = getCeTaskWsResponse(TaskStatus.SUCCESS);
  142. doReturn(pendingTask, successTask).when(wsClient).call(newGetCeTaskRequest());
  143. MockWsResponse qualityGateResponse = getQualityGateWsResponse(Status.ERROR);
  144. doReturn(qualityGateResponse).when(wsClient).call(newGetQualityGateRequest());
  145. underTest.start();
  146. assertThatThrownBy(() -> underTest.await())
  147. .isInstanceOf(MessageException.class)
  148. .hasMessage("QUALITY GATE STATUS: FAILED - View details on http://dashboard-url.com");
  149. }
  150. @Test
  151. void should_fail_if_quality_gate_timeout_exceeded() {
  152. when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
  153. when(reportMetadataHolder.getDashboardUrl()).thenReturn("http://dashboard-url.com");
  154. when(properties.shouldWaitForQualityGate()).thenReturn(true);
  155. when(properties.qualityGateWaitTimeout()).thenReturn(1);
  156. MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(TaskStatus.PENDING);
  157. doReturn(ceTaskWsResponse).when(wsClient).call(newGetCeTaskRequest());
  158. underTest.start();
  159. assertThatThrownBy(() -> underTest.await())
  160. .isInstanceOf(MessageException.class)
  161. .hasMessage("Quality Gate check timeout exceeded - View details on http://dashboard-url.com");
  162. }
  163. @Test
  164. void should_fail_if_cant_call_ws_for_quality_gate() {
  165. when(properties.shouldWaitForQualityGate()).thenReturn(true);
  166. when(properties.qualityGateWaitTimeout()).thenReturn(5);
  167. MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(TaskStatus.SUCCESS);
  168. doReturn(ceTaskWsResponse).when(wsClient).call(newGetCeTaskRequest());
  169. doThrow(new HttpException("quality-gate-url", 400, "content")).when(wsClient).call(newGetQualityGateRequest());
  170. underTest.start();
  171. assertThatThrownBy(() -> underTest.await())
  172. .isInstanceOf(MessageException.class)
  173. .hasMessage("Failed to get Quality Gate status - HTTP code 400: content");
  174. }
  175. @Test
  176. void should_fail_if_invalid_response_from_quality_gate_ws() {
  177. when(properties.shouldWaitForQualityGate()).thenReturn(true);
  178. when(properties.qualityGateWaitTimeout()).thenReturn(5);
  179. MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(TaskStatus.SUCCESS);
  180. doReturn(ceTaskWsResponse).when(wsClient).call(newGetCeTaskRequest());
  181. MockWsResponse qualityGateResponse = new MockWsResponse();
  182. qualityGateResponse.setRequestUrl("quality-gate-url");
  183. qualityGateResponse.setContent("blabla");
  184. doReturn(qualityGateResponse).when(wsClient).call(newGetQualityGateRequest());
  185. underTest.start();
  186. assertThatThrownBy(() -> underTest.await())
  187. .isInstanceOf(IllegalStateException.class)
  188. .hasMessage("Failed to parse response from quality-gate-url");
  189. }
  190. @Test
  191. void should_fail_if_cant_call_ws_for_task() {
  192. when(properties.shouldWaitForQualityGate()).thenReturn(true);
  193. when(properties.qualityGateWaitTimeout()).thenReturn(5);
  194. when(wsClient.call(newGetCeTaskRequest())).thenThrow(new HttpException("task-url", 400, "content"));
  195. underTest.start();
  196. assertThatThrownBy(() -> underTest.await())
  197. .isInstanceOf(MessageException.class)
  198. .hasMessage("Failed to get CE Task status - HTTP code 400: content");
  199. }
  200. @Test
  201. void should_fail_if_invalid_response_from_ws_task() {
  202. when(properties.shouldWaitForQualityGate()).thenReturn(true);
  203. when(properties.qualityGateWaitTimeout()).thenReturn(5);
  204. MockWsResponse getCeTaskRequest = new MockWsResponse();
  205. getCeTaskRequest.setRequestUrl("ce-task-url");
  206. getCeTaskRequest.setContent("blabla");
  207. when(wsClient.call(newGetCeTaskRequest())).thenReturn(getCeTaskRequest);
  208. underTest.start();
  209. assertThatThrownBy(() -> underTest.await())
  210. .isInstanceOf(IllegalStateException.class)
  211. .hasMessage("Failed to parse response from ce-task-url");
  212. }
  213. @ParameterizedTest
  214. @MethodSource("ceTaskNotSucceededStatuses")
  215. void should_fail_if_task_not_succeeded(TaskStatus taskStatus) {
  216. when(properties.shouldWaitForQualityGate()).thenReturn(true);
  217. when(properties.qualityGateWaitTimeout()).thenReturn(5);
  218. MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(taskStatus);
  219. when(wsClient.call(newGetCeTaskRequest())).thenReturn(ceTaskWsResponse);
  220. underTest.start();
  221. assertThatThrownBy(() -> underTest.await())
  222. .isInstanceOf(MessageException.class)
  223. .hasMessageContaining("CE Task finished abnormally with status: " + taskStatus.name());
  224. }
  225. private WsRequest newGetCeTaskRequest() {
  226. return argThat(new WsRequestPathMatcher("api/ce/task"));
  227. }
  228. private MockWsResponse getCeTaskWsResponse(TaskStatus status) {
  229. MockWsResponse submitMockResponse = new MockWsResponse();
  230. submitMockResponse.setContent(Ce.TaskResponse.newBuilder()
  231. .setTask(Ce.Task.newBuilder().setStatus(status))
  232. .build()
  233. .toByteArray());
  234. return submitMockResponse;
  235. }
  236. @Test
  237. void should_skip_wait_if_disabled() {
  238. when(properties.shouldWaitForQualityGate()).thenReturn(false);
  239. underTest.start();
  240. underTest.await();
  241. assertThat(logTester.logs())
  242. .contains("Quality Gate check disabled - skipping");
  243. }
  244. @Test
  245. void should_fail_if_enabled_with_medium_test() {
  246. when(properties.shouldWaitForQualityGate()).thenReturn(true);
  247. when(analysisMode.isMediumTest()).thenReturn(true);
  248. underTest.start();
  249. assertThatThrownBy(() -> underTest.await())
  250. .isInstanceOf(IllegalStateException.class);
  251. }
  252. private WsRequest newGetQualityGateRequest() {
  253. return argThat(new WsRequestPathMatcher("api/qualitygates/project_status"));
  254. }
  255. private MockWsResponse getQualityGateWsResponse(Status status) {
  256. MockWsResponse qualityGateWsResponse = new MockWsResponse();
  257. qualityGateWsResponse.setContent(Qualitygates.ProjectStatusResponse.newBuilder()
  258. .setProjectStatus(Qualitygates.ProjectStatusResponse.ProjectStatus.newBuilder()
  259. .setStatus(status)
  260. .build())
  261. .build()
  262. .toByteArray());
  263. return qualityGateWsResponse;
  264. }
  265. private static Stream<TaskStatus> ceTaskNotSucceededStatuses() {
  266. return Stream.of(
  267. TaskStatus.CANCELED,
  268. TaskStatus.FAILED
  269. );
  270. }
  271. private static class WsRequestPathMatcher implements ArgumentMatcher<WsRequest> {
  272. String path;
  273. WsRequestPathMatcher(String path) {
  274. this.path = path;
  275. }
  276. @Override
  277. public boolean matches(WsRequest right) {
  278. return path.equals(right.getPath());
  279. }
  280. }
  281. }