3 * Copyright (C) 2009-2024 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 IssuesChangesNotificationSerializer issuesChangesSerializer = mock(IssuesChangesNotificationSerializer.class);
86 private NotificationFactory underTest = new NotificationFactory(treeRootHolder, analysisMetadata, ruleRepository, issuesChangesSerializer);
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");
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");
103 public void newMyNewIssuesNotification_DetailsSupplier_getUserNameByUuid_fails_with_NPE_if_uuid_is_null() {
104 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
106 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
108 assertThatThrownBy(() -> detailsSupplier.getUserNameByUuid(null))
109 .isInstanceOf(NullPointerException.class)
110 .hasMessage("uuid can't be null");
114 public void newMyNewIssuesNotification_DetailsSupplier_getUserNameByUuid_always_returns_empty_if_map_argument_is_empty() {
115 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
117 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
118 assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
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());
127 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(
128 users.stream().collect(Collectors.toMap(UserDto::getUuid, Function.identity())));
130 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
131 assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
133 .forEach(user -> assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).contains(user.getName()));
137 public void newMyNewIssuesNotification_DetailsSupplier_getUserNameByUuid_returns_empty_if_user_has_null_name() {
138 UserDto user = UserTesting.newUserDto().setLogin("user_noname").setName(null);
140 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(ImmutableMap.of(user.getUuid(), user));
142 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
143 assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).isEmpty();
147 public void newNewIssuesNotification_DetailsSupplier_getUserNameByUuid_fails_with_NPE_if_uuid_is_null() {
148 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
150 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
152 assertThatThrownBy(() -> detailsSupplier.getUserNameByUuid(null))
153 .isInstanceOf(NullPointerException.class)
154 .hasMessage("uuid can't be null");
158 public void newNewIssuesNotification_DetailsSupplier_getUserNameByUuid_always_returns_empty_if_map_argument_is_empty() {
159 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
161 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
162 assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
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());
171 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(
172 users.stream().collect(Collectors.toMap(UserDto::getUuid, Function.identity())));
174 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
175 assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
177 .forEach(user -> assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).contains(user.getName()));
181 public void newNewIssuesNotification_DetailsSupplier_getUserNameByUuid_returns_empty_if_user_has_null_name() {
182 UserDto user = UserTesting.newUserDto().setLogin("user_noname").setName(null);
184 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(ImmutableMap.of(user.getUuid(), user));
186 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
187 assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).isEmpty();
191 public void newMyNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_fails_with_ISE_if_TreeRootHolder_is_not_initialized() {
192 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
194 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
196 assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid("foo"))
197 .isInstanceOf(IllegalStateException.class)
198 .hasMessage("Holder has not been initialized yet");
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());
205 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
207 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
209 assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid(null))
210 .isInstanceOf(NullPointerException.class)
211 .hasMessage("uuid can't be null");
215 public void newMyNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_returns_name_of_project_in_TreeRootHolder() {
216 treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root").build());
218 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
220 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
222 assertThat(detailsSupplier.getComponentNameByUuid("rootUuid")).contains("root");
223 assertThat(detailsSupplier.getComponentNameByUuid("foo")).isEmpty();
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())
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())
236 .addChildren(ReportComponent.builder(FILE, 11).setUuid("file11Uuid").setName("file11").setShortName("file11_short").build())
238 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
240 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
242 Stream.of("dir1", "dir2", "file11", "file21", "file31", "file32")
244 assertThat(detailsSupplier.getComponentNameByUuid(name + "Uuid")).contains(name + "_short");
245 assertThat(detailsSupplier.getComponentNameByUuid(name)).isEmpty();
250 public void newNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_fails_with_ISE_if_TreeRootHolder_is_not_initialized() {
251 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
253 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
255 assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid("foo"))
256 .isInstanceOf(IllegalStateException.class)
257 .hasMessage("Holder has not been initialized yet");
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());
265 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
267 assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid(null))
268 .isInstanceOf(NullPointerException.class)
269 .hasMessage("uuid can't be null");
273 public void newNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_returns_name_of_project_in_TreeRootHolder() {
274 treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root").build());
276 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
278 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
280 assertThat(detailsSupplier.getComponentNameByUuid("rootUuid")).contains("root");
281 assertThat(detailsSupplier.getComponentNameByUuid("foo")).isEmpty();
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())
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())
294 .addChildren(ReportComponent.builder(FILE, 11).setUuid("file11Uuid").setName("file11").setShortName("file11_short").build())
297 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
299 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
301 Stream.of("dir1", "dir2", "file11", "file21", "file31", "file32")
303 assertThat(detailsSupplier.getComponentNameByUuid(name + "Uuid")).contains(name + "_short");
304 assertThat(detailsSupplier.getComponentNameByUuid(name)).isEmpty();
309 public void newMyNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_fails_with_NPE_if_ruleKey_is_null() {
310 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
312 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
314 assertThatThrownBy(() -> detailsSupplier.getRuleDefinitionByRuleKey(null))
315 .isInstanceOf(NullPointerException.class)
316 .hasMessage("ruleKey can't be null");
320 public void newMyNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_always_returns_empty_if_RuleRepository_is_empty() {
321 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
323 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
325 assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("foo", "bar"))).isEmpty();
326 assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("bar", "foo"))).isEmpty();
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");
338 MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
340 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
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")))
353 public void newNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_fails_with_NPE_if_ruleKey_is_null() {
354 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
356 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
358 assertThatThrownBy(() -> detailsSupplier.getRuleDefinitionByRuleKey(null))
359 .isInstanceOf(NullPointerException.class)
360 .hasMessage("ruleKey can't be null");
364 public void newNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_always_returns_empty_if_RuleRepository_is_empty() {
365 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
367 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
369 assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("foo", "bar"))).isEmpty();
370 assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("bar", "foo"))).isEmpty();
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");
382 NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
384 DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
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")))
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();
403 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(issues, assigneesByUuid))
404 .isInstanceOf(IllegalStateException.class)
405 .hasMessage("Analysis date has not been set");
409 public void newIssuesChangesNotification_fails_with_IAE_if_issues_is_empty() {
410 analysisMetadata.setAnalysisDate(new Random().nextLong());
411 Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
413 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(Collections.emptySet(), assigneesByUuid))
414 .isInstanceOf(IllegalArgumentException.class)
415 .hasMessage("issues can't be empty");
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());
424 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
425 .isInstanceOf(NullPointerException.class);
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());
436 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
437 .isInstanceOf(IllegalStateException.class)
438 .hasMessage("Can not find rule " + ruleKey + " in RuleRepository");
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());
450 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
451 .isInstanceOf(IllegalStateException.class)
452 .hasMessage("Holder has not been initialized yet");
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());
465 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
466 .isInstanceOf(IllegalStateException.class)
467 .hasMessage("Branch has not been set");
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));
481 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
482 .isInstanceOf(NullPointerException.class)
483 .hasMessage("key can't be null");
487 public void newIssuesChangesNotification_fails_with_NPE_if_issue_has_no_status() {
488 RuleKey ruleKey = RuleKey.of("foo", "bar");
489 DefaultIssue issue = new DefaultIssue()
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));
498 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
499 .isInstanceOf(NullPointerException.class)
500 .hasMessage("newStatus can't be null");
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()
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);
520 IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
522 assertThat(notification).isSameAs(expected);
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();
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[][] {
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()
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);
564 IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
566 assertThat(notification).isSameAs(expected);
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);
578 public void newIssuesChangesNotification_creates_rule_from_RuleRepository() {
579 RuleKey ruleKey = RuleKey.of("foo", "bar");
580 DefaultIssue issue = new DefaultIssue()
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);
594 IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
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());
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()
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)));
620 assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
621 .isInstanceOf(IllegalStateException.class)
622 .hasMessage("Can not find DTO for assignee uuid " + assigneeUuid);
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()
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);
644 IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
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());
658 public void newIssuesChangesNotification_creates_AnalysisChange_with_analysis_date() {
659 RuleKey ruleKey = RuleKey.of("foo", "bar");
660 DefaultIssue issue = new DefaultIssue()
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);
674 IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
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);
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();
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);
704 IssuesChangesNotification notification = underTest.newIssuesChangesNotification(issues, emptyMap());
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()));
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());
721 private static Map<String, UserDto> nonEmptyAssigneesByUuid() {
722 return IntStream.range(0, 1 + new Random().nextInt(3))
724 .collect(Collectors.toMap(i -> "uuid_" + i, i1 -> new UserDto()));
727 private IssuesChangesNotificationBuilder verifyAndCaptureIssueChangeNotificationBuilder() {
728 ArgumentCaptor<IssuesChangesNotificationBuilder> builderCaptor = ArgumentCaptor.forClass(IssuesChangesNotificationBuilder.class);
729 verify(issuesChangesSerializer).serialize(builderCaptor.capture());
730 verifyNoMoreInteractions(issuesChangesSerializer);
732 return builderCaptor.getValue();
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;
743 private static Durations readDurationsField(NewIssuesNotification notification) {
744 return readField(notification, "durations");
747 private static Durations readField(NewIssuesNotification notification, String fieldName) {
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);
758 private static DetailsSupplier readDetailsSupplier(NewIssuesNotification notification) {
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);