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