123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- /*
- * SonarQube
- * Copyright (C) 2009-2024 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 java.util.stream.Stream;
- import org.junit.jupiter.api.BeforeEach;
- import org.junit.jupiter.api.Test;
- import org.junit.jupiter.api.extension.ExtendWith;
- import org.junit.jupiter.api.extension.RegisterExtension;
- import org.junit.jupiter.params.ParameterizedTest;
- import org.junit.jupiter.params.provider.MethodSource;
- import org.mockito.Answers;
- import org.mockito.ArgumentMatcher;
- import org.mockito.Mock;
- import org.mockito.junit.jupiter.MockitoExtension;
- import org.slf4j.event.Level;
- import org.sonar.api.testfixtures.log.LogTesterJUnit5;
- import org.sonar.api.utils.MessageException;
- import org.sonar.scanner.http.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.assertj.core.api.Assertions.assertThatThrownBy;
- import static org.mockito.ArgumentMatchers.argThat;
- import static org.mockito.Mockito.doReturn;
- import static org.mockito.Mockito.doThrow;
- import static org.mockito.Mockito.when;
-
- @ExtendWith(MockitoExtension.class)
- class QualityGateCheckTest {
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private DefaultScannerWsClient wsClient;
- @Mock
- private GlobalAnalysisMode analysisMode;
- @Mock
- private CeTaskReportDataHolder reportMetadataHolder;
- @Mock
- private ScanProperties properties;
-
- @RegisterExtension
- private final LogTesterJUnit5 logTester = new LogTesterJUnit5();
-
- private QualityGateCheck underTest;
-
- @BeforeEach
- void before() {
- underTest = new QualityGateCheck(wsClient, analysisMode, reportMetadataHolder, properties);
- logTester.setLevel(Level.DEBUG);
- }
-
- @Test
- void should_pass_if_quality_gate_ok() {
- when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
- when(reportMetadataHolder.getDashboardUrl()).thenReturn("http://dashboard-url.com");
- when(properties.shouldWaitForQualityGate()).thenReturn(true);
- when(properties.qualityGateWaitTimeout()).thenReturn(5);
-
- 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(Level.INFO))
- .contains(
- "Waiting for the analysis report to be processed (max 5s)",
- "QUALITY GATE STATUS: PASSED - View details on http://dashboard-url.com");
- }
-
- @Test
- void should_wait_and_then_pass_if_quality_gate_ok() {
- when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
- when(reportMetadataHolder.getDashboardUrl()).thenReturn("http://dashboard-url.com");
- when(properties.shouldWaitForQualityGate()).thenReturn(true);
- when(properties.qualityGateWaitTimeout()).thenReturn(10);
-
- 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 STATUS: PASSED - View details on http://dashboard-url.com");
- }
-
- @Test
- void should_fail_if_quality_gate_none() {
- when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
- when(reportMetadataHolder.getDashboardUrl()).thenReturn("http://dashboard-url.com");
- when(properties.shouldWaitForQualityGate()).thenReturn(true);
- when(properties.qualityGateWaitTimeout()).thenReturn(5);
-
- MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(TaskStatus.SUCCESS);
- doReturn(ceTaskWsResponse).when(wsClient).call(newGetCeTaskRequest());
-
- MockWsResponse qualityGateResponse = getQualityGateWsResponse(Status.ERROR);
- doReturn(qualityGateResponse).when(wsClient).call(newGetQualityGateRequest());
-
- underTest.start();
-
- assertThatThrownBy(() -> underTest.await())
- .isInstanceOf(MessageException.class)
- .hasMessage("QUALITY GATE STATUS: FAILED - View details on http://dashboard-url.com");
- }
-
- @Test
- void should_fail_if_quality_gate_error() {
- when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
- when(reportMetadataHolder.getDashboardUrl()).thenReturn("http://dashboard-url.com");
- when(properties.shouldWaitForQualityGate()).thenReturn(true);
- when(properties.qualityGateWaitTimeout()).thenReturn(5);
-
- MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(TaskStatus.SUCCESS);
- doReturn(ceTaskWsResponse).when(wsClient).call(newGetCeTaskRequest());
-
- MockWsResponse qualityGateResponse = getQualityGateWsResponse(Status.ERROR);
- doReturn(qualityGateResponse).when(wsClient).call(newGetQualityGateRequest());
-
- underTest.start();
-
- assertThatThrownBy(() -> underTest.await())
- .isInstanceOf(MessageException.class)
- .hasMessage("QUALITY GATE STATUS: FAILED - View details on http://dashboard-url.com");
- }
-
- @Test
- void should_wait_and_then_fail_if_quality_gate_error() {
- when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
- when(reportMetadataHolder.getDashboardUrl()).thenReturn("http://dashboard-url.com");
- when(properties.shouldWaitForQualityGate()).thenReturn(true);
- when(properties.qualityGateWaitTimeout()).thenReturn(10);
-
- 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();
-
- assertThatThrownBy(() -> underTest.await())
- .isInstanceOf(MessageException.class)
- .hasMessage("QUALITY GATE STATUS: FAILED - View details on http://dashboard-url.com");
- }
-
- @Test
- void should_fail_if_quality_gate_timeout_exceeded() {
- when(reportMetadataHolder.getCeTaskId()).thenReturn("task-1234");
- when(reportMetadataHolder.getDashboardUrl()).thenReturn("http://dashboard-url.com");
- when(properties.shouldWaitForQualityGate()).thenReturn(true);
- when(properties.qualityGateWaitTimeout()).thenReturn(1);
-
- MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(TaskStatus.PENDING);
- doReturn(ceTaskWsResponse).when(wsClient).call(newGetCeTaskRequest());
-
- underTest.start();
-
- assertThatThrownBy(() -> underTest.await())
- .isInstanceOf(MessageException.class)
- .hasMessage("Quality Gate check timeout exceeded - View details on http://dashboard-url.com");
- }
-
- @Test
- void should_fail_if_cant_call_ws_for_quality_gate() {
- when(properties.shouldWaitForQualityGate()).thenReturn(true);
- when(properties.qualityGateWaitTimeout()).thenReturn(5);
-
- 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();
-
- assertThatThrownBy(() -> underTest.await())
- .isInstanceOf(MessageException.class)
- .hasMessage("Failed to get Quality Gate status - HTTP code 400: content");
- }
-
- @Test
- void should_fail_if_invalid_response_from_quality_gate_ws() {
- when(properties.shouldWaitForQualityGate()).thenReturn(true);
- when(properties.qualityGateWaitTimeout()).thenReturn(5);
-
- 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();
-
- assertThatThrownBy(() -> underTest.await())
- .isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse response from quality-gate-url");
- }
-
- @Test
- void should_fail_if_cant_call_ws_for_task() {
- when(properties.shouldWaitForQualityGate()).thenReturn(true);
- when(properties.qualityGateWaitTimeout()).thenReturn(5);
-
- when(wsClient.call(newGetCeTaskRequest())).thenThrow(new HttpException("task-url", 400, "content"));
-
- underTest.start();
-
- assertThatThrownBy(() -> underTest.await())
- .isInstanceOf(MessageException.class)
- .hasMessage("Failed to get CE Task status - HTTP code 400: content");
- }
-
- @Test
- void should_fail_if_invalid_response_from_ws_task() {
- when(properties.shouldWaitForQualityGate()).thenReturn(true);
- when(properties.qualityGateWaitTimeout()).thenReturn(5);
-
- MockWsResponse getCeTaskRequest = new MockWsResponse();
- getCeTaskRequest.setRequestUrl("ce-task-url");
- getCeTaskRequest.setContent("blabla");
-
- when(wsClient.call(newGetCeTaskRequest())).thenReturn(getCeTaskRequest);
-
- underTest.start();
-
- assertThatThrownBy(() -> underTest.await())
- .isInstanceOf(IllegalStateException.class)
- .hasMessage("Failed to parse response from ce-task-url");
- }
-
- @ParameterizedTest
- @MethodSource("ceTaskNotSucceededStatuses")
- void should_fail_if_task_not_succeeded(TaskStatus taskStatus) {
- when(properties.shouldWaitForQualityGate()).thenReturn(true);
- when(properties.qualityGateWaitTimeout()).thenReturn(5);
-
- MockWsResponse ceTaskWsResponse = getCeTaskWsResponse(taskStatus);
- when(wsClient.call(newGetCeTaskRequest())).thenReturn(ceTaskWsResponse);
-
- underTest.start();
-
- assertThatThrownBy(() -> underTest.await())
- .isInstanceOf(MessageException.class)
- .hasMessageContaining("CE Task finished abnormally with status: " + taskStatus.name());
- }
-
- 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
- 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
- void should_fail_if_enabled_with_medium_test() {
- when(properties.shouldWaitForQualityGate()).thenReturn(true);
- when(analysisMode.isMediumTest()).thenReturn(true);
-
- underTest.start();
-
- assertThatThrownBy(() -> underTest.await())
- .isInstanceOf(IllegalStateException.class);
- }
-
- 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;
- }
-
- private static Stream<TaskStatus> ceTaskNotSucceededStatuses() {
- return Stream.of(
- 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());
- }
- }
- }
|