]> source.dussan.org Git - sonarqube.git/blob
b22f4290854022a45f624b1730f5a482303c983d
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 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.server.notification;
21
22 import com.google.common.collect.ImmutableSet;
23 import java.io.InvalidClassException;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.Random;
28 import java.util.Set;
29 import java.util.stream.Collectors;
30 import java.util.stream.IntStream;
31 import org.junit.Before;
32 import org.junit.Test;
33 import org.mockito.InOrder;
34 import org.sonar.api.notifications.Notification;
35 import org.sonar.api.notifications.NotificationChannel;
36 import org.sonar.api.utils.System2;
37 import org.sonar.db.DbClient;
38 import org.sonar.db.DbSession;
39 import org.sonar.db.EmailSubscriberDto;
40 import org.sonar.db.notification.NotificationQueueDao;
41 import org.sonar.db.notification.NotificationQueueDto;
42 import org.sonar.db.permission.AuthorizationDao;
43 import org.sonar.db.property.PropertiesDao;
44 import org.sonar.server.notification.NotificationManager.EmailRecipient;
45 import org.sonar.server.notification.NotificationManager.SubscriberPermissionsOnProject;
46
47 import static com.google.common.collect.Sets.newHashSet;
48 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
49 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
50 import static org.assertj.core.api.Assertions.assertThat;
51 import static org.assertj.core.api.Assertions.assertThatThrownBy;
52 import static org.mockito.ArgumentMatchers.any;
53 import static org.mockito.ArgumentMatchers.anyBoolean;
54 import static org.mockito.ArgumentMatchers.anySet;
55 import static org.mockito.ArgumentMatchers.anyString;
56 import static org.mockito.ArgumentMatchers.eq;
57 import static org.mockito.Mockito.inOrder;
58 import static org.mockito.Mockito.mock;
59 import static org.mockito.Mockito.only;
60 import static org.mockito.Mockito.spy;
61 import static org.mockito.Mockito.verify;
62 import static org.mockito.Mockito.when;
63 import static org.mockito.internal.verification.VerificationModeFactory.times;
64 import static org.sonar.server.notification.NotificationManager.SubscriberPermissionsOnProject.ALL_MUST_HAVE_ROLE_USER;
65
66 public class DefaultNotificationManagerTest {
67
68
69   private DefaultNotificationManager underTest;
70
71   private PropertiesDao propertiesDao = mock(PropertiesDao.class);
72   private NotificationDispatcher dispatcher = mock(NotificationDispatcher.class);
73   private NotificationChannel emailChannel = mock(NotificationChannel.class);
74   private NotificationChannel twitterChannel = mock(NotificationChannel.class);
75   private NotificationQueueDao notificationQueueDao = mock(NotificationQueueDao.class);
76   private AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
77   private DbClient dbClient = mock(DbClient.class);
78   private DbSession dbSession = mock(DbSession.class);
79   private System2 system2 = mock(System2.class);
80
81   @Before
82   public void setUp() {
83     when(dispatcher.getKey()).thenReturn("NewViolations");
84     when(emailChannel.getKey()).thenReturn("Email");
85     when(twitterChannel.getKey()).thenReturn("Twitter");
86     when(dbClient.openSession(anyBoolean())).thenReturn(dbSession);
87     when(dbClient.propertiesDao()).thenReturn(propertiesDao);
88     when(dbClient.notificationQueueDao()).thenReturn(notificationQueueDao);
89     when(dbClient.authorizationDao()).thenReturn(authorizationDao);
90     when(system2.now()).thenReturn(0L);
91
92     underTest = new DefaultNotificationManager(new NotificationChannel[] {emailChannel, twitterChannel}, dbClient);
93   }
94
95   @Test
96   public void shouldProvideChannelList() {
97     assertThat(underTest.getChannels()).containsOnly(emailChannel, twitterChannel);
98
99     underTest = new DefaultNotificationManager(new NotificationChannel[] {}, dbClient);
100     assertThat(underTest.getChannels()).isEmpty();
101   }
102
103   @Test
104   public void shouldPersist() {
105     Notification notification = new Notification("test");
106     underTest.scheduleForSending(notification);
107
108     verify(notificationQueueDao, only()).insert(any(List.class));
109   }
110
111   @Test
112   public void shouldGetFromQueueAndDelete() {
113     Notification notification = new Notification("test");
114     NotificationQueueDto dto = NotificationQueueDto.toNotificationQueueDto(notification);
115     List<NotificationQueueDto> dtos = Arrays.asList(dto);
116     when(notificationQueueDao.selectOldest(1)).thenReturn(dtos);
117
118     assertThat(underTest.<Notification>getFromQueue()).isNotNull();
119
120     InOrder inOrder = inOrder(notificationQueueDao);
121     inOrder.verify(notificationQueueDao).selectOldest(1);
122     inOrder.verify(notificationQueueDao).delete(dtos);
123   }
124
125   // SONAR-4739
126   @Test
127   public void shouldNotFailWhenUnableToDeserialize() throws Exception {
128     NotificationQueueDto dto1 = mock(NotificationQueueDto.class);
129     when(dto1.toNotification()).thenThrow(new InvalidClassException("Pouet"));
130     List<NotificationQueueDto> dtos = Arrays.asList(dto1);
131     when(notificationQueueDao.selectOldest(1)).thenReturn(dtos);
132
133     underTest = spy(underTest);
134     assertThat(underTest.<Notification>getFromQueue()).isNull();
135     assertThat(underTest.<Notification>getFromQueue()).isNull();
136
137     verify(underTest, times(1)).logDeserializationIssue();
138   }
139
140   @Test
141   public void findSubscribedEmailRecipients_fails_with_NPE_if_projectKey_is_null() {
142     String dispatcherKey = randomAlphabetic(12);
143
144     assertThatThrownBy(() -> underTest.findSubscribedEmailRecipients(dispatcherKey, null, ALL_MUST_HAVE_ROLE_USER))
145       .isInstanceOf(NullPointerException.class)
146       .hasMessage("projectKey is mandatory");
147   }
148
149   @Test
150   public void findSubscribedEmailRecipients_with_logins_fails_with_NPE_if_projectKey_is_null() {
151     String dispatcherKey = randomAlphabetic(12);
152
153     assertThatThrownBy(() -> underTest.findSubscribedEmailRecipients(dispatcherKey, null, ImmutableSet.of(), ALL_MUST_HAVE_ROLE_USER))
154       .isInstanceOf(NullPointerException.class)
155       .hasMessage("projectKey is mandatory");
156   }
157
158   @Test
159   public void findSubscribedEmailRecipients_with_logins_fails_with_NPE_if_logins_is_null() {
160     String dispatcherKey = randomAlphabetic(12);
161     String projectKey = randomAlphabetic(6);
162
163     assertThatThrownBy(() -> underTest.findSubscribedEmailRecipients(dispatcherKey, projectKey, null, ALL_MUST_HAVE_ROLE_USER))
164       .isInstanceOf(NullPointerException.class)
165       .hasMessage("logins can't be null");
166   }
167
168   @Test
169   public void findSubscribedEmailRecipients_with_logins_returns_empty_if_login_set_is_empty() {
170     String dispatcherKey = randomAlphabetic(12);
171     String projectKey = randomAlphabetic(6);
172
173     Set<EmailRecipient> recipients = underTest.findSubscribedEmailRecipients(dispatcherKey, projectKey, ImmutableSet.of(), ALL_MUST_HAVE_ROLE_USER);
174
175     assertThat(recipients).isEmpty();
176   }
177
178   @Test
179   public void findSubscribedEmailRecipients_returns_empty_if_no_email_recipients_in_project_for_dispatcher_key() {
180     String dispatcherKey = randomAlphabetic(12);
181     String globalPermission = randomAlphanumeric(4);
182     String projectPermission = randomAlphanumeric(5);
183     String projectKey = randomAlphabetic(6);
184     when(propertiesDao.findEmailSubscribersForNotification(dbSession, dispatcherKey, "EmailNotificationChannel", projectKey))
185       .thenReturn(Collections.emptySet());
186
187     Set<EmailRecipient> emailRecipients = underTest.findSubscribedEmailRecipients(dispatcherKey, projectKey,
188       new SubscriberPermissionsOnProject(globalPermission, projectPermission));
189     assertThat(emailRecipients).isEmpty();
190
191     verify(authorizationDao, times(0)).keepAuthorizedLoginsOnEntity(any(DbSession.class), anySet(), anyString(), anyString());
192   }
193
194   @Test
195   public void findSubscribedEmailRecipients_with_logins_returns_empty_if_no_email_recipients_in_project_for_dispatcher_key() {
196     String dispatcherKey = randomAlphabetic(12);
197     String globalPermission = randomAlphanumeric(4);
198     String projectPermission = randomAlphanumeric(5);
199     String projectKey = randomAlphabetic(6);
200     Set<String> logins = IntStream.range(0, 1 + new Random().nextInt(10))
201       .mapToObj(i -> "login_" + i)
202       .collect(Collectors.toSet());
203     when(propertiesDao.findEmailSubscribersForNotification(dbSession, dispatcherKey, "EmailNotificationChannel", projectKey, logins))
204       .thenReturn(Collections.emptySet());
205
206     Set<EmailRecipient> emailRecipients = underTest.findSubscribedEmailRecipients(dispatcherKey, projectKey, logins,
207       new SubscriberPermissionsOnProject(globalPermission, projectPermission));
208     assertThat(emailRecipients).isEmpty();
209
210     verify(authorizationDao, times(0)).keepAuthorizedLoginsOnEntity(any(DbSession.class), anySet(), anyString(), anyString());
211   }
212
213   @Test
214   public void findSubscribedEmailRecipients_applies_distinct_permission_filtering_global_or_project_subscribers() {
215     String dispatcherKey = randomAlphabetic(12);
216     String globalPermission = randomAlphanumeric(4);
217     String projectPermission = randomAlphanumeric(5);
218     String projectKey = randomAlphabetic(6);
219     when(propertiesDao.findEmailSubscribersForNotification(dbSession, dispatcherKey, "EmailNotificationChannel", projectKey))
220       .thenReturn(
221         newHashSet(EmailSubscriberDto.create("user1", false, "user1@foo"), EmailSubscriberDto.create("user3", false, "user3@foo"),
222           EmailSubscriberDto.create("user3", true, "user3@foo")));
223     when(authorizationDao.keepAuthorizedLoginsOnEntity(dbSession, newHashSet("user3", "user4"), projectKey, globalPermission))
224       .thenReturn(newHashSet("user3"));
225     when(authorizationDao.keepAuthorizedLoginsOnEntity(dbSession, newHashSet("user1", "user3"), projectKey, projectPermission))
226       .thenReturn(newHashSet("user1", "user3"));
227
228     Set<EmailRecipient> emailRecipients = underTest.findSubscribedEmailRecipients(dispatcherKey, projectKey,
229       new SubscriberPermissionsOnProject(globalPermission, projectPermission));
230     assertThat(emailRecipients)
231       .isEqualTo(ImmutableSet.of(new EmailRecipient("user1", "user1@foo"), new EmailRecipient("user3", "user3@foo")));
232
233     // code is optimized to perform only 2 SQL requests for all channels
234     verify(authorizationDao, times(1)).keepAuthorizedLoginsOnEntity(eq(dbSession), anySet(), anyString(), eq(globalPermission));
235     verify(authorizationDao, times(1)).keepAuthorizedLoginsOnEntity(eq(dbSession), anySet(), anyString(), eq(projectPermission));
236   }
237
238   @Test
239   public void findSubscribedEmailRecipients_with_logins_applies_distinct_permission_filtering_global_or_project_subscribers() {
240     String dispatcherKey = randomAlphabetic(12);
241     String globalPermission = randomAlphanumeric(4);
242     String projectPermission = randomAlphanumeric(5);
243     String projectKey = randomAlphabetic(6);
244     Set<String> logins = ImmutableSet.of("user1", "user2", "user3");
245     when(propertiesDao.findEmailSubscribersForNotification(dbSession, dispatcherKey, "EmailNotificationChannel", projectKey, logins))
246       .thenReturn(
247         newHashSet(EmailSubscriberDto.create("user1", false, "user1@foo"), EmailSubscriberDto.create("user3", false, "user3@foo"),
248           EmailSubscriberDto.create("user3", true, "user3@foo")));
249     when(authorizationDao.keepAuthorizedLoginsOnEntity(dbSession, newHashSet("user3", "user4"), projectKey, globalPermission))
250       .thenReturn(newHashSet("user3"));
251     when(authorizationDao.keepAuthorizedLoginsOnEntity(dbSession, newHashSet("user1", "user3"), projectKey, projectPermission))
252       .thenReturn(newHashSet("user1", "user3"));
253
254     Set<EmailRecipient> emailRecipients = underTest.findSubscribedEmailRecipients(dispatcherKey, projectKey, logins,
255       new SubscriberPermissionsOnProject(globalPermission, projectPermission));
256     assertThat(emailRecipients)
257       .isEqualTo(ImmutableSet.of(new EmailRecipient("user1", "user1@foo"), new EmailRecipient("user3", "user3@foo")));
258
259     // code is optimized to perform only 2 SQL requests for all channels
260     verify(authorizationDao, times(1)).keepAuthorizedLoginsOnEntity(eq(dbSession), anySet(), anyString(), eq(globalPermission));
261     verify(authorizationDao, times(1)).keepAuthorizedLoginsOnEntity(eq(dbSession), anySet(), anyString(), eq(projectPermission));
262   }
263
264   @Test
265   public void findSubscribedEmailRecipients_does_not_call_db_for_project_permission_filtering_if_there_is_no_project_subscriber() {
266     String dispatcherKey = randomAlphabetic(12);
267     String globalPermission = randomAlphanumeric(4);
268     String projectPermission = randomAlphanumeric(5);
269     String projectKey = randomAlphabetic(6);
270     Set<EmailSubscriberDto> subscribers = IntStream.range(0, 1 + new Random().nextInt(10))
271       .mapToObj(i -> EmailSubscriberDto.create("user" + i, true, "user" + i + "@sonarsource.com"))
272       .collect(Collectors.toSet());
273     Set<String> logins = subscribers.stream().map(EmailSubscriberDto::getLogin).collect(Collectors.toSet());
274     when(propertiesDao.findEmailSubscribersForNotification(dbSession, dispatcherKey, "EmailNotificationChannel", projectKey))
275       .thenReturn(subscribers);
276     when(authorizationDao.keepAuthorizedLoginsOnEntity(dbSession, logins, projectKey, globalPermission))
277       .thenReturn(logins);
278
279     Set<EmailRecipient> emailRecipients = underTest.findSubscribedEmailRecipients(dispatcherKey, projectKey,
280       new SubscriberPermissionsOnProject(globalPermission, projectPermission));
281     Set<EmailRecipient> expected = subscribers.stream().map(i -> new EmailRecipient(i.getLogin(), i.getEmail())).collect(Collectors.toSet());
282     assertThat(emailRecipients)
283       .isEqualTo(expected);
284
285     verify(authorizationDao, times(1)).keepAuthorizedLoginsOnEntity(eq(dbSession), anySet(), anyString(), eq(globalPermission));
286     verify(authorizationDao, times(0)).keepAuthorizedLoginsOnEntity(eq(dbSession), anySet(), anyString(), eq(projectPermission));
287   }
288
289   @Test
290   public void findSubscribedEmailRecipients_with_logins_does_not_call_db_for_project_permission_filtering_if_there_is_no_project_subscriber() {
291     String dispatcherKey = randomAlphabetic(12);
292     String globalPermission = randomAlphanumeric(4);
293     String projectPermission = randomAlphanumeric(5);
294     String projectKey = randomAlphabetic(6);
295     Set<EmailSubscriberDto> subscribers = IntStream.range(0, 1 + new Random().nextInt(10))
296       .mapToObj(i -> EmailSubscriberDto.create("user" + i, true, "user" + i + "@sonarsource.com"))
297       .collect(Collectors.toSet());
298     Set<String> logins = subscribers.stream().map(EmailSubscriberDto::getLogin).collect(Collectors.toSet());
299     when(propertiesDao.findEmailSubscribersForNotification(dbSession, dispatcherKey, "EmailNotificationChannel", projectKey, logins))
300       .thenReturn(subscribers);
301     when(authorizationDao.keepAuthorizedLoginsOnEntity(dbSession, logins, projectKey, globalPermission))
302       .thenReturn(logins);
303
304     Set<EmailRecipient> emailRecipients = underTest.findSubscribedEmailRecipients(dispatcherKey, projectKey, logins,
305       new SubscriberPermissionsOnProject(globalPermission, projectPermission));
306     Set<EmailRecipient> expected = subscribers.stream().map(i -> new EmailRecipient(i.getLogin(), i.getEmail())).collect(Collectors.toSet());
307     assertThat(emailRecipients)
308       .isEqualTo(expected);
309
310     verify(authorizationDao, times(1)).keepAuthorizedLoginsOnEntity(eq(dbSession), anySet(), anyString(), eq(globalPermission));
311     verify(authorizationDao, times(0)).keepAuthorizedLoginsOnEntity(eq(dbSession), anySet(), anyString(), eq(projectPermission));
312   }
313
314   @Test
315   public void findSubscribedEmailRecipients_does_not_call_DB_for_project_permission_filtering_if_there_is_no_global_subscriber() {
316     String dispatcherKey = randomAlphabetic(12);
317     String globalPermission = randomAlphanumeric(4);
318     String projectPermission = randomAlphanumeric(5);
319     String projectKey = randomAlphabetic(6);
320     Set<EmailSubscriberDto> subscribers = IntStream.range(0, 1 + new Random().nextInt(10))
321       .mapToObj(i -> EmailSubscriberDto.create("user" + i, false, "user" + i + "@sonarsource.com"))
322       .collect(Collectors.toSet());
323     Set<String> logins = subscribers.stream().map(EmailSubscriberDto::getLogin).collect(Collectors.toSet());
324     when(propertiesDao.findEmailSubscribersForNotification(dbSession, dispatcherKey, "EmailNotificationChannel", projectKey))
325       .thenReturn(subscribers);
326     when(authorizationDao.keepAuthorizedLoginsOnEntity(dbSession, logins, projectKey, projectPermission))
327       .thenReturn(logins);
328
329     Set<EmailRecipient> emailRecipients = underTest.findSubscribedEmailRecipients(dispatcherKey, projectKey,
330       new SubscriberPermissionsOnProject(globalPermission, projectPermission));
331     Set<EmailRecipient> expected = subscribers.stream().map(i -> new EmailRecipient(i.getLogin(), i.getEmail())).collect(Collectors.toSet());
332     assertThat(emailRecipients)
333       .isEqualTo(expected);
334
335     verify(authorizationDao, times(0)).keepAuthorizedLoginsOnEntity(eq(dbSession), anySet(), anyString(), eq(globalPermission));
336     verify(authorizationDao, times(1)).keepAuthorizedLoginsOnEntity(eq(dbSession), anySet(), anyString(), eq(projectPermission));
337   }
338
339   @Test
340   public void findSubscribedEmailRecipients_with_logins_does_not_call_DB_for_project_permission_filtering_if_there_is_no_global_subscriber() {
341     String dispatcherKey = randomAlphabetic(12);
342     String globalPermission = randomAlphanumeric(4);
343     String projectPermission = randomAlphanumeric(5);
344     String projectKey = randomAlphabetic(6);
345     Set<EmailSubscriberDto> subscribers = IntStream.range(0, 1 + new Random().nextInt(10))
346       .mapToObj(i -> EmailSubscriberDto.create("user" + i, false, "user" + i + "@sonarsource.com"))
347       .collect(Collectors.toSet());
348     Set<String> logins = subscribers.stream().map(EmailSubscriberDto::getLogin).collect(Collectors.toSet());
349     when(propertiesDao.findEmailSubscribersForNotification(dbSession, dispatcherKey, "EmailNotificationChannel", projectKey, logins))
350       .thenReturn(subscribers);
351     when(authorizationDao.keepAuthorizedLoginsOnEntity(dbSession, logins, projectKey, projectPermission))
352       .thenReturn(logins);
353
354     Set<EmailRecipient> emailRecipients = underTest.findSubscribedEmailRecipients(dispatcherKey, projectKey, logins,
355       new SubscriberPermissionsOnProject(globalPermission, projectPermission));
356     Set<EmailRecipient> expected = subscribers.stream().map(i -> new EmailRecipient(i.getLogin(), i.getEmail())).collect(Collectors.toSet());
357     assertThat(emailRecipients)
358       .isEqualTo(expected);
359
360     verify(authorizationDao, times(0)).keepAuthorizedLoginsOnEntity(eq(dbSession), anySet(), anyString(), eq(globalPermission));
361     verify(authorizationDao, times(1)).keepAuthorizedLoginsOnEntity(eq(dbSession), anySet(), anyString(), eq(projectPermission));
362   }
363 }