]> source.dussan.org Git - sonarqube.git/blob
318ab7685a28a8c39ed5e83ba4ffd821be925144
[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.ce.task.projectanalysis.notification;
21
22 import com.google.common.collect.ImmutableMap;
23 import com.google.common.collect.ImmutableSet;
24 import com.tngtech.java.junit.dataprovider.DataProvider;
25 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
26 import com.tngtech.java.junit.dataprovider.UseDataProvider;
27 import java.lang.reflect.Field;
28 import java.util.Collections;
29 import java.util.Map;
30 import java.util.Random;
31 import java.util.Set;
32 import java.util.function.Function;
33 import java.util.stream.Collectors;
34 import java.util.stream.IntStream;
35 import java.util.stream.Stream;
36 import org.junit.Rule;
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 import org.mockito.ArgumentCaptor;
40 import org.sonar.api.rule.RuleKey;
41 import org.sonar.api.utils.Durations;
42 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
43 import org.sonar.ce.task.projectanalysis.analysis.Branch;
44 import org.sonar.ce.task.projectanalysis.component.ReportComponent;
45 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
46 import org.sonar.ce.task.projectanalysis.issue.DumbRule;
47 import org.sonar.ce.task.projectanalysis.issue.RuleRepositoryRule;
48 import org.sonar.core.issue.DefaultIssue;
49 import org.sonar.db.component.BranchType;
50 import org.sonar.db.user.UserDto;
51 import org.sonar.db.user.UserTesting;
52 import org.sonar.server.issue.notification.IssuesChangesNotification;
53 import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder;
54 import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.AnalysisChange;
55 import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.ChangedIssue;
56 import org.sonar.server.issue.notification.IssuesChangesNotificationSerializer;
57 import org.sonar.server.issue.notification.MyNewIssuesNotification;
58 import org.sonar.server.issue.notification.NewIssuesNotification;
59 import org.sonar.server.issue.notification.NewIssuesNotification.DetailsSupplier;
60 import org.sonar.server.issue.notification.NewIssuesNotification.RuleDefinition;
61
62 import static java.util.Collections.emptyMap;
63 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
64 import static org.assertj.core.api.Assertions.assertThat;
65 import static org.assertj.core.api.Assertions.assertThatThrownBy;
66 import static org.mockito.ArgumentMatchers.any;
67 import static org.mockito.Mockito.mock;
68 import static org.mockito.Mockito.verify;
69 import static org.mockito.Mockito.verifyNoMoreInteractions;
70 import static org.mockito.Mockito.when;
71 import static org.sonar.api.issue.Issue.STATUS_OPEN;
72 import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
73 import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
74 import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
75
76 @RunWith(DataProviderRunner.class)
77 public class NotificationFactoryTest {
78   @Rule
79   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
80   @Rule
81   public RuleRepositoryRule ruleRepository = new RuleRepositoryRule();
82   @Rule
83   public AnalysisMetadataHolderRule analysisMetadata = new AnalysisMetadataHolderRule();
84
85   private Durations durations = new Durations();
86   private IssuesChangesNotificationSerializer issuesChangesSerializer = mock(IssuesChangesNotificationSerializer.class);
87   private NotificationFactory underTest = new NotificationFactory(treeRootHolder, analysisMetadata, ruleRepository, durations, issuesChangesSerializer);
88
89   @Test
90   public void newMyNewIssuesNotification_throws_NPE_if_assigneesByUuid_is_null() {
91     assertThatThrownBy(() -> underTest.newMyNewIssuesNotification(null))
92       .isInstanceOf(NullPointerException.class)
93       .hasMessage("assigneesByUuid can't be null");
94   }
95
96   @Test
97   public void newNewIssuesNotification_throws_NPE_if_assigneesByUuid_is_null() {
98     assertThatThrownBy(() -> underTest.newNewIssuesNotification(null))
99       .isInstanceOf(NullPointerException.class)
100       .hasMessage("assigneesByUuid can't be null");
101   }
102
103   @Test
104   public void newMyNewIssuesNotification_returns_MyNewIssuesNotification_object_with_the_constructor_Durations() {
105     MyNewIssuesNotification notification = underTest.newMyNewIssuesNotification(emptyMap());
106
107     assertThat(readDurationsField(notification)).isSameAs(durations);
108   }
109
110   @Test
111   public void newNewIssuesNotification_returns_NewIssuesNotification_object_with_the_constructor_Durations() {
112     NewIssuesNotification notification = underTest.newNewIssuesNotification(emptyMap());
113
114     assertThat(readDurationsField(notification)).isSameAs(durations);
115   }
116
117   @Test
118   public void newMyNewIssuesNotification_DetailsSupplier_getUserNameByUuid_fails_with_NPE_if_uuid_is_null() {
119     MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
120
121     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
122
123     assertThatThrownBy(() -> detailsSupplier.getUserNameByUuid(null))
124       .isInstanceOf(NullPointerException.class)
125       .hasMessage("uuid can't be null");
126   }
127
128   @Test
129   public void newMyNewIssuesNotification_DetailsSupplier_getUserNameByUuid_always_returns_empty_if_map_argument_is_empty() {
130     MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
131
132     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
133     assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
134   }
135
136   @Test
137   public void newMyNewIssuesNotification_DetailsSupplier_getUserNameByUuid_returns_name_of_user_from_map_argument() {
138     Set<UserDto> users = IntStream.range(0, 1 + new Random().nextInt(10))
139       .mapToObj(i -> UserTesting.newUserDto().setLogin("user" + i))
140       .collect(Collectors.toSet());
141
142     MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(
143       users.stream().collect(Collectors.toMap(UserDto::getUuid, Function.identity())));
144
145     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
146     assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
147     users
148       .forEach(user -> assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).contains(user.getName()));
149   }
150
151   @Test
152   public void newMyNewIssuesNotification_DetailsSupplier_getUserNameByUuid_returns_empty_if_user_has_null_name() {
153     UserDto user = UserTesting.newUserDto().setLogin("user_noname").setName(null);
154
155     MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(ImmutableMap.of(user.getUuid(), user));
156
157     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
158     assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).isEmpty();
159   }
160
161   @Test
162   public void newNewIssuesNotification_DetailsSupplier_getUserNameByUuid_fails_with_NPE_if_uuid_is_null() {
163     NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
164
165     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
166
167     assertThatThrownBy(() -> detailsSupplier.getUserNameByUuid(null))
168       .isInstanceOf(NullPointerException.class)
169       .hasMessage("uuid can't be null");
170   }
171
172   @Test
173   public void newNewIssuesNotification_DetailsSupplier_getUserNameByUuid_always_returns_empty_if_map_argument_is_empty() {
174     NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
175
176     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
177     assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
178   }
179
180   @Test
181   public void newNewIssuesNotification_DetailsSupplier_getUserNameByUuid_returns_name_of_user_from_map_argument() {
182     Set<UserDto> users = IntStream.range(0, 1 + new Random().nextInt(10))
183       .mapToObj(i -> UserTesting.newUserDto().setLogin("user" + i))
184       .collect(Collectors.toSet());
185
186     NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(
187       users.stream().collect(Collectors.toMap(UserDto::getUuid, Function.identity())));
188
189     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
190     assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
191     users
192       .forEach(user -> assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).contains(user.getName()));
193   }
194
195   @Test
196   public void newNewIssuesNotification_DetailsSupplier_getUserNameByUuid_returns_empty_if_user_has_null_name() {
197     UserDto user = UserTesting.newUserDto().setLogin("user_noname").setName(null);
198
199     NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(ImmutableMap.of(user.getUuid(), user));
200
201     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
202     assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).isEmpty();
203   }
204
205   @Test
206   public void newMyNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_fails_with_ISE_if_TreeRootHolder_is_not_initialized() {
207     MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
208
209     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
210
211     assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid("foo"))
212       .isInstanceOf(IllegalStateException.class)
213       .hasMessage("Holder has not been initialized yet");
214   }
215
216   @Test
217   public void newMyNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_fails_with_NPE_if_uuid_is_null() {
218     treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root").build());
219
220     MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
221
222     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
223
224     assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid(null))
225       .isInstanceOf(NullPointerException.class)
226       .hasMessage("uuid can't be null");
227   }
228
229   @Test
230   public void newMyNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_returns_name_of_project_in_TreeRootHolder() {
231     treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root").build());
232
233     MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
234
235     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
236
237     assertThat(detailsSupplier.getComponentNameByUuid("rootUuid")).contains("root");
238     assertThat(detailsSupplier.getComponentNameByUuid("foo")).isEmpty();
239   }
240
241   @Test
242   public void newMyNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_returns_shortName_of_dir_and_file_in_TreeRootHolder() {
243     treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root")
244       .addChildren(ReportComponent.builder(DIRECTORY, 2).setUuid("dir1Uuid").setName("dir1").setShortName("dir1_short")
245         .addChildren(ReportComponent.builder(FILE, 21).setUuid("file21Uuid").setName("file21").setShortName("file21_short").build())
246         .build())
247       .addChildren(ReportComponent.builder(DIRECTORY, 3).setUuid("dir2Uuid").setName("dir2").setShortName("dir2_short")
248         .addChildren(ReportComponent.builder(FILE, 31).setUuid("file31Uuid").setName("file31").setShortName("file31_short").build())
249         .addChildren(ReportComponent.builder(FILE, 32).setUuid("file32Uuid").setName("file32").setShortName("file32_short").build())
250         .build())
251       .addChildren(ReportComponent.builder(FILE, 11).setUuid("file11Uuid").setName("file11").setShortName("file11_short").build())
252       .build());
253     MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
254
255     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
256
257     Stream.of("dir1", "dir2", "file11", "file21", "file31", "file32")
258       .forEach(name -> {
259         assertThat(detailsSupplier.getComponentNameByUuid(name + "Uuid")).contains(name + "_short");
260         assertThat(detailsSupplier.getComponentNameByUuid(name)).isEmpty();
261       });
262   }
263
264   @Test
265   public void newNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_fails_with_ISE_if_TreeRootHolder_is_not_initialized() {
266     NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
267
268     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
269
270     assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid("foo"))
271       .isInstanceOf(IllegalStateException.class)
272       .hasMessage("Holder has not been initialized yet");
273   }
274
275   @Test
276   public void newNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_fails_with_NPE_if_uuid_is_null() {
277     treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root").build());
278     NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
279
280     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
281
282     assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid(null))
283       .isInstanceOf(NullPointerException.class)
284       .hasMessage("uuid can't be null");
285   }
286
287   @Test
288   public void newNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_returns_name_of_project_in_TreeRootHolder() {
289     treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root").build());
290
291     NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
292
293     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
294
295     assertThat(detailsSupplier.getComponentNameByUuid("rootUuid")).contains("root");
296     assertThat(detailsSupplier.getComponentNameByUuid("foo")).isEmpty();
297   }
298
299   @Test
300   public void newNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_returns_shortName_of_dir_and_file_in_TreeRootHolder() {
301     treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root")
302       .addChildren(ReportComponent.builder(DIRECTORY, 2).setUuid("dir1Uuid").setName("dir1").setShortName("dir1_short")
303         .addChildren(ReportComponent.builder(FILE, 21).setUuid("file21Uuid").setName("file21").setShortName("file21_short").build())
304         .build())
305       .addChildren(ReportComponent.builder(DIRECTORY, 3).setUuid("dir2Uuid").setName("dir2").setShortName("dir2_short")
306         .addChildren(ReportComponent.builder(FILE, 31).setUuid("file31Uuid").setName("file31").setShortName("file31_short").build())
307         .addChildren(ReportComponent.builder(FILE, 32).setUuid("file32Uuid").setName("file32").setShortName("file32_short").build())
308         .build())
309       .addChildren(ReportComponent.builder(FILE, 11).setUuid("file11Uuid").setName("file11").setShortName("file11_short").build())
310       .build());
311
312     NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
313
314     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
315
316     Stream.of("dir1", "dir2", "file11", "file21", "file31", "file32")
317       .forEach(name -> {
318         assertThat(detailsSupplier.getComponentNameByUuid(name + "Uuid")).contains(name + "_short");
319         assertThat(detailsSupplier.getComponentNameByUuid(name)).isEmpty();
320       });
321   }
322
323   @Test
324   public void newMyNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_fails_with_NPE_if_ruleKey_is_null() {
325     MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
326
327     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
328
329     assertThatThrownBy(() -> detailsSupplier.getRuleDefinitionByRuleKey(null))
330       .isInstanceOf(NullPointerException.class)
331       .hasMessage("ruleKey can't be null");
332   }
333
334   @Test
335   public void newMyNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_always_returns_empty_if_RuleRepository_is_empty() {
336     MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
337
338     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
339
340     assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("foo", "bar"))).isEmpty();
341     assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("bar", "foo"))).isEmpty();
342   }
343
344   @Test
345   public void newMyNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_returns_name_and_language_from_RuleRepository() {
346     RuleKey rulekey1 = RuleKey.of("foo", "bar");
347     RuleKey rulekey2 = RuleKey.of("foo", "donut");
348     RuleKey rulekey3 = RuleKey.of("no", "language");
349     DumbRule rule1 = ruleRepository.add(rulekey1).setName("rule1").setLanguage("lang1");
350     DumbRule rule2 = ruleRepository.add(rulekey2).setName("rule2").setLanguage("lang2");
351     DumbRule rule3 = ruleRepository.add(rulekey3).setName("rule3");
352
353     MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
354
355     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
356
357     assertThat(detailsSupplier.getRuleDefinitionByRuleKey(rulekey1))
358       .contains(new RuleDefinition(rule1.getName(), rule1.getLanguage()));
359     assertThat(detailsSupplier.getRuleDefinitionByRuleKey(rulekey2))
360       .contains(new RuleDefinition(rule2.getName(), rule2.getLanguage()));
361     assertThat(detailsSupplier.getRuleDefinitionByRuleKey(rulekey3))
362       .contains(new RuleDefinition(rule3.getName(), null));
363     assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("donut", "foo")))
364       .isEmpty();
365   }
366
367   @Test
368   public void newNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_fails_with_NPE_if_ruleKey_is_null() {
369     NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
370
371     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
372
373     assertThatThrownBy(() -> detailsSupplier.getRuleDefinitionByRuleKey(null))
374       .isInstanceOf(NullPointerException.class)
375       .hasMessage("ruleKey can't be null");
376   }
377
378   @Test
379   public void newNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_always_returns_empty_if_RuleRepository_is_empty() {
380     NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
381
382     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
383
384     assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("foo", "bar"))).isEmpty();
385     assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("bar", "foo"))).isEmpty();
386   }
387
388   @Test
389   public void newNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_returns_name_and_language_from_RuleRepository() {
390     RuleKey rulekey1 = RuleKey.of("foo", "bar");
391     RuleKey rulekey2 = RuleKey.of("foo", "donut");
392     RuleKey rulekey3 = RuleKey.of("no", "language");
393     DumbRule rule1 = ruleRepository.add(rulekey1).setName("rule1").setLanguage("lang1");
394     DumbRule rule2 = ruleRepository.add(rulekey2).setName("rule2").setLanguage("lang2");
395     DumbRule rule3 = ruleRepository.add(rulekey3).setName("rule3");
396
397     NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
398
399     DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
400
401     assertThat(detailsSupplier.getRuleDefinitionByRuleKey(rulekey1))
402       .contains(new RuleDefinition(rule1.getName(), rule1.getLanguage()));
403     assertThat(detailsSupplier.getRuleDefinitionByRuleKey(rulekey2))
404       .contains(new RuleDefinition(rule2.getName(), rule2.getLanguage()));
405     assertThat(detailsSupplier.getRuleDefinitionByRuleKey(rulekey3))
406       .contains(new RuleDefinition(rule3.getName(), null));
407     assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("donut", "foo")))
408       .isEmpty();
409   }
410
411   @Test
412   public void newIssuesChangesNotification_fails_with_ISE_if_analysis_date_has_not_been_set() {
413     Set<DefaultIssue> issues = IntStream.range(0, 1 + new Random().nextInt(2))
414       .mapToObj(i -> new DefaultIssue())
415       .collect(Collectors.toSet());
416     Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
417
418     assertThatThrownBy(() -> underTest.newIssuesChangesNotification(issues, assigneesByUuid))
419       .isInstanceOf(IllegalStateException.class)
420       .hasMessage("Analysis date has not been set");
421   }
422
423   @Test
424   public void newIssuesChangesNotification_fails_with_IAE_if_issues_is_empty() {
425     analysisMetadata.setAnalysisDate(new Random().nextLong());
426     Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
427
428     assertThatThrownBy(() ->  underTest.newIssuesChangesNotification(Collections.emptySet(), assigneesByUuid))
429       .isInstanceOf(IllegalArgumentException.class)
430       .hasMessage("issues can't be empty");
431   }
432
433   @Test
434   public void newIssuesChangesNotification_fails_with_NPE_if_issue_has_no_rule() {
435     DefaultIssue issue = new DefaultIssue();
436     Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
437     analysisMetadata.setAnalysisDate(new Random().nextLong());
438
439     assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
440       .isInstanceOf(NullPointerException.class);
441   }
442
443   @Test
444   public void newIssuesChangesNotification_fails_with_ISE_if_rule_of_issue_does_not_exist_in_repository() {
445     RuleKey ruleKey = RuleKey.of("foo", "bar");
446     DefaultIssue issue = new DefaultIssue()
447       .setRuleKey(ruleKey);
448     Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
449     analysisMetadata.setAnalysisDate(new Random().nextLong());
450
451     assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
452       .isInstanceOf(IllegalStateException.class)
453       .hasMessage("Can not find rule " + ruleKey + " in RuleRepository");
454   }
455
456   @Test
457   public void newIssuesChangesNotification_fails_with_ISE_if_treeRootHolder_is_empty() {
458     RuleKey ruleKey = RuleKey.of("foo", "bar");
459     DefaultIssue issue = new DefaultIssue()
460       .setRuleKey(ruleKey);
461     Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
462     ruleRepository.add(ruleKey);
463     analysisMetadata.setAnalysisDate(new Random().nextLong());
464
465     assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
466       .isInstanceOf(IllegalStateException.class)
467       .hasMessage("Holder has not been initialized yet");
468   }
469
470   @Test
471   public void newIssuesChangesNotification_fails_with_ISE_if_branch_has_not_been_set() {
472     RuleKey ruleKey = RuleKey.of("foo", "bar");
473     DefaultIssue issue = new DefaultIssue()
474       .setRuleKey(ruleKey);
475     Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
476     ruleRepository.add(ruleKey);
477     analysisMetadata.setAnalysisDate(new Random().nextLong());
478     treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).build());
479
480     assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
481       .isInstanceOf(IllegalStateException.class)
482       .hasMessage("Branch has not been set");
483   }
484
485   @Test
486   public void newIssuesChangesNotification_fails_with_NPE_if_issue_has_no_key() {
487     RuleKey ruleKey = RuleKey.of("foo", "bar");
488     DefaultIssue issue = new DefaultIssue()
489       .setRuleKey(ruleKey);
490     Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
491     ruleRepository.add(ruleKey);
492     treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).build());
493     analysisMetadata.setAnalysisDate(new Random().nextLong());
494     analysisMetadata.setBranch(mock(Branch.class));
495
496     assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
497       .isInstanceOf(NullPointerException.class)
498       .hasMessage("key can't be null");
499   }
500
501   @Test
502   public void newIssuesChangesNotification_fails_with_NPE_if_issue_has_no_status() {
503     RuleKey ruleKey = RuleKey.of("foo", "bar");
504     DefaultIssue issue = new DefaultIssue()
505       .setRuleKey(ruleKey)
506       .setKey("issueKey");
507     Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
508     ruleRepository.add(ruleKey);
509     treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).build());
510     analysisMetadata.setAnalysisDate(new Random().nextLong());
511     analysisMetadata.setBranch(mock(Branch.class));
512
513     assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
514       .isInstanceOf(NullPointerException.class)
515       .hasMessage("newStatus can't be null");
516   }
517
518   @Test
519   @UseDataProvider("noBranchNameBranches")
520   public void newIssuesChangesNotification_creates_project_from_TreeRootHolder_and_branch_name_only_on_non_main_branches(Branch branch) {
521     RuleKey ruleKey = RuleKey.of("foo", "bar");
522     DefaultIssue issue = new DefaultIssue()
523       .setRuleKey(ruleKey)
524       .setKey("issueKey")
525       .setStatus(STATUS_OPEN);
526     Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
527     ReportComponent project = ReportComponent.builder(PROJECT, 1).build();
528     ruleRepository.add(ruleKey);
529     treeRootHolder.setRoot(project);
530     analysisMetadata.setAnalysisDate(new Random().nextLong());
531     analysisMetadata.setBranch(branch);
532     IssuesChangesNotification expected = mock(IssuesChangesNotification.class);
533     when(issuesChangesSerializer.serialize(any(IssuesChangesNotificationBuilder.class))).thenReturn(expected);
534
535     IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
536
537     assertThat(notification).isSameAs(expected);
538
539     IssuesChangesNotificationBuilder builder = verifyAndCaptureIssueChangeNotificationBuilder();
540     assertThat(builder.getIssues()).hasSize(1);
541     ChangedIssue changeIssue = builder.getIssues().iterator().next();
542     assertThat(changeIssue.getProject().getUuid()).isEqualTo(project.getUuid());
543     assertThat(changeIssue.getProject().getKey()).isEqualTo(project.getKey());
544     assertThat(changeIssue.getProject().getProjectName()).isEqualTo(project.getName());
545     assertThat(changeIssue.getProject().getBranchName()).isEmpty();
546   }
547
548   @DataProvider
549   public static Object[][] noBranchNameBranches() {
550     Branch mainBranch = mock(Branch.class);
551     when(mainBranch.isMain()).thenReturn(true);
552     when(mainBranch.getType()).thenReturn(BranchType.BRANCH);
553     Branch pr = mock(Branch.class);
554     when(pr.isMain()).thenReturn(false);
555     when(pr.getType()).thenReturn(BranchType.PULL_REQUEST);
556     return new Object[][] {
557       {mainBranch},
558       {pr}
559     };
560   }
561
562   @Test
563   public void newIssuesChangesNotification_creates_project_from_TreeRootHolder_and_branch_name_from_branch() {
564     RuleKey ruleKey = RuleKey.of("foo", "bar");
565     DefaultIssue issue = new DefaultIssue()
566       .setRuleKey(ruleKey)
567       .setKey("issueKey")
568       .setStatus(STATUS_OPEN);
569     Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
570     ReportComponent project = ReportComponent.builder(PROJECT, 1).build();
571     String branchName = randomAlphabetic(12);
572     ruleRepository.add(ruleKey);
573     treeRootHolder.setRoot(project);
574     analysisMetadata.setAnalysisDate(new Random().nextLong());
575     analysisMetadata.setBranch(newNonMainBranch(BranchType.BRANCH, branchName));
576     IssuesChangesNotification expected = mock(IssuesChangesNotification.class);
577     when(issuesChangesSerializer.serialize(any(IssuesChangesNotificationBuilder.class))).thenReturn(expected);
578
579     IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
580
581     assertThat(notification).isSameAs(expected);
582
583     IssuesChangesNotificationBuilder builder = verifyAndCaptureIssueChangeNotificationBuilder();
584     assertThat(builder.getIssues()).hasSize(1);
585     ChangedIssue changeIssue = builder.getIssues().iterator().next();
586     assertThat(changeIssue.getProject().getUuid()).isEqualTo(project.getUuid());
587     assertThat(changeIssue.getProject().getKey()).isEqualTo(project.getKey());
588     assertThat(changeIssue.getProject().getProjectName()).isEqualTo(project.getName());
589     assertThat(changeIssue.getProject().getBranchName()).contains(branchName);
590   }
591
592   @Test
593   public void newIssuesChangesNotification_creates_rule_from_RuleRepository() {
594     RuleKey ruleKey = RuleKey.of("foo", "bar");
595     DefaultIssue issue = new DefaultIssue()
596       .setRuleKey(ruleKey)
597       .setKey("issueKey")
598       .setStatus(STATUS_OPEN);
599     Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
600     ReportComponent project = ReportComponent.builder(PROJECT, 1).build();
601     String branchName = randomAlphabetic(12);
602     ruleRepository.add(ruleKey);
603     treeRootHolder.setRoot(project);
604     analysisMetadata.setAnalysisDate(new Random().nextLong());
605     analysisMetadata.setBranch(newNonMainBranch(BranchType.BRANCH, branchName));
606     IssuesChangesNotification expected = mock(IssuesChangesNotification.class);
607     when(issuesChangesSerializer.serialize(any(IssuesChangesNotificationBuilder.class))).thenReturn(expected);
608
609     IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
610
611     assertThat(notification).isSameAs(expected);
612     IssuesChangesNotificationBuilder builder = verifyAndCaptureIssueChangeNotificationBuilder();
613     assertThat(builder.getIssues()).hasSize(1);
614     ChangedIssue changeIssue = builder.getIssues().iterator().next();
615     assertThat(changeIssue.getRule().getKey()).isEqualTo(ruleKey);
616     assertThat(changeIssue.getRule().getName()).isEqualTo(ruleRepository.getByKey(ruleKey).getName());
617   }
618
619   @Test
620   public void newIssuesChangesNotification_fails_with_ISE_if_issue_has_assignee_not_in_assigneesByUuid() {
621     RuleKey ruleKey = RuleKey.of("foo", "bar");
622     String assigneeUuid = randomAlphabetic(40);
623     DefaultIssue issue = new DefaultIssue()
624       .setRuleKey(ruleKey)
625       .setKey("issueKey")
626       .setStatus(STATUS_OPEN)
627       .setAssigneeUuid(assigneeUuid);
628     Map<String, UserDto> assigneesByUuid = Collections.emptyMap();
629     ReportComponent project = ReportComponent.builder(PROJECT, 1).build();
630     ruleRepository.add(ruleKey);
631     treeRootHolder.setRoot(project);
632     analysisMetadata.setAnalysisDate(new Random().nextLong());
633     analysisMetadata.setBranch(newNonMainBranch(BranchType.BRANCH, randomAlphabetic(12)));
634
635     assertThatThrownBy(() ->  underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
636       .isInstanceOf(IllegalStateException.class)
637       .hasMessage("Can not find DTO for assignee uuid " + assigneeUuid);
638   }
639
640   @Test
641   public void newIssuesChangesNotification_creates_assignee_from_UserDto() {
642     RuleKey ruleKey = RuleKey.of("foo", "bar");
643     String assigneeUuid = randomAlphabetic(40);
644     DefaultIssue issue = new DefaultIssue()
645       .setRuleKey(ruleKey)
646       .setKey("issueKey")
647       .setStatus(STATUS_OPEN)
648       .setAssigneeUuid(assigneeUuid);
649     UserDto userDto = UserTesting.newUserDto();
650     Map<String, UserDto> assigneesByUuid = ImmutableMap.of(assigneeUuid, userDto);
651     ReportComponent project = ReportComponent.builder(PROJECT, 1).build();
652     ruleRepository.add(ruleKey);
653     treeRootHolder.setRoot(project);
654     analysisMetadata.setAnalysisDate(new Random().nextLong());
655     analysisMetadata.setBranch(newNonMainBranch(BranchType.BRANCH, randomAlphabetic(12)));
656     IssuesChangesNotification expected = mock(IssuesChangesNotification.class);
657     when(issuesChangesSerializer.serialize(any(IssuesChangesNotificationBuilder.class))).thenReturn(expected);
658
659     IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
660
661     assertThat(notification).isSameAs(expected);
662     IssuesChangesNotificationBuilder builder = verifyAndCaptureIssueChangeNotificationBuilder();
663     assertThat(builder.getIssues()).hasSize(1);
664     ChangedIssue changeIssue = builder.getIssues().iterator().next();
665     assertThat(changeIssue.getAssignee()).isPresent();
666     IssuesChangesNotificationBuilder.User assignee = changeIssue.getAssignee().get();
667     assertThat(assignee.getUuid()).isEqualTo(userDto.getUuid());
668     assertThat(assignee.getName()).contains(userDto.getName());
669     assertThat(assignee.getLogin()).isEqualTo(userDto.getLogin());
670   }
671
672   @Test
673   public void newIssuesChangesNotification_creates_AnalysisChange_with_analysis_date() {
674     RuleKey ruleKey = RuleKey.of("foo", "bar");
675     DefaultIssue issue = new DefaultIssue()
676       .setRuleKey(ruleKey)
677       .setKey("issueKey")
678       .setStatus(STATUS_OPEN);
679     Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
680     ReportComponent project = ReportComponent.builder(PROJECT, 1).build();
681     long analysisDate = new Random().nextLong();
682     ruleRepository.add(ruleKey);
683     treeRootHolder.setRoot(project);
684     analysisMetadata.setAnalysisDate(analysisDate);
685     analysisMetadata.setBranch(newNonMainBranch(BranchType.BRANCH, randomAlphabetic(12)));
686     IssuesChangesNotification expected = mock(IssuesChangesNotification.class);
687     when(issuesChangesSerializer.serialize(any(IssuesChangesNotificationBuilder.class))).thenReturn(expected);
688
689     IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
690
691     assertThat(notification).isSameAs(expected);
692     IssuesChangesNotificationBuilder builder = verifyAndCaptureIssueChangeNotificationBuilder();
693     assertThat(builder.getIssues()).hasSize(1);
694     assertThat(builder.getChange())
695       .isInstanceOf(AnalysisChange.class)
696       .extracting(IssuesChangesNotificationBuilder.Change::getDate)
697       .isEqualTo(analysisDate);
698   }
699
700   @Test
701   public void newIssuesChangesNotification_maps_all_issues() {
702     Set<DefaultIssue> issues = IntStream.range(0, 3 + new Random().nextInt(5))
703       .mapToObj(i -> new DefaultIssue()
704         .setRuleKey(RuleKey.of("repo_" + i, "rule_" + i))
705         .setKey("issue_key_" + i)
706         .setStatus("status_" + i))
707       .collect(Collectors.toSet());
708     ReportComponent project = ReportComponent.builder(PROJECT, 1).build();
709     long analysisDate = new Random().nextLong();
710     issues.stream()
711       .map(DefaultIssue::ruleKey)
712       .forEach(ruleKey -> ruleRepository.add(ruleKey));
713     treeRootHolder.setRoot(project);
714     analysisMetadata.setAnalysisDate(analysisDate);
715     analysisMetadata.setBranch(newNonMainBranch(BranchType.BRANCH, randomAlphabetic(12)));
716     IssuesChangesNotification expected = mock(IssuesChangesNotification.class);
717     when(issuesChangesSerializer.serialize(any(IssuesChangesNotificationBuilder.class))).thenReturn(expected);
718
719     IssuesChangesNotification notification = underTest.newIssuesChangesNotification(issues, emptyMap());
720
721     assertThat(notification).isSameAs(expected);
722     IssuesChangesNotificationBuilder builder = verifyAndCaptureIssueChangeNotificationBuilder();
723     assertThat(builder.getIssues()).hasSize(issues.size());
724     Map<String, ChangedIssue> changedIssuesByKey = builder.getIssues().stream()
725       .collect(Collectors.toMap(ChangedIssue::getKey, Function.identity()));
726     issues.forEach(
727       issue -> {
728         ChangedIssue changedIssue = changedIssuesByKey.get(issue.key());
729         assertThat(changedIssue.getNewStatus()).isEqualTo(issue.status());
730         assertThat(changedIssue.getAssignee()).isEmpty();
731         assertThat(changedIssue.getRule().getKey()).isEqualTo(issue.ruleKey());
732         assertThat(changedIssue.getRule().getName()).isEqualTo(ruleRepository.getByKey(issue.ruleKey()).getName());
733       });
734   }
735
736   private static Map<String, UserDto> nonEmptyAssigneesByUuid() {
737     return IntStream.range(0, 1 + new Random().nextInt(3))
738       .boxed()
739       .collect(Collectors.toMap(i -> "uuid_" + i, i1 -> new UserDto()));
740   }
741
742   private IssuesChangesNotificationBuilder verifyAndCaptureIssueChangeNotificationBuilder() {
743     ArgumentCaptor<IssuesChangesNotificationBuilder> builderCaptor = ArgumentCaptor.forClass(IssuesChangesNotificationBuilder.class);
744     verify(issuesChangesSerializer).serialize(builderCaptor.capture());
745     verifyNoMoreInteractions(issuesChangesSerializer);
746
747     return builderCaptor.getValue();
748   }
749
750   private static Branch newNonMainBranch(BranchType branchType, String branchName) {
751     Branch nonMainBranch = mock(Branch.class);
752     when(nonMainBranch.isMain()).thenReturn(false);
753     when(nonMainBranch.getType()).thenReturn(branchType);
754     when(nonMainBranch.getName()).thenReturn(branchName);
755     return nonMainBranch;
756   }
757
758   private static Durations readDurationsField(NewIssuesNotification notification) {
759     return readField(notification, "durations");
760   }
761
762   private static Durations readField(NewIssuesNotification notification, String fieldName) {
763     try {
764       Field durationsField = NewIssuesNotification.class.getDeclaredField(fieldName);
765       durationsField.setAccessible(true);
766       Object o = durationsField.get(notification);
767       return (Durations) o;
768     } catch (IllegalAccessException | NoSuchFieldException e) {
769       throw new RuntimeException(e);
770     }
771   }
772
773   private static DetailsSupplier readDetailsSupplier(NewIssuesNotification notification) {
774     try {
775       Field durationsField = NewIssuesNotification.class.getDeclaredField("detailsSupplier");
776       durationsField.setAccessible(true);
777       return (DetailsSupplier) durationsField.get(notification);
778     } catch (IllegalAccessException | NoSuchFieldException e) {
779       throw new RuntimeException(e);
780     }
781   }
782 }