3 * Copyright (C) 2009-2017 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.server.computation.task.projectanalysis.step;
22 import java.util.Date;
23 import org.junit.Before;
24 import org.junit.Rule;
25 import org.junit.Test;
26 import org.junit.rules.TemporaryFolder;
27 import org.mockito.ArgumentCaptor;
28 import org.sonar.api.notifications.Notification;
29 import org.sonar.api.rule.Severity;
30 import org.sonar.api.utils.Duration;
31 import org.sonar.api.utils.System2;
32 import org.sonar.core.issue.DefaultIssue;
33 import org.sonar.db.component.ComponentDto;
34 import org.sonar.db.rule.RuleDefinitionDto;
35 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
36 import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
37 import org.sonar.server.computation.task.projectanalysis.component.Component;
38 import org.sonar.server.computation.task.projectanalysis.component.Component.Type;
39 import org.sonar.server.computation.task.projectanalysis.component.DefaultBranchImpl;
40 import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
41 import org.sonar.server.computation.task.projectanalysis.issue.IssueCache;
42 import org.sonar.server.computation.task.projectanalysis.issue.RuleRepositoryRule;
43 import org.sonar.server.computation.task.step.ComputationStep;
44 import org.sonar.server.issue.notification.IssueChangeNotification;
45 import org.sonar.server.issue.notification.MyNewIssuesNotification;
46 import org.sonar.server.issue.notification.NewIssuesNotification;
47 import org.sonar.server.issue.notification.NewIssuesNotificationFactory;
48 import org.sonar.server.issue.notification.NewIssuesStatistics;
49 import org.sonar.server.notification.NotificationService;
51 import static org.assertj.core.api.Java6Assertions.assertThat;
52 import static org.mockito.Matchers.anyString;
53 import static org.mockito.Matchers.eq;
54 import static org.mockito.Mockito.any;
55 import static org.mockito.Mockito.mock;
56 import static org.mockito.Mockito.never;
57 import static org.mockito.Mockito.times;
58 import static org.mockito.Mockito.verify;
59 import static org.mockito.Mockito.when;
60 import static org.sonar.db.component.ComponentTesting.newBranchDto;
61 import static org.sonar.db.component.ComponentTesting.newFileDto;
62 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
63 import static org.sonar.db.component.ComponentTesting.newProjectBranch;
64 import static org.sonar.db.issue.IssueTesting.newIssue;
65 import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
66 import static org.sonar.db.rule.RuleTesting.newRule;
67 import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder;
69 public class SendIssueNotificationsStepTest extends BaseStepTest {
71 private static final String BRANCH_NAME = "feature";
73 private static final long ANALYSE_DATE = 123L;
75 private static final Duration ISSUE_DURATION = Duration.create(100L);
76 private static final String ISSUE_ASSIGNEE = "John";
78 private static final Component FILE = builder(Component.Type.FILE, 11).build();
79 private static final Component PROJECT = builder(Type.PROJECT, 1).addChildren(FILE).build();
82 public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule()
86 public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule()
87 .setBranch(new DefaultBranchImpl())
88 .setAnalysisDate(new Date(ANALYSE_DATE));
91 public RuleRepositoryRule ruleRepository = new RuleRepositoryRule();
94 public TemporaryFolder temp = new TemporaryFolder();
96 private NotificationService notificationService = mock(NotificationService.class);
97 private NewIssuesNotificationFactory newIssuesNotificationFactory = mock(NewIssuesNotificationFactory.class);
98 private NewIssuesNotification newIssuesNotificationMock = createNewIssuesNotificationMock();
99 private MyNewIssuesNotification myNewIssuesNotificationMock = createMyNewIssuesNotificationMock();
101 private IssueCache issueCache;
102 private SendIssueNotificationsStep underTest;
105 public void setUp() throws Exception {
106 issueCache = new IssueCache(temp.newFile(), System2.INSTANCE);
107 underTest = new SendIssueNotificationsStep(issueCache, ruleRepository, treeRootHolder, notificationService, analysisMetadataHolder,
108 newIssuesNotificationFactory);
110 when(newIssuesNotificationFactory.newNewIssuesNotication()).thenReturn(newIssuesNotificationMock);
111 when(newIssuesNotificationFactory.newMyNewIssuesNotification()).thenReturn(myNewIssuesNotificationMock);
115 public void do_not_send_notifications_if_no_subscribers() {
116 when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(false);
120 verify(notificationService, never()).deliver(any(Notification.class));
124 public void send_global_new_issues_notification() throws Exception {
125 issueCache.newAppender().append(
126 new DefaultIssue().setSeverity(Severity.BLOCKER).setEffort(ISSUE_DURATION)).close();
128 when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
132 verify(notificationService).deliver(any(NewIssuesNotification.class));
133 verify(newIssuesNotificationMock).setProject(PROJECT.getPublicKey(), PROJECT.getUuid(), PROJECT.getName(), null);
134 verify(newIssuesNotificationMock).setAnalysisDate(new Date(ANALYSE_DATE));
135 verify(newIssuesNotificationMock).setStatistics(eq(PROJECT.getName()), any(NewIssuesStatistics.Stats.class));
136 verify(newIssuesNotificationMock).setDebt(ISSUE_DURATION);
140 public void send_global_new_issues_notification_on_branch() throws Exception {
141 ComponentDto project = newPrivateProjectDto(newOrganizationDto());
142 ComponentDto branch = newProjectBranch(project, newBranchDto(project).setKey(BRANCH_NAME));
143 ComponentDto file = newFileDto(branch);
144 treeRootHolder.setRoot(builder(Type.PROJECT, 2).setKey(branch.getDbKey()).setPublicKey(branch.getKey()).setName(branch.longName()).setUuid(branch.uuid()).addChildren(
145 builder(Component.Type.FILE, 11).setKey(file.getDbKey()).setPublicKey(file.getKey()).setName(file.longName()).build()
147 issueCache.newAppender().append(
148 new DefaultIssue().setSeverity(Severity.BLOCKER).setEffort(ISSUE_DURATION)).close();
150 when(notificationService.hasProjectSubscribersForTypes(branch.uuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
151 analysisMetadataHolder.setBranch(newBranch());
155 verify(notificationService).deliver(any(NewIssuesNotification.class));
156 verify(newIssuesNotificationMock).setProject(branch.getKey(), branch.uuid(), branch.longName(), BRANCH_NAME);
157 verify(newIssuesNotificationMock).setAnalysisDate(new Date(ANALYSE_DATE));
158 verify(newIssuesNotificationMock).setStatistics(eq(branch.longName()), any(NewIssuesStatistics.Stats.class));
159 verify(newIssuesNotificationMock).setDebt(ISSUE_DURATION);
163 public void send_new_issues_notification_to_user() throws Exception {
164 issueCache.newAppender().append(
165 new DefaultIssue().setSeverity(Severity.BLOCKER).setEffort(ISSUE_DURATION).setAssignee(ISSUE_ASSIGNEE)).close();
167 when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
171 verify(notificationService, times(2)).deliver(any(Notification.class));
172 verify(myNewIssuesNotificationMock).setAssignee(ISSUE_ASSIGNEE);
173 verify(myNewIssuesNotificationMock).setProject(PROJECT.getPublicKey(), PROJECT.getUuid(), PROJECT.getName(), null);
174 verify(myNewIssuesNotificationMock).setAnalysisDate(new Date(ANALYSE_DATE));
175 verify(myNewIssuesNotificationMock).setStatistics(eq(PROJECT.getName()), any(NewIssuesStatistics.Stats.class));
176 verify(myNewIssuesNotificationMock).setDebt(ISSUE_DURATION);
180 public void send_issues_change_notification() throws Exception {
181 ComponentDto project = newPrivateProjectDto(newOrganizationDto()).setDbKey(PROJECT.getKey()).setLongName(PROJECT.getName());
182 ComponentDto file = newFileDto(project).setDbKey(FILE.getKey()).setLongName(FILE.getName());
183 RuleDefinitionDto ruleDefinitionDto = newRule();
184 DefaultIssue issue = newIssue(ruleDefinitionDto, project, file).toDefaultIssue()
187 .setSendNotifications(true);
188 ruleRepository.add(ruleDefinitionDto.getKey()).setName(ruleDefinitionDto.getName());
189 issueCache.newAppender().append(issue).close();
190 when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
194 ArgumentCaptor<IssueChangeNotification> issueChangeNotificationCaptor = ArgumentCaptor.forClass(IssueChangeNotification.class);
195 verify(notificationService).deliver(issueChangeNotificationCaptor.capture());
196 assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("key")).isEqualTo(issue.key());
197 assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("assignee")).isEqualTo(issue.assignee());
198 assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("message")).isEqualTo(issue.message());
199 assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("ruleName")).isEqualTo(ruleDefinitionDto.getName());
200 assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("projectName")).isEqualTo(project.longName());
201 assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("projectKey")).isEqualTo(project.getKey());
202 assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("componentKey")).isEqualTo(file.getKey());
203 assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("componentName")).isEqualTo(file.longName());
207 public void send_issues_change_notification_on_branch() throws Exception {
208 ComponentDto project = newPrivateProjectDto(newOrganizationDto());
209 ComponentDto branch = newProjectBranch(project, newBranchDto(project).setKey(BRANCH_NAME));
210 ComponentDto file = newFileDto(branch);
211 treeRootHolder.setRoot(builder(Type.PROJECT, 2).setKey(branch.getDbKey()).setPublicKey(branch.getKey()).setName(branch.longName()).setUuid(branch.uuid()).addChildren(
212 builder(Component.Type.FILE, 11).setKey(file.getDbKey()).setPublicKey(file.getKey()).setName(file.longName()).build()
214 RuleDefinitionDto ruleDefinitionDto = newRule();
215 DefaultIssue issue = newIssue(ruleDefinitionDto, branch, file).toDefaultIssue()
218 .setSendNotifications(true);
219 ruleRepository.add(ruleDefinitionDto.getKey()).setName(ruleDefinitionDto.getName());
220 issueCache.newAppender().append(issue).close();
221 when(notificationService.hasProjectSubscribersForTypes(branch.uuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
222 analysisMetadataHolder.setBranch(newBranch());
226 ArgumentCaptor<IssueChangeNotification> issueChangeNotificationCaptor = ArgumentCaptor.forClass(IssueChangeNotification.class);
227 verify(notificationService).deliver(issueChangeNotificationCaptor.capture());
228 assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("projectName")).isEqualTo(branch.longName());
229 assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("projectKey")).isEqualTo(branch.getKey());
230 assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("branch")).isEqualTo(BRANCH_NAME);
231 assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("componentKey")).isEqualTo(file.getKey());
232 assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("componentName")).isEqualTo(file.longName());
235 private NewIssuesNotification createNewIssuesNotificationMock() {
236 NewIssuesNotification notification = mock(NewIssuesNotification.class);
237 when(notification.setProject(anyString(), anyString(), anyString(), anyString())).thenReturn(notification);
238 when(notification.setAnalysisDate(any(Date.class))).thenReturn(notification);
239 when(notification.setStatistics(anyString(), any(NewIssuesStatistics.Stats.class))).thenReturn(notification);
240 when(notification.setDebt(any(Duration.class))).thenReturn(notification);
244 private MyNewIssuesNotification createMyNewIssuesNotificationMock() {
245 MyNewIssuesNotification notification = mock(MyNewIssuesNotification.class);
246 when(notification.setAssignee(anyString())).thenReturn(notification);
247 when(notification.setProject(anyString(), anyString(), anyString(), anyString())).thenReturn(notification);
248 when(notification.setAnalysisDate(any(Date.class))).thenReturn(notification);
249 when(notification.setStatistics(anyString(), any(NewIssuesStatistics.Stats.class))).thenReturn(notification);
250 when(notification.setDebt(any(Duration.class))).thenReturn(notification);
254 private static Branch newBranch() {
255 Branch branch = mock(Branch.class);
256 when(branch.isMain()).thenReturn(false);
257 when(branch.getName()).thenReturn(BRANCH_NAME);
262 protected ComputationStep step() {