--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.scanner.qualitygate;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mockito;
+import org.sonar.api.utils.MessageException;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.scanner.bootstrap.DefaultScannerWsClient;
+import org.sonar.scanner.bootstrap.GlobalAnalysisMode;
+import org.sonar.scanner.report.CeTaskReportDataHolder;
+import org.sonar.scanner.scan.ScanProperties;
+import org.sonarqube.ws.Ce;
+import org.sonarqube.ws.Ce.TaskStatus;
+import org.sonarqube.ws.Qualitygates;
+import org.sonarqube.ws.Qualitygates.ProjectStatusResponse.Status;
+import org.sonarqube.ws.client.HttpException;
+import org.sonarqube.ws.client.MockWsResponse;
+import org.sonarqube.ws.client.WsRequest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(DataProviderRunner.class)
+public class QualityGateCheckTest {
+ private DefaultScannerWsClient wsClient = mock(DefaultScannerWsClient.class, Mockito.RETURNS_DEEP_STUBS);
+ private GlobalAnalysisMode analysisMode = mock(GlobalAnalysisMode.class);
+ private CeTaskReportDataHolder reportMetadataHolder = mock(CeTaskReportDataHolder.class);
+ private ScanProperties properties = mock(ScanProperties.class);
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ QualityGateCheck underTest = new QualityGateCheck(wsClient, analysisMode, reportMetadataHolder, properties);
+
+ @Test
+ public void should_pass_if_quality_gate_ok() {
+ when(properties.shouldWaitForQualityGate()).thenReturn(true);
+ when(properties.qualityGateWaitTimeout()).thenReturn(5);
+
+ when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
+
+ MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(TaskStatus.SUCCESS);
+ doReturn(ceTaskWsResponse).when(wsClient).call(newGetCeTaskRequest());
+
+ MockWsResponse qualityGateResponse = getQualityGateWsResponse(Status.OK);
+ doReturn(qualityGateResponse).when(wsClient).call(newGetQualityGateRequest());
+
+ underTest.start();
+
+ underTest.await();
+
+ underTest.stop();
+
+ assertThat(logTester.logs())
+ .containsOnly("Quality Gate - OK");
+ }
+
+ @Test
+ public void should_wait_and_then_pass_if_quality_gate_ok() {
+ when(properties.shouldWaitForQualityGate()).thenReturn(true);
+ when(properties.qualityGateWaitTimeout()).thenReturn(10);
+
+ when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
+
+ MockWsResponse pendingTask = getCeTaskWsResponse(TaskStatus.PENDING);
+ MockWsResponse successTask = getCeTaskWsResponse(TaskStatus.SUCCESS);
+ doReturn(pendingTask, successTask).when(wsClient).call(newGetCeTaskRequest());
+
+ MockWsResponse qualityGateResponse = getQualityGateWsResponse(Status.OK);
+ doReturn(qualityGateResponse).when(wsClient).call(newGetQualityGateRequest());
+
+ underTest.start();
+
+ underTest.await();
+
+ assertThat(logTester.logs())
+ .contains("Quality Gate - OK");
+ }
+
+ @Test
+ public void should_fail_if_quality_gate_none() {
+ when(properties.shouldWaitForQualityGate()).thenReturn(true);
+ when(properties.qualityGateWaitTimeout()).thenReturn(5);
+
+ when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
+
+ MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(TaskStatus.SUCCESS);
+ doReturn(ceTaskWsResponse).when(wsClient).call(newGetCeTaskRequest());
+
+ MockWsResponse qualityGateResponse = getQualityGateWsResponse(Status.ERROR);
+ doReturn(qualityGateResponse).when(wsClient).call(newGetQualityGateRequest());
+
+ underTest.start();
+
+ exception.expect(MessageException.class);
+ exception.expectMessage("Quality Gate - FAILED");
+ underTest.await();
+ }
+
+ @Test
+ public void should_fail_if_quality_gate_error() {
+ when(properties.shouldWaitForQualityGate()).thenReturn(true);
+ when(properties.qualityGateWaitTimeout()).thenReturn(5);
+
+ when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
+
+ MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(TaskStatus.SUCCESS);
+ doReturn(ceTaskWsResponse).when(wsClient).call(newGetCeTaskRequest());
+
+ MockWsResponse qualityGateResponse = getQualityGateWsResponse(Status.ERROR);
+ doReturn(qualityGateResponse).when(wsClient).call(newGetQualityGateRequest());
+
+ underTest.start();
+
+ exception.expect(MessageException.class);
+ exception.expectMessage("Quality Gate - FAILED");
+ underTest.await();
+ }
+
+ @Test
+ public void should_wait_and_then_fail_if_quality_gate_error() {
+ when(properties.shouldWaitForQualityGate()).thenReturn(true);
+ when(properties.qualityGateWaitTimeout()).thenReturn(10);
+
+ when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
+
+ MockWsResponse pendingTask = getCeTaskWsResponse(TaskStatus.PENDING);
+ MockWsResponse successTask = getCeTaskWsResponse(TaskStatus.SUCCESS);
+ doReturn(pendingTask, successTask).when(wsClient).call(newGetCeTaskRequest());
+
+ MockWsResponse qualityGateResponse = getQualityGateWsResponse(Status.ERROR);
+ doReturn(qualityGateResponse).when(wsClient).call(newGetQualityGateRequest());
+
+ underTest.start();
+
+ exception.expect(MessageException.class);
+ exception.expectMessage("Quality Gate - FAILED");
+
+ underTest.await();
+ }
+
+ @Test
+ public void should_fail_if_quality_gate_timeout_exceeded() {
+ when(properties.shouldWaitForQualityGate()).thenReturn(true);
+ when(properties.qualityGateWaitTimeout()).thenReturn(1);
+
+ when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
+
+ MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(TaskStatus.PENDING);
+ doReturn(ceTaskWsResponse).when(wsClient).call(newGetCeTaskRequest());
+
+ underTest.start();
+
+ exception.expect(MessageException.class);
+ exception.expectMessage("Quality Gate check timeout exceeded");
+ underTest.await();
+ }
+
+ @Test
+ public void should_fail_if_cant_call_ws_for_quality_gate() {
+ when(properties.shouldWaitForQualityGate()).thenReturn(true);
+ when(properties.qualityGateWaitTimeout()).thenReturn(5);
+
+ when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
+
+ MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(TaskStatus.SUCCESS);
+ doReturn(ceTaskWsResponse).when(wsClient).call(newGetCeTaskRequest());
+
+ doThrow(new HttpException("quality-gate-url", 400, "content")).when(wsClient).call(newGetQualityGateRequest());
+
+ underTest.start();
+
+ exception.expect(MessageException.class);
+ exception.expectMessage("Failed to get Quality Gate status - HTTP code 400: content");
+ underTest.await();
+ }
+
+ @Test
+ public void should_fail_if_invalid_response_from_quality_gate_ws() {
+ when(properties.shouldWaitForQualityGate()).thenReturn(true);
+ when(properties.qualityGateWaitTimeout()).thenReturn(5);
+
+ when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
+
+ MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(TaskStatus.SUCCESS);
+ doReturn(ceTaskWsResponse).when(wsClient).call(newGetCeTaskRequest());
+
+ MockWsResponse qualityGateResponse = new MockWsResponse();
+ qualityGateResponse.setRequestUrl("quality-gate-url");
+ qualityGateResponse.setContent("blabla");
+ doReturn(qualityGateResponse).when(wsClient).call(newGetQualityGateRequest());
+
+ underTest.start();
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Failed to parse response from quality-gate-url");
+ underTest.await();
+ }
+
+ @Test
+ public void should_fail_if_cant_call_ws_for_task() {
+ when(properties.shouldWaitForQualityGate()).thenReturn(true);
+ when(properties.qualityGateWaitTimeout()).thenReturn(5);
+
+ when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
+
+ when(wsClient.call(newGetCeTaskRequest())).thenThrow(new HttpException("task-url", 400, "content"));
+
+ underTest.start();
+
+ exception.expect(MessageException.class);
+ exception.expectMessage("Failed to get CE Task status - HTTP code 400: content");
+ underTest.await();
+ }
+
+ @Test
+ public void should_fail_if_invalid_response_from_ws_task() {
+ when(properties.shouldWaitForQualityGate()).thenReturn(true);
+ when(properties.qualityGateWaitTimeout()).thenReturn(5);
+
+ when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
+
+ MockWsResponse getCeTaskRequest = new MockWsResponse();
+ getCeTaskRequest.setRequestUrl("ce-task-url");
+ getCeTaskRequest.setContent("blabla");
+
+ when(wsClient.call(newGetCeTaskRequest())).thenReturn(getCeTaskRequest);
+
+ underTest.start();
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Failed to parse response from ce-task-url");
+ underTest.await();
+ }
+
+ @Test
+ @UseDataProvider("ceTaskNotSucceededStatuses")
+ public void should_fail_if_task_not_succeeded(TaskStatus taskStatus) {
+ when(properties.shouldWaitForQualityGate()).thenReturn(true);
+ when(properties.qualityGateWaitTimeout()).thenReturn(5);
+
+ when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
+
+ MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(taskStatus);
+ when(wsClient.call(newGetCeTaskRequest())).thenReturn(ceTaskWsResponse);
+
+ underTest.start();
+
+ exception.expect(MessageException.class);
+ exception.expectMessage("CE Task finished abnormally with status: " + taskStatus.name());
+ underTest.await();
+ }
+
+ private WsRequest newGetCeTaskRequest() {
+ return argThat(new WsRequestPathMatcher("api/ce/task"));
+ }
+
+ private MockWsResponse getCeTaskWsResponse(TaskStatus status) {
+ MockWsResponse submitMockResponse = new MockWsResponse();
+ submitMockResponse.setContent(Ce.TaskResponse.newBuilder()
+ .setTask(Ce.Task.newBuilder().setStatus(status))
+ .build()
+ .toByteArray());
+ return submitMockResponse;
+ }
+
+ @Test
+ public void should_skip_wait_if_disabled() {
+ when(properties.shouldWaitForQualityGate()).thenReturn(false);
+
+ underTest.start();
+
+ underTest.await();
+
+ assertThat(logTester.logs())
+ .contains("Quality Gate check disabled - skipping");
+ }
+
+ @Test
+ public void should_fail_if_enabled_with_medium_test() {
+ when(properties.shouldWaitForQualityGate()).thenReturn(true);
+ when(analysisMode.isMediumTest()).thenReturn(true);
+
+ underTest.start();
+
+ exception.expect(IllegalStateException.class);
+
+ underTest.await();
+ }
+
+ private WsRequest newGetQualityGateRequest() {
+ return argThat(new WsRequestPathMatcher("api/qualitygates/project_status"));
+ }
+
+ private MockWsResponse getQualityGateWsResponse(Status status) {
+ MockWsResponse qualityGateWsResponse = new MockWsResponse();
+ qualityGateWsResponse.setContent(Qualitygates.ProjectStatusResponse.newBuilder()
+ .setProjectStatus(Qualitygates.ProjectStatusResponse.ProjectStatus.newBuilder()
+ .setStatus(status)
+ .build())
+ .build()
+ .toByteArray());
+ return qualityGateWsResponse;
+ }
+
+ @DataProvider
+ public static Object[][] ceTaskNotSucceededStatuses() {
+ return new Object[][] {
+ {TaskStatus.CANCELED},
+ {TaskStatus.FAILED},
+ };
+ }
+
+ private static class WsRequestPathMatcher implements ArgumentMatcher<WsRequest> {
+ String path;
+
+ WsRequestPathMatcher(String path) {
+ this.path = path;
+ }
+
+ @Override
+ public boolean matches(WsRequest right) {
+ return path.equals(right.getPath());
+ }
+ }
+}