3 * Copyright (C) 2009-2023 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.ce.task.projectanalysis.notification;
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;
30 import java.util.Random;
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;
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;
76 @RunWith(DataProviderRunner.class)
77 public class NotificationFactoryTest {
79 public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
81 public RuleRepositoryRule ruleRepository = new RuleRepositoryRule();
83 public AnalysisMetadataHolderRule analysisMetadata = new AnalysisMetadataHolderRule();
85 private Durations durations = new Durations();
86 private IssuesChangesNotificationSerializer issuesChangesSerializer = mock(IssuesChangesNotificationSerializer.class);
87 private NotificationFactory underTest = new NotificationFactory(treeRootHolder, analysisMetadata, ruleRepository, durations, issuesChangesSerializer);
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");
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");
104 public void newMyNewIssuesNotification_returns_MyNewIssuesNotification_object_with_the_constructor_Durations() {
105 MyNewIssuesNotification notification = underTest.newMyNewIssuesNotification(emptyMap());
107 assertThat(readDurationsField(notification)).isSameAs(durations);
111 public void newNewIssuesNotification_returns_NewIssuesNotification_object_with_the_constructor_Durations() {
112 NewIssuesNotification notification = underTest.newNewIssuesNotification(emptyMap());
114 assertThat(readDurationsField(notification)).isSameAs(durations);
118 public void newMyNewIssuesNotification_DetailsSupplier_getUserNameByUuid_fails_with_NPE_if_uuid_is_null() {
119 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
121 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
123 assertThatThrownBy(() -> detailsSupplier.getUserNameByUuid(null))
124 .isInstanceOf(NullPointerException.class)
125 .hasMessage("uuid can't be null");
129 public void newMyNewIssuesNotification_DetailsSupplier_getUserNameByUuid_always_returns_empty_if_map_argument_is_empty() {
130 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
132 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
133 assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
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());
142 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(
143 users.stream().collect(Collectors.toMap(UserDto::getUuid, Function.identity())));
145 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
146 assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
148 .forEach(user -> assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).contains(user.getName()));
152 public void newMyNewIssuesNotification_DetailsSupplier_getUserNameByUuid_returns_empty_if_user_has_null_name() {
153 UserDto user = UserTesting.newUserDto().setLogin("user_noname").setName(null);
155 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(ImmutableMap.of(user.getUuid(), user));
157 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
158 assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).isEmpty();
162 public void newNewIssuesNotification_DetailsSupplier_getUserNameByUuid_fails_with_NPE_if_uuid_is_null() {
163 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
165 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
167 assertThatThrownBy(() -> detailsSupplier.getUserNameByUuid(null))
168 .isInstanceOf(NullPointerException.class)
169 .hasMessage("uuid can't be null");
173 public void newNewIssuesNotification_DetailsSupplier_getUserNameByUuid_always_returns_empty_if_map_argument_is_empty() {
174 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
176 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
177 assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
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());
186 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(
187 users.stream().collect(Collectors.toMap(UserDto::getUuid, Function.identity())));
189 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
190 assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
192 .forEach(user -> assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).contains(user.getName()));
196 public void newNewIssuesNotification_DetailsSupplier_getUserNameByUuid_returns_empty_if_user_has_null_name() {
197 UserDto user = UserTesting.newUserDto().setLogin("user_noname").setName(null);
199 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(ImmutableMap.of(user.getUuid(), user));
201 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
202 assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).isEmpty();
206 public void newMyNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_fails_with_ISE_if_TreeRootHolder_is_not_initialized() {
207 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
209 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
211 assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid("foo"))
212 .isInstanceOf(IllegalStateException.class)
213 .hasMessage("Holder has not been initialized yet");
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());
220 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
222 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
224 assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid(null))
225 .isInstanceOf(NullPointerException.class)
226 .hasMessage("uuid can't be null");
230 public void newMyNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_returns_name_of_project_in_TreeRootHolder() {
231 treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root").build());
233 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
235 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
237 assertThat(detailsSupplier.getComponentNameByUuid("rootUuid")).contains("root");
238 assertThat(detailsSupplier.getComponentNameByUuid("foo")).isEmpty();
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())
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())
251 .addChildren(ReportComponent.builder(FILE, 11).setUuid("file11Uuid").setName("file11").setShortName("file11_short").build())
253 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
255 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
257 Stream.of("dir1", "dir2", "file11", "file21", "file31", "file32")
259 assertThat(detailsSupplier.getComponentNameByUuid(name + "Uuid")).contains(name + "_short");
260 assertThat(detailsSupplier.getComponentNameByUuid(name)).isEmpty();
265 public void newNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_fails_with_ISE_if_TreeRootHolder_is_not_initialized() {
266 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
268 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
270 assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid("foo"))
271 .isInstanceOf(IllegalStateException.class)
272 .hasMessage("Holder has not been initialized yet");
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());
280 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
282 assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid(null))
283 .isInstanceOf(NullPointerException.class)
284 .hasMessage("uuid can't be null");
288 public void newNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_returns_name_of_project_in_TreeRootHolder() {
289 treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root").build());
291 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
293 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
295 assertThat(detailsSupplier.getComponentNameByUuid("rootUuid")).contains("root");
296 assertThat(detailsSupplier.getComponentNameByUuid("foo")).isEmpty();
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())
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())
309 .addChildren(ReportComponent.builder(FILE, 11).setUuid("file11Uuid").setName("file11").setShortName("file11_short").build())
312 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
314 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
316 Stream.of("dir1", "dir2", "file11", "file21", "file31", "file32")
318 assertThat(detailsSupplier.getComponentNameByUuid(name + "Uuid")).contains(name + "_short");
319 assertThat(detailsSupplier.getComponentNameByUuid(name)).isEmpty();
324 public void newMyNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_fails_with_NPE_if_ruleKey_is_null() {
325 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
327 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
329 assertThatThrownBy(() -> detailsSupplier.getRuleDefinitionByRuleKey(null))
330 .isInstanceOf(NullPointerException.class)
331 .hasMessage("ruleKey can't be null");
335 public void newMyNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_always_returns_empty_if_RuleRepository_is_empty() {
336 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
338 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
340 assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("foo", "bar"))).isEmpty();
341 assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("bar", "foo"))).isEmpty();
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");
353 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
355 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
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")))
368 public void newNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_fails_with_NPE_if_ruleKey_is_null() {
369 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
371 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
373 assertThatThrownBy(() -> detailsSupplier.getRuleDefinitionByRuleKey(null))
374 .isInstanceOf(NullPointerException.class)
375 .hasMessage("ruleKey can't be null");
379 public void newNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_always_returns_empty_if_RuleRepository_is_empty() {
380 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
382 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
384 assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("foo", "bar"))).isEmpty();
385 assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("bar", "foo"))).isEmpty();
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");
397 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
399 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
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")))
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();
418 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(issues, assigneesByUuid))
419 .isInstanceOf(IllegalStateException.class)
420 .hasMessage("Analysis date has not been set");
424 public void newIssuesChangesNotification_fails_with_IAE_if_issues_is_empty() {
425 analysisMetadata.setAnalysisDate(new Random().nextLong());
426 Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
428 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(Collections.emptySet(), assigneesByUuid))
429 .isInstanceOf(IllegalArgumentException.class)
430 .hasMessage("issues can't be empty");
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());
439 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
440 .isInstanceOf(NullPointerException.class);
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());
451 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
452 .isInstanceOf(IllegalStateException.class)
453 .hasMessage("Can not find rule " + ruleKey + " in RuleRepository");
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());
465 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
466 .isInstanceOf(IllegalStateException.class)
467 .hasMessage("Holder has not been initialized yet");
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());
480 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
481 .isInstanceOf(IllegalStateException.class)
482 .hasMessage("Branch has not been set");
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));
496 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
497 .isInstanceOf(NullPointerException.class)
498 .hasMessage("key can't be null");
502 public void newIssuesChangesNotification_fails_with_NPE_if_issue_has_no_status() {
503 RuleKey ruleKey = RuleKey.of("foo", "bar");
504 DefaultIssue issue = new DefaultIssue()
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));
513 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
514 .isInstanceOf(NullPointerException.class)
515 .hasMessage("newStatus can't be null");
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()
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);
535 IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
537 assertThat(notification).isSameAs(expected);
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();
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[][] {
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()
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);
579 IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
581 assertThat(notification).isSameAs(expected);
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);
593 public void newIssuesChangesNotification_creates_rule_from_RuleRepository() {
594 RuleKey ruleKey = RuleKey.of("foo", "bar");
595 DefaultIssue issue = new DefaultIssue()
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);
609 IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
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());
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()
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)));
635 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
636 .isInstanceOf(IllegalStateException.class)
637 .hasMessage("Can not find DTO for assignee uuid " + assigneeUuid);
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()
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);
659 IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
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());
673 public void newIssuesChangesNotification_creates_AnalysisChange_with_analysis_date() {
674 RuleKey ruleKey = RuleKey.of("foo", "bar");
675 DefaultIssue issue = new DefaultIssue()
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);
689 IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
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);
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();
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);
719 IssuesChangesNotification notification = underTest.newIssuesChangesNotification(issues, emptyMap());
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()));
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());
736 private static Map<String, UserDto> nonEmptyAssigneesByUuid() {
737 return IntStream.range(0, 1 + new Random().nextInt(3))
739 .collect(Collectors.toMap(i -> "uuid_" + i, i1 -> new UserDto()));
742 private IssuesChangesNotificationBuilder verifyAndCaptureIssueChangeNotificationBuilder() {
743 ArgumentCaptor<IssuesChangesNotificationBuilder> builderCaptor = ArgumentCaptor.forClass(IssuesChangesNotificationBuilder.class);
744 verify(issuesChangesSerializer).serialize(builderCaptor.capture());
745 verifyNoMoreInteractions(issuesChangesSerializer);
747 return builderCaptor.getValue();
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;
758 private static Durations readDurationsField(NewIssuesNotification notification) {
759 return readField(notification, "durations");
762 private static Durations readField(NewIssuesNotification notification, String fieldName) {
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);
773 private static DetailsSupplier readDetailsSupplier(NewIssuesNotification notification) {
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);