]> source.dussan.org Git - sonarqube.git/blob
8646a7bb46c0255d26ba0555c9a0053bb978a263
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2017 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.computation.task.projectanalysis.step;
21
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.Date;
25 import java.util.List;
26 import java.util.Random;
27 import java.util.stream.Collectors;
28 import java.util.stream.IntStream;
29 import java.util.stream.Stream;
30 import org.apache.commons.lang.RandomStringUtils;
31 import org.junit.Before;
32 import org.junit.Rule;
33 import org.junit.Test;
34 import org.junit.rules.TemporaryFolder;
35 import org.mockito.ArgumentCaptor;
36 import org.mockito.exceptions.verification.junit.ArgumentsAreDifferent;
37 import org.sonar.api.notifications.Notification;
38 import org.sonar.api.rules.RuleType;
39 import org.sonar.api.utils.Duration;
40 import org.sonar.api.utils.System2;
41 import org.sonar.core.issue.DefaultIssue;
42 import org.sonar.db.component.ComponentDto;
43 import org.sonar.db.rule.RuleDefinitionDto;
44 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
45 import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
46 import org.sonar.server.computation.task.projectanalysis.component.Component;
47 import org.sonar.server.computation.task.projectanalysis.component.Component.Type;
48 import org.sonar.server.computation.task.projectanalysis.component.DefaultBranchImpl;
49 import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
50 import org.sonar.server.computation.task.projectanalysis.issue.IssueCache;
51 import org.sonar.server.computation.task.projectanalysis.issue.RuleRepositoryRule;
52 import org.sonar.server.computation.task.step.ComputationStep;
53 import org.sonar.server.issue.notification.DistributedMetricStatsInt;
54 import org.sonar.server.issue.notification.IssueChangeNotification;
55 import org.sonar.server.issue.notification.MyNewIssuesNotification;
56 import org.sonar.server.issue.notification.NewIssuesNotification;
57 import org.sonar.server.issue.notification.NewIssuesNotificationFactory;
58 import org.sonar.server.issue.notification.NewIssuesStatistics;
59 import org.sonar.server.notification.NotificationService;
60 import org.sonar.server.util.cache.DiskCache;
61
62 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
63 import static org.assertj.core.api.Java6Assertions.assertThat;
64 import static org.mockito.Matchers.anyString;
65 import static org.mockito.Matchers.eq;
66 import static org.mockito.Mockito.any;
67 import static org.mockito.Mockito.mock;
68 import static org.mockito.Mockito.never;
69 import static org.mockito.Mockito.verify;
70 import static org.mockito.Mockito.when;
71 import static org.sonar.db.component.ComponentTesting.newBranchDto;
72 import static org.sonar.db.component.ComponentTesting.newFileDto;
73 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
74 import static org.sonar.db.component.ComponentTesting.newProjectBranch;
75 import static org.sonar.db.issue.IssueTesting.newIssue;
76 import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
77 import static org.sonar.db.rule.RuleTesting.newRule;
78 import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder;
79
80 public class SendIssueNotificationsStepTest extends BaseStepTest {
81
82   private static final String BRANCH_NAME = "feature";
83
84   private static final long ANALYSE_DATE = 123L;
85   private static final int FIVE_MINUTES_IN_MS = 1000 * 60 * 5;
86
87   private static final Duration ISSUE_DURATION = Duration.create(100L);
88   private static final String ISSUE_ASSIGNEE = "John";
89
90   private static final Component FILE = builder(Component.Type.FILE, 11).build();
91   private static final Component PROJECT = builder(Type.PROJECT, 1)
92     .setVersion(RandomStringUtils.randomAlphanumeric(10))
93     .addChildren(FILE).build();
94
95   @Rule
96   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule()
97     .setRoot(PROJECT);
98   @Rule
99   public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule()
100     .setBranch(new DefaultBranchImpl())
101     .setAnalysisDate(new Date(ANALYSE_DATE));
102   @Rule
103   public RuleRepositoryRule ruleRepository = new RuleRepositoryRule();
104   @Rule
105   public TemporaryFolder temp = new TemporaryFolder();
106
107   private final Random random = new Random();
108   private final RuleType randomRuleType = RuleType.values()[random.nextInt(RuleType.values().length)];
109   private NotificationService notificationService = mock(NotificationService.class);
110   private NewIssuesNotificationFactory newIssuesNotificationFactory = mock(NewIssuesNotificationFactory.class);
111   private NewIssuesNotification newIssuesNotificationMock = createNewIssuesNotificationMock();
112   private MyNewIssuesNotification myNewIssuesNotificationMock = createMyNewIssuesNotificationMock();
113
114   private IssueCache issueCache;
115   private SendIssueNotificationsStep underTest;
116
117   @Before
118   public void setUp() throws Exception {
119     issueCache = new IssueCache(temp.newFile(), System2.INSTANCE);
120     underTest = new SendIssueNotificationsStep(issueCache, ruleRepository, treeRootHolder, notificationService, analysisMetadataHolder,
121       newIssuesNotificationFactory);
122
123     when(newIssuesNotificationFactory.newNewIssuesNotication()).thenReturn(newIssuesNotificationMock);
124     when(newIssuesNotificationFactory.newMyNewIssuesNotification()).thenReturn(myNewIssuesNotificationMock);
125   }
126
127   @Test
128   public void do_not_send_notifications_if_no_subscribers() {
129     when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(false);
130
131     underTest.execute();
132
133     verify(notificationService, never()).deliver(any(Notification.class));
134   }
135
136   @Test
137   public void send_global_new_issues_notification() throws Exception {
138     issueCache.newAppender().append(
139       new DefaultIssue().setType(randomRuleType).setEffort(ISSUE_DURATION)
140         .setCreationDate(new Date(ANALYSE_DATE)))
141       .close();
142     when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
143
144     underTest.execute();
145
146     verify(notificationService).deliver(newIssuesNotificationMock);
147     verify(newIssuesNotificationMock).setProject(PROJECT.getPublicKey(), PROJECT.getUuid(), PROJECT.getName(), null);
148     verify(newIssuesNotificationMock).setAnalysisDate(new Date(ANALYSE_DATE));
149     verify(newIssuesNotificationMock).setStatistics(eq(PROJECT.getName()), any(NewIssuesStatistics.Stats.class));
150     verify(newIssuesNotificationMock).setDebt(ISSUE_DURATION);
151   }
152
153   @Test
154   public void send_global_new_issues_notification_only_for_non_backdated_issues() {
155     Random random = new Random();
156     Integer[] efforts = IntStream.range(0, 1 + random.nextInt(10)).mapToObj(i -> 10_000 * i).toArray(Integer[]::new);
157     Integer[] backDatedEfforts = IntStream.range(0, 1 + random.nextInt(10)).mapToObj(i -> 10 + random.nextInt(100)).toArray(Integer[]::new);
158     Duration expectedEffort = Duration.create(Arrays.stream(efforts).mapToInt(i -> i).sum());
159     List<DefaultIssue> issues = Stream.concat(Arrays.stream(efforts)
160       .map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort))
161         .setCreationDate(new Date(ANALYSE_DATE))),
162       Arrays.stream(backDatedEfforts)
163         .map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort))
164           .setCreationDate(new Date(ANALYSE_DATE - FIVE_MINUTES_IN_MS))))
165       .collect(Collectors.toList());
166     Collections.shuffle(issues);
167     DiskCache<DefaultIssue>.DiskAppender issueCache = this.issueCache.newAppender();
168     issues.forEach(issueCache::append);
169     when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
170
171     underTest.execute();
172
173     verify(notificationService).deliver(newIssuesNotificationMock);
174     ArgumentCaptor<NewIssuesStatistics.Stats> statsCaptor = ArgumentCaptor.forClass(NewIssuesStatistics.Stats.class);
175     verify(newIssuesNotificationMock).setStatistics(eq(PROJECT.getName()), statsCaptor.capture());
176     verify(newIssuesNotificationMock).setDebt(expectedEffort);
177     NewIssuesStatistics.Stats stats = statsCaptor.getValue();
178     assertThat(stats.hasIssues()).isTrue();
179     // just checking all issues have been added to the stats
180     DistributedMetricStatsInt severity = stats.getDistributedMetricStats(NewIssuesStatistics.Metric.RULE_TYPE);
181     assertThat(severity.getOnLeak()).isEqualTo(efforts.length);
182     assertThat(severity.getOffLeak()).isEqualTo(backDatedEfforts.length);
183     assertThat(severity.getTotal()).isEqualTo(backDatedEfforts.length + efforts.length);
184   }
185
186   @Test
187   public void do_not_send_global_new_issues_notification_if_issue_has_been_backdated() {
188     issueCache.newAppender().append(
189       new DefaultIssue().setType(randomRuleType).setEffort(ISSUE_DURATION)
190         .setCreationDate(new Date(ANALYSE_DATE - FIVE_MINUTES_IN_MS)))
191       .close();
192     when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
193
194     underTest.execute();
195
196     verify(notificationService, never()).deliver(any(Notification.class));
197   }
198
199   @Test
200   public void send_global_new_issues_notification_on_branch() throws Exception {
201     ComponentDto branch = setUpProjectWithBranch();
202     issueCache.newAppender().append(
203       new DefaultIssue().setType(randomRuleType).setEffort(ISSUE_DURATION).setCreationDate(new Date(ANALYSE_DATE))).close();
204     when(notificationService.hasProjectSubscribersForTypes(branch.uuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
205     analysisMetadataHolder.setBranch(newBranch());
206
207     underTest.execute();
208
209     verify(notificationService).deliver(newIssuesNotificationMock);
210     verify(newIssuesNotificationMock).setProject(branch.getKey(), branch.uuid(), branch.longName(), BRANCH_NAME);
211     verify(newIssuesNotificationMock).setAnalysisDate(new Date(ANALYSE_DATE));
212     verify(newIssuesNotificationMock).setStatistics(eq(branch.longName()), any(NewIssuesStatistics.Stats.class));
213     verify(newIssuesNotificationMock).setDebt(ISSUE_DURATION);
214   }
215
216   @Test
217   public void do_not_send_global_new_issues_notification_on_branch_if_issue_has_been_backdated() throws Exception {
218     ComponentDto branch = setUpProjectWithBranch();
219     issueCache.newAppender().append(
220       new DefaultIssue().setType(randomRuleType).setEffort(ISSUE_DURATION).setCreationDate(new Date(ANALYSE_DATE - FIVE_MINUTES_IN_MS))).close();
221     when(notificationService.hasProjectSubscribersForTypes(branch.uuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
222     analysisMetadataHolder.setBranch(newBranch());
223
224     underTest.execute();
225
226     verify(notificationService, never()).deliver(any(Notification.class));
227   }
228
229   @Test
230   public void send_new_issues_notification_to_user() throws Exception {
231     issueCache.newAppender().append(
232       new DefaultIssue().setType(randomRuleType).setEffort(ISSUE_DURATION).setAssignee(ISSUE_ASSIGNEE)
233         .setCreationDate(new Date(ANALYSE_DATE)))
234       .close();
235     when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
236
237     underTest.execute();
238
239     verify(notificationService).deliver(newIssuesNotificationMock);
240     verify(notificationService).deliver(myNewIssuesNotificationMock);
241     verify(myNewIssuesNotificationMock).setAssignee(ISSUE_ASSIGNEE);
242     verify(myNewIssuesNotificationMock).setProject(PROJECT.getPublicKey(), PROJECT.getUuid(), PROJECT.getName(), null);
243     verify(myNewIssuesNotificationMock).setAnalysisDate(new Date(ANALYSE_DATE));
244     verify(myNewIssuesNotificationMock).setStatistics(eq(PROJECT.getName()), any(NewIssuesStatistics.Stats.class));
245     verify(myNewIssuesNotificationMock).setDebt(ISSUE_DURATION);
246   }
247
248   @Test
249   public void send_new_issues_notification_to_user_only_for_those_assigned_to_her() {
250     Random random = new Random();
251     Integer[] assigned = IntStream.range(0, 1 + random.nextInt(10)).mapToObj(i -> 10_000 * i).toArray(Integer[]::new);
252     Integer[] assignedToOther = IntStream.range(0, 1 + random.nextInt(10)).mapToObj(i -> 10 + random.nextInt(100)).toArray(Integer[]::new);
253     Duration expectedEffort = Duration.create(Arrays.stream(assigned).mapToInt(i -> i).sum());
254     String assignee = randomAlphanumeric(5);
255     String otherAssignee = randomAlphanumeric(5);
256     List<DefaultIssue> issues = Stream.concat(Arrays.stream(assigned)
257       .map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort))
258         .setAssignee(assignee)
259         .setCreationDate(new Date(ANALYSE_DATE))),
260       Arrays.stream(assignedToOther)
261         .map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort))
262           .setAssignee(otherAssignee)
263           .setCreationDate(new Date(ANALYSE_DATE))))
264       .collect(Collectors.toList());
265     Collections.shuffle(issues);
266     DiskCache<DefaultIssue>.DiskAppender issueCache = this.issueCache.newAppender();
267     issues.forEach(issueCache::append);
268     when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
269     MyNewIssuesNotification myNewIssuesNotificationMock2 = createMyNewIssuesNotificationMock();
270     when(newIssuesNotificationFactory.newMyNewIssuesNotification())
271       .thenReturn(myNewIssuesNotificationMock)
272       .thenReturn(myNewIssuesNotificationMock2);
273
274     underTest.execute();
275
276     verify(notificationService).deliver(newIssuesNotificationMock);
277     verify(notificationService).deliver(myNewIssuesNotificationMock);
278     verify(notificationService).deliver(myNewIssuesNotificationMock2);
279
280     MyNewIssuesNotification effectiveMyNewIssuesNotificationMock = this.myNewIssuesNotificationMock;
281     try {
282       verify(effectiveMyNewIssuesNotificationMock).setAssignee(assignee);
283     } catch (ArgumentsAreDifferent e) {
284       assertThat(e.getMessage())
285         .contains("Wanted:\nmyNewIssuesNotification.setAssignee(\"" + assignee + "\")")
286         .contains("Actual invocation has different arguments:\n" +
287           "myNewIssuesNotification.setAssignee(\"" + otherAssignee + "\")");
288       effectiveMyNewIssuesNotificationMock = myNewIssuesNotificationMock2;
289     }
290     ArgumentCaptor<NewIssuesStatistics.Stats> statsCaptor = ArgumentCaptor.forClass(NewIssuesStatistics.Stats.class);
291     verify(effectiveMyNewIssuesNotificationMock).setStatistics(eq(PROJECT.getName()), statsCaptor.capture());
292     verify(effectiveMyNewIssuesNotificationMock).setDebt(expectedEffort);
293     NewIssuesStatistics.Stats stats = statsCaptor.getValue();
294     assertThat(stats.hasIssues()).isTrue();
295     // just checking all issues have been added to the stats
296     DistributedMetricStatsInt severity = stats.getDistributedMetricStats(NewIssuesStatistics.Metric.RULE_TYPE);
297     assertThat(severity.getOnLeak()).isEqualTo(assigned.length);
298     assertThat(severity.getOffLeak()).isEqualTo(0);
299     assertThat(severity.getTotal()).isEqualTo(assigned.length);
300   }
301
302   @Test
303   public void send_new_issues_notification_to_user_only_for_non_backdated_issues() throws Exception {
304     Random random = new Random();
305     Integer[] efforts = IntStream.range(0, 1 + random.nextInt(10)).mapToObj(i -> 10_000 * i).toArray(Integer[]::new);
306     Integer[] backDatedEfforts = IntStream.range(0, 1 + random.nextInt(10)).mapToObj(i -> 10 + random.nextInt(100)).toArray(Integer[]::new);
307     Duration expectedEffort = Duration.create(Arrays.stream(efforts).mapToInt(i -> i).sum());
308     List<DefaultIssue> issues = Stream.concat(Arrays.stream(efforts)
309       .map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort))
310         .setAssignee(ISSUE_ASSIGNEE)
311         .setCreationDate(new Date(ANALYSE_DATE))),
312       Arrays.stream(backDatedEfforts)
313         .map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort))
314           .setAssignee(ISSUE_ASSIGNEE)
315           .setCreationDate(new Date(ANALYSE_DATE - FIVE_MINUTES_IN_MS))))
316       .collect(Collectors.toList());
317     Collections.shuffle(issues);
318     DiskCache<DefaultIssue>.DiskAppender issueCache = this.issueCache.newAppender();
319     issues.forEach(issueCache::append);
320     when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
321
322     underTest.execute();
323
324     verify(notificationService).deliver(newIssuesNotificationMock);
325     verify(notificationService).deliver(myNewIssuesNotificationMock);
326     verify(myNewIssuesNotificationMock).setAssignee(ISSUE_ASSIGNEE);
327     ArgumentCaptor<NewIssuesStatistics.Stats> statsCaptor = ArgumentCaptor.forClass(NewIssuesStatistics.Stats.class);
328     verify(myNewIssuesNotificationMock).setStatistics(eq(PROJECT.getName()), statsCaptor.capture());
329     verify(myNewIssuesNotificationMock).setDebt(expectedEffort);
330     NewIssuesStatistics.Stats stats = statsCaptor.getValue();
331     assertThat(stats.hasIssues()).isTrue();
332     // just checking all issues have been added to the stats
333     DistributedMetricStatsInt severity = stats.getDistributedMetricStats(NewIssuesStatistics.Metric.RULE_TYPE);
334     assertThat(severity.getOnLeak()).isEqualTo(efforts.length);
335     assertThat(severity.getOffLeak()).isEqualTo(backDatedEfforts.length);
336     assertThat(severity.getTotal()).isEqualTo(backDatedEfforts.length + efforts.length);
337   }
338
339   @Test
340   public void do_not_send_new_issues_notification_to_user_if_issue_is_backdated() throws Exception {
341     issueCache.newAppender().append(
342       new DefaultIssue().setType(randomRuleType).setEffort(ISSUE_DURATION).setAssignee(ISSUE_ASSIGNEE)
343         .setCreationDate(new Date(ANALYSE_DATE - FIVE_MINUTES_IN_MS)))
344       .close();
345     when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
346
347     underTest.execute();
348
349     verify(notificationService, never()).deliver(any(Notification.class));
350   }
351
352   @Test
353   public void send_issues_change_notification() throws Exception {
354     sendIssueChangeNotification(ANALYSE_DATE);
355   }
356
357   @Test
358   public void send_issues_change_notification_even_if_issue_is_backdated() throws Exception {
359     sendIssueChangeNotification(ANALYSE_DATE - FIVE_MINUTES_IN_MS);
360   }
361
362   private void sendIssueChangeNotification(long issueCreatedAt) {
363     ComponentDto project = newPrivateProjectDto(newOrganizationDto()).setDbKey(PROJECT.getKey()).setLongName(PROJECT.getName());
364     ComponentDto file = newFileDto(project).setDbKey(FILE.getKey()).setLongName(FILE.getName());
365     RuleDefinitionDto ruleDefinitionDto = newRule();
366     DefaultIssue issue = newIssue(ruleDefinitionDto, project, file).toDefaultIssue()
367       .setNew(false).setChanged(true).setSendNotifications(true).setCreationDate(new Date(issueCreatedAt));
368     ruleRepository.add(ruleDefinitionDto.getKey()).setName(ruleDefinitionDto.getName());
369     issueCache.newAppender().append(issue).close();
370     when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
371
372     underTest.execute();
373
374     ArgumentCaptor<IssueChangeNotification> issueChangeNotificationCaptor = ArgumentCaptor.forClass(IssueChangeNotification.class);
375     verify(notificationService).deliver(issueChangeNotificationCaptor.capture());
376     IssueChangeNotification issueChangeNotification = issueChangeNotificationCaptor.getValue();
377     assertThat(issueChangeNotification.getFieldValue("key")).isEqualTo(issue.key());
378     assertThat(issueChangeNotification.getFieldValue("assignee")).isEqualTo(issue.assignee());
379     assertThat(issueChangeNotification.getFieldValue("message")).isEqualTo(issue.message());
380     assertThat(issueChangeNotification.getFieldValue("ruleName")).isEqualTo(ruleDefinitionDto.getName());
381     assertThat(issueChangeNotification.getFieldValue("projectName")).isEqualTo(project.longName());
382     assertThat(issueChangeNotification.getFieldValue("projectKey")).isEqualTo(project.getKey());
383     assertThat(issueChangeNotification.getFieldValue("componentKey")).isEqualTo(file.getKey());
384     assertThat(issueChangeNotification.getFieldValue("componentName")).isEqualTo(file.longName());
385   }
386
387   @Test
388   public void send_issues_change_notification_on_branch() throws Exception {
389     sendIssueChangeNotificationOnBranch(ANALYSE_DATE);
390   }
391
392   @Test
393   public void send_issues_change_notification_on_branch_even_if_issue_is_backdated() throws Exception {
394     sendIssueChangeNotificationOnBranch(ANALYSE_DATE - FIVE_MINUTES_IN_MS);
395   }
396
397   private void sendIssueChangeNotificationOnBranch(long issueCreatedAt) {
398     ComponentDto project = newPrivateProjectDto(newOrganizationDto());
399     ComponentDto branch = newProjectBranch(project, newBranchDto(project).setKey(BRANCH_NAME));
400     ComponentDto file = newFileDto(branch);
401     treeRootHolder.setRoot(builder(Type.PROJECT, 2).setKey(branch.getDbKey()).setPublicKey(branch.getKey()).setName(branch.longName()).setUuid(branch.uuid()).addChildren(
402       builder(Type.FILE, 11).setKey(file.getDbKey()).setPublicKey(file.getKey()).setName(file.longName()).build()).build());
403     RuleDefinitionDto ruleDefinitionDto = newRule();
404     DefaultIssue issue = newIssue(ruleDefinitionDto, branch, file).toDefaultIssue()
405       .setNew(false)
406       .setChanged(true)
407       .setSendNotifications(true)
408       .setCreationDate(new Date(issueCreatedAt));
409     ruleRepository.add(ruleDefinitionDto.getKey()).setName(ruleDefinitionDto.getName());
410     issueCache.newAppender().append(issue).close();
411     when(notificationService.hasProjectSubscribersForTypes(branch.uuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
412     analysisMetadataHolder.setBranch(newBranch());
413
414     underTest.execute();
415
416     ArgumentCaptor<IssueChangeNotification> issueChangeNotificationCaptor = ArgumentCaptor.forClass(IssueChangeNotification.class);
417     verify(notificationService).deliver(issueChangeNotificationCaptor.capture());
418     IssueChangeNotification issueChangeNotification = issueChangeNotificationCaptor.getValue();
419     assertThat(issueChangeNotification.getFieldValue("projectName")).isEqualTo(branch.longName());
420     assertThat(issueChangeNotification.getFieldValue("projectKey")).isEqualTo(branch.getKey());
421     assertThat(issueChangeNotification.getFieldValue("branch")).isEqualTo(BRANCH_NAME);
422     assertThat(issueChangeNotification.getFieldValue("componentKey")).isEqualTo(file.getKey());
423     assertThat(issueChangeNotification.getFieldValue("componentName")).isEqualTo(file.longName());
424   }
425
426   private NewIssuesNotification createNewIssuesNotificationMock() {
427     NewIssuesNotification notification = mock(NewIssuesNotification.class);
428     when(notification.setProject(anyString(), anyString(), anyString(), anyString())).thenReturn(notification);
429     when(notification.setProjectVersion(anyString())).thenReturn(notification);
430     when(notification.setAnalysisDate(any(Date.class))).thenReturn(notification);
431     when(notification.setStatistics(anyString(), any(NewIssuesStatistics.Stats.class))).thenReturn(notification);
432     when(notification.setDebt(any(Duration.class))).thenReturn(notification);
433     return notification;
434   }
435
436   private MyNewIssuesNotification createMyNewIssuesNotificationMock() {
437     MyNewIssuesNotification notification = mock(MyNewIssuesNotification.class);
438     when(notification.setAssignee(anyString())).thenReturn(notification);
439     when(notification.setProject(anyString(), anyString(), anyString(), anyString())).thenReturn(notification);
440     when(notification.setProjectVersion(anyString())).thenReturn(notification);
441     when(notification.setAnalysisDate(any(Date.class))).thenReturn(notification);
442     when(notification.setStatistics(anyString(), any(NewIssuesStatistics.Stats.class))).thenReturn(notification);
443     when(notification.setDebt(any(Duration.class))).thenReturn(notification);
444     return notification;
445   }
446
447   private static Branch newBranch() {
448     Branch branch = mock(Branch.class);
449     when(branch.isMain()).thenReturn(false);
450     when(branch.getName()).thenReturn(BRANCH_NAME);
451     return branch;
452   }
453
454   private ComponentDto setUpProjectWithBranch() {
455     ComponentDto project = newPrivateProjectDto(newOrganizationDto());
456     ComponentDto branch = newProjectBranch(project, newBranchDto(project).setKey(BRANCH_NAME));
457     ComponentDto file = newFileDto(branch);
458     treeRootHolder.setRoot(builder(Type.PROJECT, 2).setKey(branch.getDbKey()).setPublicKey(branch.getKey()).setName(branch.longName()).setUuid(branch.uuid()).addChildren(
459       builder(Type.FILE, 11).setKey(file.getDbKey()).setPublicKey(file.getKey()).setName(file.longName()).build()).build());
460     return branch;
461   }
462
463   @Override
464   protected ComputationStep step() {
465     return underTest;
466   }
467 }