import java.util.Map;
import java.util.Set;
import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.util.CloseableIterator;
import org.sonar.server.computation.batch.BatchReportReader;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.TreeRootHolder;
import org.sonar.server.issue.notification.NewIssuesNotificationFactory;
import org.sonar.server.issue.notification.NewIssuesStatistics;
import org.sonar.server.notification.NotificationService;
-import org.sonar.core.util.CloseableIterator;
/**
* Reads issues from disk cache and send related notifications. For performance reasons,
private void doExecute(Component project) {
NewIssuesStatistics newIssuesStats = new NewIssuesStatistics();
CloseableIterator<DefaultIssue> issues = issueCache.traverse();
- String projectName = reportReader.readComponent(reportReader.readMetadata().getRootComponentRef()).getName();
try {
- while (issues.hasNext()) {
- DefaultIssue issue = issues.next();
- if (issue.isNew() && issue.resolution() == null) {
- newIssuesStats.add(issue);
- } else if (issue.isChanged() && issue.mustSendNotifications()) {
- IssueChangeNotification changeNotification = new IssueChangeNotification();
- changeNotification.setRuleName(rules.getByKey(issue.ruleKey()).getName());
- changeNotification.setIssue(issue);
- changeNotification.setProject(project.getKey(), projectName);
- service.deliver(changeNotification);
- }
- }
-
+ processIssues(newIssuesStats, issues, project);
} finally {
issues.close();
}
- sendNewIssuesStatistics(newIssuesStats, project, projectName);
+ if (newIssuesStats.hasIssues()) {
+ long analysisDate = reportReader.readMetadata().getAnalysisDate();
+ sendNewIssuesNotification(newIssuesStats, project, analysisDate);
+ sendNewIssuesNotificationToAssignees(newIssuesStats, project, analysisDate);
+ }
}
- private void sendNewIssuesStatistics(NewIssuesStatistics statistics, Component project, String projectName) {
- if (statistics.hasIssues()) {
- NewIssuesStatistics.Stats globalStatistics = statistics.globalStatistics();
- long analysisDate = reportReader.readMetadata().getAnalysisDate();
- NewIssuesNotification notification = newIssuesNotificationFactory
- .newNewIssuesNotication()
- .setProject(project.getKey(), project.getUuid(), projectName)
- .setAnalysisDate(new Date(analysisDate))
- .setStatistics(projectName, globalStatistics)
- .setDebt(globalStatistics.debt());
- service.deliver(notification);
+ private void processIssues(NewIssuesStatistics newIssuesStats, CloseableIterator<DefaultIssue> issues, Component project) {
+ while (issues.hasNext()) {
+ DefaultIssue issue = issues.next();
+ if (issue.isNew() && issue.resolution() == null) {
+ newIssuesStats.add(issue);
+ } else if (issue.isChanged() && issue.mustSendNotifications()) {
+ sendIssueChangeNotification(issue, project);
+ }
+ }
+ }
- // send email to each user having issues
- for (Map.Entry<String, NewIssuesStatistics.Stats> assigneeAndStatisticsTuple : statistics.assigneesStatistics().entrySet()) {
- String assignee = assigneeAndStatisticsTuple.getKey();
- NewIssuesStatistics.Stats assigneeStatistics = assigneeAndStatisticsTuple.getValue();
- MyNewIssuesNotification myNewIssuesNotification = newIssuesNotificationFactory
- .newMyNewIssuesNotification()
- .setAssignee(assignee);
- myNewIssuesNotification
- .setProject(project.getKey(), project.getUuid(), projectName)
- .setAnalysisDate(new Date(analysisDate))
- .setStatistics(projectName, assigneeStatistics)
- .setDebt(assigneeStatistics.debt());
+ private void sendIssueChangeNotification(DefaultIssue issue, Component project) {
+ IssueChangeNotification changeNotification = new IssueChangeNotification();
+ changeNotification.setRuleName(rules.getByKey(issue.ruleKey()).getName());
+ changeNotification.setIssue(issue);
+ changeNotification.setProject(project.getKey(), project.getName());
+ service.deliver(changeNotification);
+ }
- service.deliver(myNewIssuesNotification);
- }
+ private void sendNewIssuesNotification(NewIssuesStatistics statistics, Component project, long analysisDate) {
+ NewIssuesStatistics.Stats globalStatistics = statistics.globalStatistics();
+ NewIssuesNotification notification = newIssuesNotificationFactory
+ .newNewIssuesNotication()
+ .setProject(project.getKey(), project.getUuid(), project.getName())
+ .setAnalysisDate(new Date(analysisDate))
+ .setStatistics(project.getName(), globalStatistics)
+ .setDebt(globalStatistics.debt());
+ service.deliver(notification);
+ }
+
+ private void sendNewIssuesNotificationToAssignees(NewIssuesStatistics statistics, Component project, long analysisDate) {
+ // send email to each user having issues
+ for (Map.Entry<String, NewIssuesStatistics.Stats> assigneeAndStatisticsTuple : statistics.assigneesStatistics().entrySet()) {
+ String assignee = assigneeAndStatisticsTuple.getKey();
+ NewIssuesStatistics.Stats assigneeStatistics = assigneeAndStatisticsTuple.getValue();
+ MyNewIssuesNotification myNewIssuesNotification = newIssuesNotificationFactory
+ .newMyNewIssuesNotification()
+ .setAssignee(assignee);
+ myNewIssuesNotification
+ .setProject(project.getKey(), project.getUuid(), project.getName())
+ .setAnalysisDate(new Date(analysisDate))
+ .setStatistics(project.getName(), assigneeStatistics)
+ .setDebt(assigneeStatistics.debt());
+
+ service.deliver(myNewIssuesNotification);
}
}
*/
package org.sonar.server.computation.step;
+import java.util.Date;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-import org.mockito.Mockito;
import org.sonar.api.notifications.Notification;
import org.sonar.api.rule.Severity;
+import org.sonar.api.utils.Duration;
import org.sonar.api.utils.System2;
-import org.sonar.batch.protocol.Constants;
import org.sonar.batch.protocol.output.BatchReport;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.server.computation.batch.BatchReportReaderRule;
import org.sonar.server.computation.batch.TreeRootHolderRule;
import org.sonar.server.computation.component.Component;
-import org.sonar.server.computation.component.ReportComponent;
+import org.sonar.server.computation.component.Component.Type;
import org.sonar.server.computation.issue.IssueCache;
import org.sonar.server.computation.issue.RuleRepository;
import org.sonar.server.issue.notification.IssueChangeNotification;
+import org.sonar.server.issue.notification.MyNewIssuesNotification;
import org.sonar.server.issue.notification.NewIssuesNotification;
import org.sonar.server.issue.notification.NewIssuesNotificationFactory;
+import org.sonar.server.issue.notification.NewIssuesStatistics;
import org.sonar.server.notification.NotificationService;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.sonar.server.computation.component.ReportComponent.builder;
public class SendIssueNotificationsStepTest extends BaseStepTest {
- private static final String PROJECT_UUID = "PROJECT_UUID";
- private static final String PROJECT_KEY = "PROJECT_KEY";
+ static final String PROJECT_UUID = "PROJECT_UUID";
+ static final String PROJECT_KEY = "PROJECT_KEY";
+ static final String PROJECT_NAME = "PROJECT_NAME";
+
+ static final long ANALYSE_DATE = 123L;
+
+ static final Duration ISSUE_DURATION = Duration.create(100L);
+ static final String ISSUE_ASSIGNEE = "John";
+
+ static final Component PROJECT = builder(Type.PROJECT, 1).setUuid(PROJECT_UUID).setKey(PROJECT_KEY).setName(PROJECT_NAME).build();
@Rule
public BatchReportReaderRule reportReader = new BatchReportReaderRule();
@Rule
- public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule()
+ .setRoot(PROJECT);
@Rule
public TemporaryFolder temp = new TemporaryFolder();
- NotificationService notifService = mock(NotificationService.class);
+ NotificationService notificationService = mock(NotificationService.class);
+ NewIssuesNotificationFactory newIssuesNotificationFactory = mock(NewIssuesNotificationFactory.class);
+ NewIssuesNotification newIssuesNotificationMock = createNewIssuesNotificationMock();
+ MyNewIssuesNotification myNewIssuesNotificationMock = createMyNewIssuesNotificationMock();
+
IssueCache issueCache;
SendIssueNotificationsStep underTest;
@Before
public void setUp() throws Exception {
issueCache = new IssueCache(temp.newFile(), System2.INSTANCE);
- NewIssuesNotificationFactory newIssuesNotificationFactory = mock(NewIssuesNotificationFactory.class, Mockito.RETURNS_DEEP_STUBS);
- underTest = new SendIssueNotificationsStep(issueCache, mock(RuleRepository.class), treeRootHolder, notifService, reportReader, newIssuesNotificationFactory);
+ underTest = new SendIssueNotificationsStep(issueCache, mock(RuleRepository.class), treeRootHolder, notificationService, reportReader, newIssuesNotificationFactory);
- treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid(PROJECT_UUID).setKey(PROJECT_KEY).build());
+ when(newIssuesNotificationFactory.newNewIssuesNotication()).thenReturn(newIssuesNotificationMock);
+ when(newIssuesNotificationFactory.newMyNewIssuesNotification()).thenReturn(myNewIssuesNotificationMock);
reportReader.setMetadata(BatchReport.Metadata.newBuilder()
.setRootComponentRef(1)
- .build());
- reportReader.putComponent(BatchReport.Component.newBuilder()
- .setRef(1)
- .setType(Constants.ComponentType.PROJECT)
- .setKey(PROJECT_KEY)
- .setName("Project name")
+ .setAnalysisDate(ANALYSE_DATE)
.build());
}
@Test
public void do_not_send_notifications_if_no_subscribers() {
- when(notifService.hasProjectSubscribersForTypes(PROJECT_UUID, SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(false);
+ when(notificationService.hasProjectSubscribersForTypes(PROJECT_UUID, SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(false);
+
+ underTest.execute();
+
+ verify(notificationService, never()).deliver(any(Notification.class));
+ }
+
+ @Test
+ public void send_global_new_issues_notification() throws Exception {
+ issueCache.newAppender().append(
+ new DefaultIssue().setSeverity(Severity.BLOCKER).setDebt(ISSUE_DURATION)
+ ).close();
+
+ when(notificationService.hasProjectSubscribersForTypes(PROJECT_UUID, SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
underTest.execute();
- verify(notifService, never()).deliver(any(Notification.class));
+ verify(notificationService).deliver(any(NewIssuesNotification.class));
+ verify(newIssuesNotificationMock).setProject(PROJECT_KEY, PROJECT_UUID, PROJECT_NAME);
+ verify(newIssuesNotificationMock).setAnalysisDate(new Date(ANALYSE_DATE));
+ verify(newIssuesNotificationMock).setStatistics(eq(PROJECT_NAME), any(NewIssuesStatistics.Stats.class));
+ verify(newIssuesNotificationMock).setDebt(ISSUE_DURATION);
}
@Test
- public void send_notifications_if_subscribers() {
- issueCache.newAppender().append(new DefaultIssue()
- .setSeverity(Severity.BLOCKER)).close();
+ public void send_new_issues_notification_to_user() throws Exception {
+ issueCache.newAppender().append(
+ new DefaultIssue().setSeverity(Severity.BLOCKER).setDebt(ISSUE_DURATION).setAssignee(ISSUE_ASSIGNEE)
+ ).close();
- when(notifService.hasProjectSubscribersForTypes(PROJECT_UUID, SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
+ when(notificationService.hasProjectSubscribersForTypes(PROJECT_UUID, SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
underTest.execute();
- verify(notifService).deliver(any(NewIssuesNotification.class));
- verify(notifService, atLeastOnce()).deliver(any(IssueChangeNotification.class));
+ verify(notificationService, times(2)).deliver(any(Notification.class));
+ verify(myNewIssuesNotificationMock).setAssignee(ISSUE_ASSIGNEE);
+ verify(myNewIssuesNotificationMock).setProject(PROJECT_KEY, PROJECT_UUID, PROJECT_NAME);
+ verify(myNewIssuesNotificationMock).setAnalysisDate(new Date(ANALYSE_DATE));
+ verify(myNewIssuesNotificationMock).setStatistics(eq(PROJECT_NAME), any(NewIssuesStatistics.Stats.class));
+ verify(myNewIssuesNotificationMock).setDebt(ISSUE_DURATION);
+ }
+
+ @Test
+ public void send_issues_change_notification() throws Exception {
+ DefaultIssue issue = new DefaultIssue().setSeverity(Severity.BLOCKER).setDebt(ISSUE_DURATION).setChanged(true).setSendNotifications(true);
+ issueCache.newAppender().append(issue).close();
+
+ when(notificationService.hasProjectSubscribersForTypes(PROJECT_UUID, SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
+
+ underTest.execute();
+
+ verify(notificationService).deliver(any(IssueChangeNotification.class));
+ }
+
+ private NewIssuesNotification createNewIssuesNotificationMock() {
+ NewIssuesNotification notification = mock(NewIssuesNotification.class);
+ when(notification.setProject(anyString(), anyString(), anyString())).thenReturn(notification);
+ when(notification.setAnalysisDate(any(Date.class))).thenReturn(notification);
+ when(notification.setStatistics(anyString(), any(NewIssuesStatistics.Stats.class))).thenReturn(notification);
+ when(notification.setDebt(any(Duration.class))).thenReturn(notification);
+ return notification;
+ }
+
+ private MyNewIssuesNotification createMyNewIssuesNotificationMock() {
+ MyNewIssuesNotification notification = mock(MyNewIssuesNotification.class);
+ when(notification.setAssignee(anyString())).thenReturn(notification);
+ when(notification.setProject(anyString(), anyString(), anyString())).thenReturn(notification);
+ when(notification.setAnalysisDate(any(Date.class))).thenReturn(notification);
+ when(notification.setStatistics(anyString(), any(NewIssuesStatistics.Stats.class))).thenReturn(notification);
+ when(notification.setDebt(any(Duration.class))).thenReturn(notification);
+ return notification;
}
@Override