3 * Copyright (C) 2009-2024 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.lang3.RandomStringUtils.randomAlphabetic;
51 import static org.assertj.core.api.Assertions.assertThat;
52 import static org.mockito.Mockito.mock;
53 import static org.mockito.Mockito.reset;
54 import static org.mockito.Mockito.verify;
55 import static org.mockito.Mockito.verifyNoInteractions;
56 import static org.mockito.Mockito.verifyNoMoreInteractions;
57 import static org.mockito.Mockito.when;
58 import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
59 import static org.sonar.ce.task.projectanalysis.measure.Measure.Level.ERROR;
60 import static org.sonar.ce.task.projectanalysis.measure.Measure.Level.OK;
61 import static org.sonar.db.component.BranchDto.DEFAULT_MAIN_BRANCH_NAME;
63 public class QualityGateEventsStepTest {
64 private static final String PROJECT_VERSION = randomAlphabetic(19);
65 private static final ReportComponent PROJECT_COMPONENT = ReportComponent.builder(Component.Type.PROJECT, 1)
68 .setProjectVersion(PROJECT_VERSION)
69 .setBuildString("V1.9")
70 .setScmRevisionId("456def")
71 .addChildren(ReportComponent.builder(Component.Type.DIRECTORY, 2).build())
73 private static final String INVALID_ALERT_STATUS = "trololo";
74 private static final String ALERT_TEXT = "alert text";
75 private static final QualityGateStatus OK_QUALITY_GATE_STATUS = new QualityGateStatus(OK, ALERT_TEXT);
76 private static final QualityGateStatus ERROR_QUALITY_GATE_STATUS = new QualityGateStatus(ERROR, ALERT_TEXT);
79 public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
82 public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
84 private ArgumentCaptor<Event> eventArgumentCaptor = ArgumentCaptor.forClass(Event.class);
85 private ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(Notification.class);
87 private Metric alertStatusMetric = mock(Metric.class);
89 private MetricRepository metricRepository = mock(MetricRepository.class);
90 private MeasureRepository measureRepository = mock(MeasureRepository.class);
91 private EventRepository eventRepository = mock(EventRepository.class);
92 private NotificationService notificationService = mock(NotificationService.class);
93 private QualityGateEventsStep underTest = new QualityGateEventsStep(treeRootHolder, metricRepository, measureRepository, eventRepository, notificationService,
94 analysisMetadataHolder);
98 when(metricRepository.getByKey(ALERT_STATUS_KEY)).thenReturn(alertStatusMetric);
99 analysisMetadataHolder
100 .setProject(new Project(PROJECT_COMPONENT.getUuid(), PROJECT_COMPONENT.getKey(), PROJECT_COMPONENT.getName(), PROJECT_COMPONENT.getDescription(), emptyList()));
101 analysisMetadataHolder.setBranch(mock(Branch.class));
102 treeRootHolder.setRoot(PROJECT_COMPONENT);
106 public void no_event_if_no_raw_ALERT_STATUS_measure() {
107 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(Optional.empty());
109 underTest.execute(new TestComputationStepContext());
111 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric);
112 verifyNoMoreInteractions(measureRepository, eventRepository);
116 public void no_event_created_if_raw_ALERT_STATUS_measure_is_null() {
117 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(Measure.newMeasureBuilder().createNoValue()));
119 underTest.execute(new TestComputationStepContext());
121 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric);
122 verifyNoMoreInteractions(measureRepository, eventRepository);
125 private static Optional<Measure> of(Measure measure) {
126 return Optional.of(measure);
130 public void no_event_created_if_raw_ALERT_STATUS_measure_is_unsupported_value() {
131 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(Measure.newMeasureBuilder().create(INVALID_ALERT_STATUS)));
133 underTest.execute(new TestComputationStepContext());
135 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric);
136 verifyNoMoreInteractions(measureRepository, eventRepository);
140 public void no_event_created_if_no_base_ALERT_STATUS_and_raw_is_OK() {
141 QualityGateStatus someQGStatus = new QualityGateStatus(Measure.Level.OK);
143 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(Measure.newMeasureBuilder().setQualityGateStatus(someQGStatus).createNoValue()));
144 when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(Measure.newMeasureBuilder().createNoValue()));
146 underTest.execute(new TestComputationStepContext());
148 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric);
149 verify(measureRepository).getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric);
150 verifyNoMoreInteractions(measureRepository, eventRepository);
154 public void event_created_if_base_ALERT_STATUS_has_no_alertStatus_and_raw_is_ERROR() {
155 verify_event_created_if_no_base_ALERT_STATUS_measure(ERROR, "Failed");
159 public void event_created_if_base_ALERT_STATUS_has_invalid_alertStatus_and_raw_is_ERROR() {
160 verify_event_created_if_no_base_ALERT_STATUS_measure(ERROR, "Failed");
163 private void verify_event_created_if_no_base_ALERT_STATUS_measure(Measure.Level rawAlterStatus, String expectedLabel) {
164 QualityGateStatus someQGStatus = new QualityGateStatus(rawAlterStatus, ALERT_TEXT);
166 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(Measure.newMeasureBuilder().setQualityGateStatus(someQGStatus).createNoValue()));
167 when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(Measure.newMeasureBuilder().createNoValue()));
169 underTest.execute(new TestComputationStepContext());
171 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric);
172 verify(measureRepository).getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric);
173 verify(eventRepository).add(eventArgumentCaptor.capture());
174 verifyNoMoreInteractions(measureRepository, eventRepository);
176 Event event = eventArgumentCaptor.getValue();
177 assertThat(event.getCategory()).isEqualTo(Event.Category.ALERT);
178 assertThat(event.getName()).isEqualTo(expectedLabel);
179 assertThat(event.getDescription()).isEqualTo(ALERT_TEXT);
180 assertThat(event.getData()).isNull();
182 ArgumentCaptor<Collection> collectionCaptor = ArgumentCaptor.forClass(Collection.class);
183 verify(notificationService).deliverEmails(collectionCaptor.capture());
184 verify(notificationService).deliver(notificationArgumentCaptor.capture());
185 Notification notification = notificationArgumentCaptor.getValue();
186 assertThat(collectionCaptor.getValue()).hasSize(1);
187 assertThat(collectionCaptor.getValue().iterator().next()).isSameAs(notification);
188 assertThat(notification).isInstanceOf(QGChangeNotification.class);
189 assertThat(notification.getType()).isEqualTo("alerts");
190 assertThat(notification.getFieldValue("projectKey")).isEqualTo(PROJECT_COMPONENT.getKey());
191 assertThat(notification.getFieldValue("projectName")).isEqualTo(PROJECT_COMPONENT.getName());
192 assertThat(notification.getFieldValue("projectVersion")).isEqualTo(PROJECT_COMPONENT.getProjectAttributes().getProjectVersion());
193 assertThat(notification.getFieldValue("branch")).isNull();
194 assertThat(notification.getFieldValue("alertLevel")).isEqualTo(rawAlterStatus.name());
195 assertThat(notification.getFieldValue("alertName")).isEqualTo(expectedLabel);
199 public void no_event_created_if_base_ALERT_STATUS_measure_but_status_is_the_same() {
200 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric))
201 .thenReturn(of(Measure.newMeasureBuilder().setQualityGateStatus(OK_QUALITY_GATE_STATUS).createNoValue()));
202 when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric))
203 .thenReturn(of(Measure.newMeasureBuilder().setQualityGateStatus(OK_QUALITY_GATE_STATUS).createNoValue()));
205 underTest.execute(new TestComputationStepContext());
207 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric);
208 verify(measureRepository).getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric);
209 verifyNoMoreInteractions(measureRepository, eventRepository);
213 public void event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed() {
214 verify_event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed(OK, ERROR_QUALITY_GATE_STATUS, "Failed");
215 verify_event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed(ERROR, OK_QUALITY_GATE_STATUS, "Passed");
218 private void verify_event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed(Measure.Level previousAlertStatus,
219 QualityGateStatus newQualityGateStatus, String expectedLabel) {
220 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric))
221 .thenReturn(of(Measure.newMeasureBuilder().setQualityGateStatus(newQualityGateStatus).createNoValue()));
222 when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(
223 of(Measure.newMeasureBuilder().setQualityGateStatus(new QualityGateStatus(previousAlertStatus)).createNoValue()));
225 underTest.execute(new TestComputationStepContext());
227 verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric);
228 verify(measureRepository).getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric);
229 verify(eventRepository).add(eventArgumentCaptor.capture());
230 verifyNoMoreInteractions(measureRepository, eventRepository);
232 Event event = eventArgumentCaptor.getValue();
233 assertThat(event.getCategory()).isEqualTo(Event.Category.ALERT);
234 assertThat(event.getName()).isEqualTo(expectedLabel);
235 assertThat(event.getDescription()).isEqualTo(ALERT_TEXT);
236 assertThat(event.getData()).isNull();
238 verify(notificationService).deliver(notificationArgumentCaptor.capture());
239 Notification notification = notificationArgumentCaptor.getValue();
240 assertThat(notification.getType()).isEqualTo("alerts");
241 assertThat(notification.getFieldValue("projectKey")).isEqualTo(PROJECT_COMPONENT.getKey());
242 assertThat(notification.getFieldValue("projectName")).isEqualTo(PROJECT_COMPONENT.getName());
243 assertThat(notification.getFieldValue("projectVersion")).isEqualTo(PROJECT_COMPONENT.getProjectAttributes().getProjectVersion());
244 assertThat(notification.getFieldValue("branch")).isNull();
245 assertThat(notification.getFieldValue("alertLevel")).isEqualTo(newQualityGateStatus.getStatus().name());
246 assertThat(notification.getFieldValue("alertName")).isEqualTo(expectedLabel);
248 reset(measureRepository, eventRepository, notificationService);
252 public void verify_branch_name_is_not_set_in_notification_when_main() {
253 analysisMetadataHolder.setBranch(new DefaultBranchImpl(DEFAULT_MAIN_BRANCH_NAME));
255 when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric))
256 .thenReturn(of(Measure.newMeasureBuilder().setQualityGateStatus(OK_QUALITY_GATE_STATUS).createNoValue()));
257 when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(
258 of(Measure.newMeasureBuilder().setQualityGateStatus(new QualityGateStatus(ERROR)).createNoValue()));
260 underTest.execute(new TestComputationStepContext());
262 verify(notificationService).deliver(notificationArgumentCaptor.capture());
263 Notification notification = notificationArgumentCaptor.getValue();
264 assertThat(notification.getType()).isEqualTo("alerts");
265 assertThat(notification.getFieldValue("projectKey")).isEqualTo(PROJECT_COMPONENT.getKey());
266 assertThat(notification.getFieldValue("projectName")).isEqualTo(PROJECT_COMPONENT.getName());
267 assertThat(notification.getFieldValue("projectVersion")).isEqualTo(PROJECT_COMPONENT.getProjectAttributes().getProjectVersion());
268 assertThat(notification.getFieldValue("branch")).isNull();
270 reset(measureRepository, eventRepository, notificationService);
274 public void no_alert_on_pull_request_branches() {
275 Branch pr = mock(Branch.class);
276 when(pr.getType()).thenReturn(BranchType.PULL_REQUEST);
277 analysisMetadataHolder.setBranch(pr);
278 TreeRootHolder treeRootHolder = mock(TreeRootHolder.class);
279 MetricRepository metricRepository = mock(MetricRepository.class);
280 MeasureRepository measureRepository = mock(MeasureRepository.class);
281 EventRepository eventRepository = mock(EventRepository.class);
282 NotificationService notificationService = mock(NotificationService.class);
284 QualityGateEventsStep underTest = new QualityGateEventsStep(treeRootHolder, metricRepository, measureRepository,
285 eventRepository, notificationService, analysisMetadataHolder);
287 underTest.execute(new TestComputationStepContext());
289 verifyNoInteractions(treeRootHolder, metricRepository, measureRepository, eventRepository, notificationService);