3 * Copyright (C) 2009-2022 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.ce.task.projectanalysis.step;
22 import java.util.Collection;
23 import java.util.Optional;
24 import org.junit.Before;
25 import org.junit.Rule;
26 import org.junit.Test;
27 import org.mockito.ArgumentCaptor;
28 import org.sonar.api.notifications.Notification;
29 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
30 import org.sonar.ce.task.projectanalysis.analysis.Branch;
31 import org.sonar.ce.task.projectanalysis.component.Component;
32 import org.sonar.ce.task.projectanalysis.component.DefaultBranchImpl;
33 import org.sonar.ce.task.projectanalysis.component.ReportComponent;
34 import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
35 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
36 import org.sonar.ce.task.projectanalysis.event.Event;
37 import org.sonar.ce.task.projectanalysis.event.EventRepository;
38 import org.sonar.ce.task.projectanalysis.measure.Measure;
39 import org.sonar.ce.task.projectanalysis.measure.MeasureRepository;
40 import org.sonar.ce.task.projectanalysis.measure.QualityGateStatus;
41 import org.sonar.ce.task.projectanalysis.metric.Metric;
42 import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
43 import org.sonar.ce.task.step.TestComputationStepContext;
44 import org.sonar.db.component.BranchType;
45 import org.sonar.server.notification.NotificationService;
46 import org.sonar.server.project.Project;
47 import org.sonar.server.qualitygate.notification.QGChangeNotification;
49 import static java.util.Collections.emptyList;
50 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
51 import static org.assertj.core.api.Assertions.assertThat;
52 import static org.mockito.ArgumentMatchers.eq;
53 import static org.mockito.Mockito.mock;
54 import static org.mockito.Mockito.reset;
55 import static org.mockito.Mockito.verify;
56 import static org.mockito.Mockito.verifyNoInteractions;
57 import static org.mockito.Mockito.verifyNoMoreInteractions;
58 import static org.mockito.Mockito.when;
59 import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
60 import static org.sonar.ce.task.projectanalysis.measure.Measure.Level.ERROR;
61 import static org.sonar.ce.task.projectanalysis.measure.Measure.Level.OK;
62 import static org.sonar.db.component.BranchDto.DEFAULT_PROJECT_MAIN_BRANCH_NAME;
64 public class QualityGateEventsStepTest {
65 private static final String PROJECT_VERSION = randomAlphabetic(19);
66 private static final ReportComponent PROJECT_COMPONENT = ReportComponent.builder(Component.Type.PROJECT, 1)
69 .setProjectVersion(PROJECT_VERSION)
70 .setBuildString("V1.9")
71 .setScmRevisionId("456def")
72 .addChildren(ReportComponent.builder(Component.Type.DIRECTORY, 2).build())
74 private static final String INVALID_ALERT_STATUS = "trololo";
75 private static final String ALERT_TEXT = "alert text";
76 private static final QualityGateStatus OK_QUALITY_GATE_STATUS = new QualityGateStatus(OK, ALERT_TEXT);
77 private static final QualityGateStatus ERROR_QUALITY_GATE_STATUS = new QualityGateStatus(ERROR, ALERT_TEXT);
80 public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
83 public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
85 private ArgumentCaptor<Event> eventArgumentCaptor = ArgumentCaptor.forClass(Event.class);
86 private ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(Notification.class);
88 private Metric alertStatusMetric = mock(Metric.class);
90 private MetricRepository metricRepository = mock(MetricRepository.class);
91 private MeasureRepository measureRepository = mock(MeasureRepository.class);
92 private EventRepository eventRepository = mock(EventRepository.class);
93 private NotificationService notificationService = mock(NotificationService.class);
94 private QualityGateEventsStep underTest = new QualityGateEventsStep(treeRootHolder, metricRepository, measureRepository, eventRepository, notificationService,
95 analysisMetadataHolder);
99 when(metricRepository.getByKey(ALERT_STATUS_KEY)).thenReturn(alertStatusMetric);
100 analysisMetadataHolder
101 .setProject(new Project(PROJECT_COMPONENT.getUuid(), PROJECT_COMPONENT.getKey(), PROJECT_COMPONENT.getName(), PROJECT_COMPONENT.getDescription(), emptyList()));
102 analysisMetadataHolder.setBranch(mock(Branch.class));
103 treeRootHolder.setRoot(PROJECT_COMPONENT);
107 public void no_event_if_no_raw_ALERT_STATUS_measure() {
108 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(Optional.empty());
110 underTest.execute(new TestComputationStepContext());
112 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric);
113 verifyNoMoreInteractions(measureRepository, eventRepository);
117 public void no_event_created_if_raw_ALERT_STATUS_measure_is_null() {
118 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(Measure.newMeasureBuilder().createNoValue()));
120 underTest.execute(new TestComputationStepContext());
122 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric);
123 verifyNoMoreInteractions(measureRepository, eventRepository);
126 private static Optional<Measure> of(Measure measure) {
127 return Optional.of(measure);
131 public void no_event_created_if_raw_ALERT_STATUS_measure_is_unsupported_value() {
132 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(Measure.newMeasureBuilder().create(INVALID_ALERT_STATUS)));
134 underTest.execute(new TestComputationStepContext());
136 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric);
137 verifyNoMoreInteractions(measureRepository, eventRepository);
141 public void no_event_created_if_no_base_ALERT_STATUS_and_raw_is_OK() {
142 QualityGateStatus someQGStatus = new QualityGateStatus(Measure.Level.OK);
144 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(Measure.newMeasureBuilder().setQualityGateStatus(someQGStatus).createNoValue()));
145 when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(Measure.newMeasureBuilder().createNoValue()));
147 underTest.execute(new TestComputationStepContext());
149 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric);
150 verify(measureRepository).getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric);
151 verifyNoMoreInteractions(measureRepository, eventRepository);
155 public void event_created_if_base_ALERT_STATUS_has_no_alertStatus_and_raw_is_ERROR() {
156 verify_event_created_if_no_base_ALERT_STATUS_measure(ERROR, "Failed");
160 public void event_created_if_base_ALERT_STATUS_has_invalid_alertStatus_and_raw_is_ERROR() {
161 verify_event_created_if_no_base_ALERT_STATUS_measure(ERROR, "Failed");
164 private void verify_event_created_if_no_base_ALERT_STATUS_measure(Measure.Level rawAlterStatus, String expectedLabel) {
165 QualityGateStatus someQGStatus = new QualityGateStatus(rawAlterStatus, ALERT_TEXT);
167 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(Measure.newMeasureBuilder().setQualityGateStatus(someQGStatus).createNoValue()));
168 when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(Measure.newMeasureBuilder().createNoValue()));
170 underTest.execute(new TestComputationStepContext());
172 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric);
173 verify(measureRepository).getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric);
174 verify(eventRepository).add(eq(PROJECT_COMPONENT), eventArgumentCaptor.capture());
175 verifyNoMoreInteractions(measureRepository, eventRepository);
177 Event event = eventArgumentCaptor.getValue();
178 assertThat(event.getCategory()).isEqualTo(Event.Category.ALERT);
179 assertThat(event.getName()).isEqualTo(expectedLabel);
180 assertThat(event.getDescription()).isEqualTo(ALERT_TEXT);
181 assertThat(event.getData()).isNull();
183 ArgumentCaptor<Collection> collectionCaptor = ArgumentCaptor.forClass(Collection.class);
184 verify(notificationService).deliverEmails(collectionCaptor.capture());
185 verify(notificationService).deliver(notificationArgumentCaptor.capture());
186 Notification notification = notificationArgumentCaptor.getValue();
187 assertThat(collectionCaptor.getValue()).hasSize(1);
188 assertThat(collectionCaptor.getValue().iterator().next()).isSameAs(notification);
189 assertThat(notification).isInstanceOf(QGChangeNotification.class);
190 assertThat(notification.getType()).isEqualTo("alerts");
191 assertThat(notification.getFieldValue("projectKey")).isEqualTo(PROJECT_COMPONENT.getKey());
192 assertThat(notification.getFieldValue("projectName")).isEqualTo(PROJECT_COMPONENT.getName());
193 assertThat(notification.getFieldValue("projectVersion")).isEqualTo(PROJECT_COMPONENT.getProjectAttributes().getProjectVersion());
194 assertThat(notification.getFieldValue("branch")).isNull();
195 assertThat(notification.getFieldValue("alertLevel")).isEqualTo(rawAlterStatus.name());
196 assertThat(notification.getFieldValue("alertName")).isEqualTo(expectedLabel);
200 public void no_event_created_if_base_ALERT_STATUS_measure_but_status_is_the_same() {
201 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric))
202 .thenReturn(of(Measure.newMeasureBuilder().setQualityGateStatus(OK_QUALITY_GATE_STATUS).createNoValue()));
203 when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric))
204 .thenReturn(of(Measure.newMeasureBuilder().setQualityGateStatus(OK_QUALITY_GATE_STATUS).createNoValue()));
206 underTest.execute(new TestComputationStepContext());
208 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric);
209 verify(measureRepository).getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric);
210 verifyNoMoreInteractions(measureRepository, eventRepository);
214 public void event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed() {
215 verify_event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed(OK, ERROR_QUALITY_GATE_STATUS, "Failed");
216 verify_event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed(ERROR, OK_QUALITY_GATE_STATUS, "Passed");
219 private void verify_event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed(Measure.Level previousAlertStatus,
220 QualityGateStatus newQualityGateStatus, String expectedLabel) {
221 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric))
222 .thenReturn(of(Measure.newMeasureBuilder().setQualityGateStatus(newQualityGateStatus).createNoValue()));
223 when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(
224 of(Measure.newMeasureBuilder().setQualityGateStatus(new QualityGateStatus(previousAlertStatus)).createNoValue()));
226 underTest.execute(new TestComputationStepContext());
228 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric);
229 verify(measureRepository).getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric);
230 verify(eventRepository).add(eq(PROJECT_COMPONENT), eventArgumentCaptor.capture());
231 verifyNoMoreInteractions(measureRepository, eventRepository);
233 Event event = eventArgumentCaptor.getValue();
234 assertThat(event.getCategory()).isEqualTo(Event.Category.ALERT);
235 assertThat(event.getName()).isEqualTo(expectedLabel);
236 assertThat(event.getDescription()).isEqualTo(ALERT_TEXT);
237 assertThat(event.getData()).isNull();
239 verify(notificationService).deliver(notificationArgumentCaptor.capture());
240 Notification notification = notificationArgumentCaptor.getValue();
241 assertThat(notification.getType()).isEqualTo("alerts");
242 assertThat(notification.getFieldValue("projectKey")).isEqualTo(PROJECT_COMPONENT.getKey());
243 assertThat(notification.getFieldValue("projectName")).isEqualTo(PROJECT_COMPONENT.getName());
244 assertThat(notification.getFieldValue("projectVersion")).isEqualTo(PROJECT_COMPONENT.getProjectAttributes().getProjectVersion());
245 assertThat(notification.getFieldValue("branch")).isNull();
246 assertThat(notification.getFieldValue("alertLevel")).isEqualTo(newQualityGateStatus.getStatus().name());
247 assertThat(notification.getFieldValue("alertName")).isEqualTo(expectedLabel);
249 reset(measureRepository, eventRepository, notificationService);
253 public void verify_branch_name_is_not_set_in_notification_when_main() {
254 analysisMetadataHolder.setBranch(new DefaultBranchImpl(DEFAULT_PROJECT_MAIN_BRANCH_NAME));
256 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric))
257 .thenReturn(of(Measure.newMeasureBuilder().setQualityGateStatus(OK_QUALITY_GATE_STATUS).createNoValue()));
258 when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(
259 of(Measure.newMeasureBuilder().setQualityGateStatus(new QualityGateStatus(ERROR)).createNoValue()));
261 underTest.execute(new TestComputationStepContext());
263 verify(notificationService).deliver(notificationArgumentCaptor.capture());
264 Notification notification = notificationArgumentCaptor.getValue();
265 assertThat(notification.getType()).isEqualTo("alerts");
266 assertThat(notification.getFieldValue("projectKey")).isEqualTo(PROJECT_COMPONENT.getKey());
267 assertThat(notification.getFieldValue("projectName")).isEqualTo(PROJECT_COMPONENT.getName());
268 assertThat(notification.getFieldValue("projectVersion")).isEqualTo(PROJECT_COMPONENT.getProjectAttributes().getProjectVersion());
269 assertThat(notification.getFieldValue("branch")).isNull();
271 reset(measureRepository, eventRepository, notificationService);
275 public void no_alert_on_pull_request_branches() {
276 Branch pr = mock(Branch.class);
277 when(pr.getType()).thenReturn(BranchType.PULL_REQUEST);
278 analysisMetadataHolder.setBranch(pr);
279 TreeRootHolder treeRootHolder = mock(TreeRootHolder.class);
280 MetricRepository metricRepository = mock(MetricRepository.class);
281 MeasureRepository measureRepository = mock(MeasureRepository.class);
282 EventRepository eventRepository = mock(EventRepository.class);
283 NotificationService notificationService = mock(NotificationService.class);
285 QualityGateEventsStep underTest = new QualityGateEventsStep(treeRootHolder, metricRepository, measureRepository,
286 eventRepository, notificationService, analysisMetadataHolder);
288 underTest.execute(new TestComputationStepContext());
290 verifyNoInteractions(treeRootHolder, metricRepository, measureRepository, eventRepository, notificationService);