]> source.dussan.org Git - sonarqube.git/blob
014198f1e21b40f9fcba1690d0b0faafa31f6524
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2019 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.ce.notification;
21
22 import java.util.Arrays;
23 import java.util.Optional;
24 import java.util.Random;
25 import javax.annotation.Nullable;
26 import org.junit.Before;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.junit.rules.ExpectedException;
30 import org.mockito.ArgumentCaptor;
31 import org.sonar.api.notifications.Notification;
32 import org.sonar.api.utils.System2;
33 import org.sonar.ce.task.CeTask;
34 import org.sonar.ce.task.CeTaskResult;
35 import org.sonar.ce.task.projectanalysis.notification.ReportAnalysisFailureNotification;
36 import org.sonar.ce.task.projectanalysis.notification.ReportAnalysisFailureNotificationBuilder;
37 import org.sonar.ce.task.projectanalysis.notification.ReportAnalysisFailureNotificationSerializer;
38 import org.sonar.db.DbClient;
39 import org.sonar.db.DbTester;
40 import org.sonar.db.RowNotFoundException;
41 import org.sonar.db.ce.CeActivityDto;
42 import org.sonar.db.ce.CeQueueDto;
43 import org.sonar.db.ce.CeTaskTypes;
44 import org.sonar.db.component.ComponentDto;
45 import org.sonar.db.component.ComponentTesting;
46 import org.sonar.db.organization.OrganizationDto;
47 import org.sonar.db.organization.OrganizationTesting;
48 import org.sonar.server.notification.NotificationService;
49
50 import static java.util.Collections.singleton;
51 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
52 import static org.assertj.core.api.Assertions.assertThat;
53 import static org.assertj.core.api.Assertions.fail;
54 import static org.mockito.ArgumentMatchers.any;
55 import static org.mockito.ArgumentMatchers.same;
56 import static org.mockito.Mockito.mock;
57 import static org.mockito.Mockito.verify;
58 import static org.mockito.Mockito.verifyZeroInteractions;
59 import static org.mockito.Mockito.when;
60 import static org.sonar.db.component.ComponentTesting.newDirectory;
61 import static org.sonar.db.component.ComponentTesting.newModuleDto;
62
63 public class ReportAnalysisFailureNotificationExecutionListenerTest {
64   @Rule
65   public DbTester dbTester = DbTester.create(System2.INSTANCE);
66   @Rule
67   public ExpectedException expectedException = ExpectedException.none();
68
69   private final Random random = new Random();
70   private DbClient dbClient = dbTester.getDbClient();
71   private NotificationService notificationService = mock(NotificationService.class);
72   private ReportAnalysisFailureNotificationSerializer serializer = mock(ReportAnalysisFailureNotificationSerializer.class);
73   private System2 system2 = mock(System2.class);
74   private DbClient dbClientMock = mock(DbClient.class);
75   private CeTask ceTaskMock = mock(CeTask.class);
76   private Throwable throwableMock = mock(Throwable.class);
77   private CeTaskResult ceTaskResultMock = mock(CeTaskResult.class);
78   private ReportAnalysisFailureNotificationExecutionListener fullMockedUnderTest = new ReportAnalysisFailureNotificationExecutionListener(
79     notificationService, dbClientMock, serializer, system2);
80   private ReportAnalysisFailureNotificationExecutionListener underTest = new ReportAnalysisFailureNotificationExecutionListener(
81     notificationService, dbClient, serializer, system2);
82
83   @Before
84   public void setUp() {
85
86   }
87
88   @Test
89   public void onStart_has_no_effect() {
90     CeTask mockedCeTask = mock(CeTask.class);
91
92     fullMockedUnderTest.onStart(mockedCeTask);
93
94     verifyZeroInteractions(mockedCeTask, notificationService, dbClientMock, serializer, system2);
95   }
96
97   @Test
98   public void onEnd_has_no_effect_if_status_is_SUCCESS() {
99     fullMockedUnderTest.onEnd(ceTaskMock, CeActivityDto.Status.SUCCESS, ceTaskResultMock, throwableMock);
100
101     verifyZeroInteractions(ceTaskMock, ceTaskResultMock, throwableMock, notificationService, dbClientMock, serializer, system2);
102   }
103
104   @Test
105   public void onEnd_has_no_effect_if_CeTask_type_is_not_report() {
106     when(ceTaskMock.getType()).thenReturn(randomAlphanumeric(12));
107
108     fullMockedUnderTest.onEnd(ceTaskMock, CeActivityDto.Status.FAILED, ceTaskResultMock, throwableMock);
109
110     verifyZeroInteractions(ceTaskResultMock, throwableMock, notificationService, dbClientMock, serializer, system2);
111   }
112
113   @Test
114   public void onEnd_has_no_effect_if_CeTask_has_no_component_uuid() {
115     when(ceTaskMock.getType()).thenReturn(CeTaskTypes.REPORT);
116
117     fullMockedUnderTest.onEnd(ceTaskMock, CeActivityDto.Status.FAILED, ceTaskResultMock, throwableMock);
118
119     verifyZeroInteractions(ceTaskResultMock, throwableMock, notificationService, dbClientMock, serializer, system2);
120   }
121
122   @Test
123   public void onEnd_has_no_effect_if_there_is_no_subscriber_for_ReportAnalysisFailureNotification_type() {
124     String componentUuid = randomAlphanumeric(6);
125     when(ceTaskMock.getType()).thenReturn(CeTaskTypes.REPORT);
126     when(ceTaskMock.getComponent()).thenReturn(Optional.of(new CeTask.Component(componentUuid, null, null)));
127     when(notificationService.hasProjectSubscribersForTypes(componentUuid, singleton(ReportAnalysisFailureNotification.class)))
128       .thenReturn(false);
129
130     fullMockedUnderTest.onEnd(ceTaskMock, CeActivityDto.Status.FAILED, ceTaskResultMock, throwableMock);
131
132     verifyZeroInteractions(ceTaskResultMock, throwableMock, dbClientMock, serializer, system2);
133   }
134
135   @Test
136   public void onEnd_fails_with_RowNotFoundException_if_component_does_not_exist_in_DB() {
137     String componentUuid = randomAlphanumeric(6);
138     when(ceTaskMock.getType()).thenReturn(CeTaskTypes.REPORT);
139     when(ceTaskMock.getComponent()).thenReturn(Optional.of(new CeTask.Component(componentUuid, null, null)));
140     when(notificationService.hasProjectSubscribersForTypes(componentUuid, singleton(ReportAnalysisFailureNotification.class)))
141       .thenReturn(true);
142
143     expectedException.expect(RowNotFoundException.class);
144     expectedException.expectMessage("Component with uuid '" + componentUuid + "' not found");
145
146     underTest.onEnd(ceTaskMock, CeActivityDto.Status.FAILED, ceTaskResultMock, throwableMock);
147   }
148
149   @Test
150   public void onEnd_fails_with_IAE_if_component_is_not_a_project() {
151     when(ceTaskMock.getType()).thenReturn(CeTaskTypes.REPORT);
152     OrganizationDto organization = OrganizationTesting.newOrganizationDto();
153     ComponentDto project = dbTester.components().insertPrivateProject();
154     ComponentDto module = dbTester.components().insertComponent(newModuleDto(project));
155     ComponentDto directory = dbTester.components().insertComponent(newDirectory(module, randomAlphanumeric(12)));
156     ComponentDto file = dbTester.components().insertComponent(ComponentTesting.newFileDto(project));
157     ComponentDto view = dbTester.components().insertComponent(ComponentTesting.newView(organization));
158     ComponentDto subView = dbTester.components().insertComponent(ComponentTesting.newSubView(view));
159     ComponentDto projectCopy = dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project, subView));
160     ComponentDto application = dbTester.components().insertComponent(ComponentTesting.newApplication(organization));
161
162     Arrays.asList(module, directory, file, view, subView, projectCopy, application)
163       .forEach(component -> {
164         try {
165           when(ceTaskMock.getComponent()).thenReturn(Optional.of(new CeTask.Component(component.uuid(), null, null)));
166           when(notificationService.hasProjectSubscribersForTypes(component.uuid(), singleton(ReportAnalysisFailureNotification.class)))
167             .thenReturn(true);
168
169           underTest.onEnd(ceTaskMock, CeActivityDto.Status.FAILED, ceTaskResultMock, throwableMock);
170
171           fail("An IllegalArgumentException should have been thrown for component " + component);
172         } catch (IllegalArgumentException e) {
173           assertThat(e.getMessage()).isEqualTo(String.format(
174             "Component %s must be a project (scope=%s, qualifier=%s)", component.uuid(), component.scope(), component.qualifier()));
175         }
176       });
177   }
178
179   @Test
180   public void onEnd_fails_with_RowNotFoundException_if_activity_for_task_does_not_exist_in_DB() {
181     String componentUuid = randomAlphanumeric(6);
182     String taskUuid = randomAlphanumeric(6);
183     when(ceTaskMock.getType()).thenReturn(CeTaskTypes.REPORT);
184     when(ceTaskMock.getUuid()).thenReturn(taskUuid);
185     when(ceTaskMock.getComponent()).thenReturn(Optional.of(new CeTask.Component(componentUuid, null, null)));
186     when(notificationService.hasProjectSubscribersForTypes(componentUuid, singleton(ReportAnalysisFailureNotification.class)))
187       .thenReturn(true);
188     dbTester.components().insertPrivateProject(s -> s.setUuid(componentUuid));
189
190     expectedException.expect(RowNotFoundException.class);
191     expectedException.expectMessage("CeActivity with uuid '" + taskUuid + "' not found");
192
193     underTest.onEnd(ceTaskMock, CeActivityDto.Status.FAILED, ceTaskResultMock, throwableMock);
194   }
195
196   @Test
197   public void onEnd_creates_notification_with_data_from_activity_and_project_and_deliver_it() {
198     String taskUuid = randomAlphanumeric(12);
199     int createdAt = random.nextInt(999_999);
200     long executedAt = random.nextInt(999_999);
201     ComponentDto project = initMocksToPassConditions(taskUuid, createdAt, executedAt);
202     Notification notificationMock = mockSerializer();
203
204     underTest.onEnd(ceTaskMock, CeActivityDto.Status.FAILED, ceTaskResultMock, throwableMock);
205
206     ArgumentCaptor<ReportAnalysisFailureNotificationBuilder> notificationCaptor = verifyAndCaptureSerializedNotification();
207     verify(notificationService).deliver(same(notificationMock));
208
209     ReportAnalysisFailureNotificationBuilder reportAnalysisFailureNotificationBuilder = notificationCaptor.getValue();
210
211     ReportAnalysisFailureNotificationBuilder.Project notificationProject = reportAnalysisFailureNotificationBuilder.getProject();
212     assertThat(notificationProject.getName()).isEqualTo(project.name());
213     assertThat(notificationProject.getKey()).isEqualTo(project.getKey());
214     assertThat(notificationProject.getUuid()).isEqualTo(project.uuid());
215     assertThat(notificationProject.getBranchName()).isEqualTo(project.getBranch());
216     ReportAnalysisFailureNotificationBuilder.Task notificationTask = reportAnalysisFailureNotificationBuilder.getTask();
217     assertThat(notificationTask.getUuid()).isEqualTo(taskUuid);
218     assertThat(notificationTask.getCreatedAt()).isEqualTo(createdAt);
219     assertThat(notificationTask.getFailedAt()).isEqualTo(executedAt);
220   }
221
222   @Test
223   public void onEnd_creates_notification_with_error_message_from_Throwable_argument_message() {
224     initMocksToPassConditions(randomAlphanumeric(12), random.nextInt(999_999), (long) random.nextInt(999_999));
225     String message = randomAlphanumeric(66);
226     when(throwableMock.getMessage()).thenReturn(message);
227
228     underTest.onEnd(ceTaskMock, CeActivityDto.Status.FAILED, ceTaskResultMock, throwableMock);
229
230     ArgumentCaptor<ReportAnalysisFailureNotificationBuilder> notificationCaptor = verifyAndCaptureSerializedNotification();
231
232     ReportAnalysisFailureNotificationBuilder reportAnalysisFailureNotificationBuilder = notificationCaptor.getValue();
233     assertThat(reportAnalysisFailureNotificationBuilder.getErrorMessage()).isEqualTo(message);
234   }
235
236   @Test
237   public void onEnd_creates_notification_with_null_error_message_if_Throwable_is_null() {
238     String taskUuid = randomAlphanumeric(12);
239     initMocksToPassConditions(taskUuid, random.nextInt(999_999), (long) random.nextInt(999_999));
240     Notification notificationMock = mockSerializer();
241
242     underTest.onEnd(ceTaskMock, CeActivityDto.Status.FAILED, ceTaskResultMock, null);
243
244     verify(notificationService).deliver(same(notificationMock));
245     ArgumentCaptor<ReportAnalysisFailureNotificationBuilder> notificationCaptor = verifyAndCaptureSerializedNotification();
246
247     ReportAnalysisFailureNotificationBuilder reportAnalysisFailureNotificationBuilder = notificationCaptor.getValue();
248     assertThat(reportAnalysisFailureNotificationBuilder.getErrorMessage()).isNull();
249   }
250
251   @Test
252   public void onEnd_ignores_null_CeTaskResult_argument() {
253     String taskUuid = randomAlphanumeric(12);
254     initMocksToPassConditions(taskUuid, random.nextInt(999_999), (long) random.nextInt(999_999));
255     Notification notificationMock = mockSerializer();
256
257     underTest.onEnd(ceTaskMock, CeActivityDto.Status.FAILED, null, null);
258
259     verify(notificationService).deliver(same(notificationMock));
260   }
261
262   @Test
263   public void onEnd_ignores_CeTaskResult_argument() {
264     String taskUuid = randomAlphanumeric(12);
265     initMocksToPassConditions(taskUuid, random.nextInt(999_999), (long) random.nextInt(999_999));
266     Notification notificationMock = mockSerializer();
267
268     underTest.onEnd(ceTaskMock, CeActivityDto.Status.FAILED, ceTaskResultMock, null);
269
270     verify(notificationService).deliver(same(notificationMock));
271     verifyZeroInteractions(ceTaskResultMock);
272   }
273
274   @Test
275   public void onEnd_uses_system_data_as_failedAt_if_task_has_no_executedAt() {
276     String taskUuid = randomAlphanumeric(12);
277     initMocksToPassConditions(taskUuid, random.nextInt(999_999), null);
278     long now = random.nextInt(999_999);
279     when(system2.now()).thenReturn(now);
280     Notification notificationMock = mockSerializer();
281
282     underTest.onEnd(ceTaskMock, CeActivityDto.Status.FAILED, ceTaskResultMock, null);
283
284     verify(notificationService).deliver(same(notificationMock));
285     ArgumentCaptor<ReportAnalysisFailureNotificationBuilder> notificationCaptor = verifyAndCaptureSerializedNotification();
286     assertThat(notificationCaptor.getValue().getTask().getFailedAt()).isEqualTo(now);
287   }
288
289   private ReportAnalysisFailureNotification mockSerializer() {
290     ReportAnalysisFailureNotification notificationMock = mock(ReportAnalysisFailureNotification.class);
291     when(serializer.toNotification(any(ReportAnalysisFailureNotificationBuilder.class))).thenReturn(notificationMock);
292     return notificationMock;
293   }
294
295   private ComponentDto initMocksToPassConditions(String taskUuid, int createdAt, @Nullable Long executedAt) {
296     ComponentDto project = random.nextBoolean() ? dbTester.components().insertPrivateProject() : dbTester.components().insertPublicProject();
297     when(ceTaskMock.getType()).thenReturn(CeTaskTypes.REPORT);
298     when(ceTaskMock.getComponent()).thenReturn(Optional.of(new CeTask.Component(project.uuid(), null, null)));
299     when(ceTaskMock.getUuid()).thenReturn(taskUuid);
300     when(notificationService.hasProjectSubscribersForTypes(project.uuid(), singleton(ReportAnalysisFailureNotification.class)))
301       .thenReturn(true);
302     insertActivityDto(taskUuid, createdAt, executedAt, project);
303     return project;
304   }
305
306   private void insertActivityDto(String taskUuid, int createdAt, @Nullable Long executedAt, ComponentDto project) {
307     dbClient.ceActivityDao().insert(dbTester.getSession(), new CeActivityDto(new CeQueueDto()
308       .setUuid(taskUuid)
309       .setTaskType(CeTaskTypes.REPORT)
310       .setComponentUuid(project.uuid())
311       .setCreatedAt(createdAt))
312         .setExecutedAt(executedAt)
313         .setStatus(CeActivityDto.Status.FAILED));
314     dbTester.getSession().commit();
315   }
316
317   private ArgumentCaptor<ReportAnalysisFailureNotificationBuilder> verifyAndCaptureSerializedNotification() {
318     ArgumentCaptor<ReportAnalysisFailureNotificationBuilder> notificationCaptor = ArgumentCaptor.forClass(ReportAnalysisFailureNotificationBuilder.class);
319     verify(serializer).toNotification(notificationCaptor.capture());
320     return notificationCaptor;
321   }
322 }