]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9616 Support notifications on branches (#2413)
authorJanos Gyerik <janos.gyerik@sonarsource.com>
Thu, 24 Aug 2017 12:20:51 +0000 (14:20 +0200)
committerJanos Gyerik <janos.gyerik@sonarsource.com>
Tue, 12 Sep 2017 09:34:49 +0000 (11:34 +0200)
19 files changed:
server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/SendIssueNotificationsStep.java
server/sonar-server/src/main/java/org/sonar/server/issue/IssueUpdater.java
server/sonar-server/src/main/java/org/sonar/server/issue/notification/AbstractNewIssuesEmailTemplate.java
server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangeNotification.java
server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangesEmailTemplate.java
server/sonar-server/src/main/java/org/sonar/server/issue/notification/MyNewIssuesEmailTemplate.java
server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStepTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/IssueUpdaterTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangeNotificationTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangesEmailTemplateTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/notification/MyNewIssuesEmailTemplateTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesEmailTemplateTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/BulkChangeActionTest.java
server/sonar-server/src/test/resources/org/sonar/server/issue/notification/IssueChangesEmailTemplateTest/email_with_issue_on_branch.txt [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/issue/notification/MyNewIssuesEmailTemplateTest/email_with_issue_on_branch.txt [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/issue/notification/NewIssuesEmailTemplateTest/email_with_issue_on_branch.txt [new file with mode: 0644]

index a30f402448cdac260853984bdddc11e806cc38d9..c61e9363f900aff3c267f0d2eaa6432a15c064a4 100644 (file)
@@ -83,6 +83,7 @@
     from projects p
     where
       p.enabled=${_true}
+      and p.main_branch_project_uuid is null
       and p.kee in
       <foreach collection="keys" open="(" close=")" item="key" separator=",">
         #{key,jdbcType=VARCHAR}
index d39e3c4021a7b4ad7d57800a4ea0d586c39f10de..5a042baf5747462c7f0222c4f2ef50297246700c 100644 (file)
@@ -25,6 +25,7 @@ import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.notifications.Notification;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
+import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
 import org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor;
 import org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit;
@@ -52,15 +53,17 @@ public class QualityGateEventsStep implements ComputationStep {
   private final MeasureRepository measureRepository;
   private final EventRepository eventRepository;
   private final NotificationService notificationService;
+  private final AnalysisMetadataHolder analysisMetadataHolder;
 
   public QualityGateEventsStep(TreeRootHolder treeRootHolder,
     MetricRepository metricRepository, MeasureRepository measureRepository, EventRepository eventRepository,
-    NotificationService notificationService) {
+    NotificationService notificationService, AnalysisMetadataHolder analysisMetadataHolder) {
     this.treeRootHolder = treeRootHolder;
     this.metricRepository = metricRepository;
     this.measureRepository = measureRepository;
     this.eventRepository = eventRepository;
     this.notificationService = notificationService;
+    this.analysisMetadataHolder = analysisMetadataHolder;
   }
 
   @Override
@@ -129,6 +132,7 @@ public class QualityGateEventsStep implements ComputationStep {
       .setFieldValue("alertText", rawStatus.getText())
       .setFieldValue("alertLevel", rawStatus.getStatus().toString())
       .setFieldValue("isNewAlert", Boolean.toString(isNewAlert));
+    analysisMetadataHolder.getBranch().ifPresent(branch -> notification.setFieldValue("branch", branch.getName().orElse(null)));
     notificationService.deliver(notification);
   }
 
index 8e8360ac6691611bff9051c43adb97a6f2a64926..1688932db8d6770374a37fe1134b213b4bf5883d 100644 (file)
@@ -26,6 +26,7 @@ import java.util.Set;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.util.CloseableIterator;
 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
+import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
 import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder;
 import org.sonar.server.computation.task.projectanalysis.issue.IssueCache;
@@ -105,7 +106,8 @@ public class SendIssueNotificationsStep implements ComputationStep {
     IssueChangeNotification changeNotification = new IssueChangeNotification();
     changeNotification.setRuleName(rules.getByKey(issue.ruleKey()).getName());
     changeNotification.setIssue(issue);
-    changeNotification.setProject(project.getKey(), project.getName());
+    String branchName = analysisMetadataHolder.getBranch().flatMap(Branch::getName).orElse(null);
+    changeNotification.setProject(project.getKey(), project.getName(), branchName);
     service.deliver(changeNotification);
   }
 
index b10635e43541781b281830e10fa852507d5c9194..a368b01ca7a3b98206eb3e6e2c6d6120dac1d616 100644 (file)
@@ -80,7 +80,7 @@ public class IssueUpdater {
       .setIssue(issue)
       .setChangeAuthorLogin(context.login())
       .setRuleName(rule.map(RuleDefinitionDto::getName).orElse(null))
-      .setProject(project.getDbKey(), project.name())
+      .setProject(project.getKey(), project.name(), project.getBranch())
       .setComponent(component)
       .setComment(comment));
     return issueDto;
index 09c6611dcfe72d098362f97949b095cf1b7f4916..caecaa2851e9a1da5f4b53d3ae82a1f7a6378289 100644 (file)
@@ -52,6 +52,7 @@ public abstract class AbstractNewIssuesEmailTemplate extends EmailTemplate {
   static final String FIELD_PROJECT_DATE = "projectDate";
   static final String FIELD_PROJECT_UUID = "projectUuid";
   static final String FIELD_ASSIGNEE = "assignee";
+  static final String FIELD_BRANCH = "branch";
 
   protected final EmailSettings settings;
   protected final I18n i18n;
@@ -175,8 +176,13 @@ public abstract class AbstractNewIssuesEmailTemplate extends EmailTemplate {
     String dateString = notification.getFieldValue(FIELD_PROJECT_DATE);
     if (projectKey != null && dateString != null) {
       Date date = DateUtils.parseDateTime(dateString);
-      String url = String.format("%s/project/issues?id=%s&createdAt=%s",
-        settings.getServerBaseURL(), encode(projectKey), encode(DateUtils.formatDateTime(date)));
+      String url = String.format("%s/project/issues?id=%s",
+        settings.getServerBaseURL(), encode(projectKey));
+      String branchName = notification.getFieldValue("branch");
+      if (branchName != null) {
+        url += "&branch=" + encode(branchName);
+      }
+      url += "&createdAt=" + encode(DateUtils.formatDateTime(date));
       message
         .append("See it in SonarQube: ")
         .append(url)
index 7c91d749fd86be68052fa63daa12892bc080fdbb..f5aaa6cf104bdf1765fcd2231769f5140879a112 100644 (file)
@@ -55,14 +55,15 @@ public class IssueChangeNotification extends Notification {
   }
 
   public IssueChangeNotification setProject(ComponentDto project) {
-    setFieldValue("projectName", project.longName());
-    setFieldValue("projectKey", project.getDbKey());
-    return this;
+    return setProject(project.getKey(), project.longName(), project.getBranch());
   }
 
-  public IssueChangeNotification setProject(String projectKey, String projectName) {
+  public IssueChangeNotification setProject(String projectKey, String projectName, @Nullable String branch) {
     setFieldValue("projectName", projectName);
     setFieldValue("projectKey", projectKey);
+    if (branch != null) {
+      setFieldValue("branch", branch);
+    }
     return this;
   }
 
index 6835d58a35f90d41260057e24dfd0afbfa8a4a92..81c8a698502dde6548031c059437717703ac47a2 100644 (file)
@@ -109,8 +109,12 @@ public class IssueChangesEmailTemplate extends EmailTemplate {
       sb.append("See it in SonarQube: ").append(settings.getServerBaseURL())
         .append("/project/issues?id=").append(encode(notification.getFieldValue("projectKey"), "UTF-8"))
         .append("&issues=").append(issueKey)
-        .append("&open=").append(issueKey)
-        .append(NEW_LINE);
+        .append("&open=").append(issueKey);
+      String branchName = notification.getFieldValue("branch");
+      if (branchName != null) {
+        sb.append("&branch=").append(branchName);
+      }
+      sb.append(NEW_LINE);
     } catch (UnsupportedEncodingException e) {
       throw new IllegalStateException("Encoding not supported", e);
     }
index e57c8f4beb1ebcfcf2d41f92752ccf2f2b93de2f..3beaa27762cbdd9018e45a2f3d23a7291dbff0b1 100644 (file)
@@ -58,13 +58,17 @@ public class MyNewIssuesEmailTemplate extends AbstractNewIssuesEmailTemplate {
     String projectUuid = notification.getFieldValue(FIELD_PROJECT_UUID);
     String dateString = notification.getFieldValue(FIELD_PROJECT_DATE);
     String assignee = notification.getFieldValue(FIELD_ASSIGNEE);
+    String branch = notification.getFieldValue(FIELD_BRANCH);
     if (projectUuid != null && dateString != null && assignee != null) {
       Date date = DateUtils.parseDateTime(dateString);
-      String url = String.format("%s/issues?projectUuids=%s&assignees=%s&createdAt=%s",
+      String url = String.format("%s/issues?projectUuids=%s&assignees=%s",
         settings.getServerBaseURL(),
         encode(projectUuid),
-        encode(assignee),
-        encode(DateUtils.formatDateTime(date)));
+        encode(assignee));
+      if (branch != null) {
+        url += "&branch=" + encode(branch);
+      }
+      url += "&createdAt=" + encode(DateUtils.formatDateTime(date));
       message.append("See it in SonarQube: ").append(url).append(NEW_LINE);
     }
   }
index bd4b62667f863ddb83288ed2da3785107af5e559..5012cd3b96b371313f282f9a5d381492b9af3440 100644 (file)
@@ -129,7 +129,7 @@ public abstract class AbstractUserSession implements UserSession {
   protected List<ComponentDto> doKeepAuthorizedComponents(String permission, Collection<ComponentDto> components) {
     boolean allowPublicComponent = ProjectPermissions.PUBLIC_PERMISSIONS.contains(permission);
     return components.stream()
-      .filter(c -> (allowPublicComponent && !c.isPrivate()) || hasProjectUuidPermission(permission, c.projectUuid()))
+      .filter(c -> (allowPublicComponent && !c.isPrivate()) || hasComponentPermission(permission, c))
       .collect(MoreCollectors.toList());
   }
 
index 8f634c9b33e191f1837b8f7c3781d2b46cc8647d..2508728aa4ae670ab7c7eb1bbdab1c734c89ac9a 100644 (file)
@@ -25,7 +25,9 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.sonar.api.notifications.Notification;
+import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
+import org.sonar.server.computation.task.projectanalysis.component.DefaultBranchImpl;
 import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
 import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
 import org.sonar.server.computation.task.projectanalysis.event.Event;
@@ -62,6 +64,9 @@ public class QualityGateEventsStepTest {
   @Rule
   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
 
+  @Rule
+  public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
+
   private ArgumentCaptor<Event> eventArgumentCaptor = ArgumentCaptor.forClass(Event.class);
   private ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(Notification.class);
 
@@ -71,11 +76,12 @@ public class QualityGateEventsStepTest {
   private MeasureRepository measureRepository = mock(MeasureRepository.class);
   private EventRepository eventRepository = mock(EventRepository.class);
   private NotificationService notificationService = mock(NotificationService.class);
-  private QualityGateEventsStep underTest = new QualityGateEventsStep(treeRootHolder, metricRepository, measureRepository, eventRepository, notificationService);
+  private QualityGateEventsStep underTest = new QualityGateEventsStep(treeRootHolder, metricRepository, measureRepository, eventRepository, notificationService, analysisMetadataHolder);
 
   @Before
   public void setUp() {
     when(metricRepository.getByKey(ALERT_STATUS_KEY)).thenReturn(alertStatusMetric);
+    analysisMetadataHolder.setBranch(null);
     treeRootHolder.setRoot(PROJECT_COMPONENT);
   }
 
@@ -182,6 +188,7 @@ public class QualityGateEventsStepTest {
     assertThat(notification.getFieldValue("projectKey")).isEqualTo(PROJECT_COMPONENT.getKey());
     assertThat(notification.getFieldValue("projectUuid")).isEqualTo(PROJECT_COMPONENT.getUuid());
     assertThat(notification.getFieldValue("projectName")).isEqualTo(PROJECT_COMPONENT.getName());
+    assertThat(notification.getFieldValue("branch")).isNull();
     assertThat(notification.getFieldValue("alertLevel")).isEqualTo(rawAlterStatus.name());
     assertThat(notification.getFieldValue("alertName")).isEqualTo(expectedLabel);
   }
@@ -233,10 +240,33 @@ public class QualityGateEventsStepTest {
     assertThat(notification.getFieldValue("projectKey")).isEqualTo(PROJECT_COMPONENT.getKey());
     assertThat(notification.getFieldValue("projectUuid")).isEqualTo(PROJECT_COMPONENT.getUuid());
     assertThat(notification.getFieldValue("projectName")).isEqualTo(PROJECT_COMPONENT.getName());
+    assertThat(notification.getFieldValue("branch")).isNull();
     assertThat(notification.getFieldValue("alertLevel")).isEqualTo(newQualityGateStatus.getStatus().name());
     assertThat(notification.getFieldValue("alertName")).isEqualTo(expectedLabel);
 
     reset(measureRepository, eventRepository, notificationService);
   }
 
+  @Test
+  public void verify_branch_name_is_set_in_notification() {
+    String branchName = "feature1";
+    analysisMetadataHolder.setBranch(new DefaultBranchImpl(branchName));
+
+    when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(Measure.newMeasureBuilder().setQualityGateStatus(WARN_QUALITY_GATE_STATUS).createNoValue()));
+    when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(
+      of(Measure.newMeasureBuilder().setQualityGateStatus(new QualityGateStatus(ERROR)).createNoValue()));
+
+    underTest.execute();
+
+    verify(notificationService).deliver(notificationArgumentCaptor.capture());
+    Notification notification = notificationArgumentCaptor.getValue();
+    assertThat(notification.getType()).isEqualTo("alerts");
+    assertThat(notification.getFieldValue("projectKey")).isEqualTo(PROJECT_COMPONENT.getKey());
+    assertThat(notification.getFieldValue("projectUuid")).isEqualTo(PROJECT_COMPONENT.getUuid());
+    assertThat(notification.getFieldValue("projectName")).isEqualTo(PROJECT_COMPONENT.getName());
+    assertThat(notification.getFieldValue("branch")).isEqualTo(branchName);
+
+    reset(measureRepository, eventRepository, notificationService);
+  }
+
 }
index ce7fd1dc7629bc85ab85b72c29d14ee3addc3244..8e33f079c96b8b2fe6fb9d28b27d065fe7650e8f 100644 (file)
@@ -123,6 +123,26 @@ public class IssueUpdaterTest {
     assertThat(issueChangeNotification.getFieldValue("comment")).isEqualTo("increase severity");
   }
 
+  @Test
+  public void verify_notification_on_branch() throws Exception {
+    RuleDto rule = ruleDbTester.insertRule(newRuleDto());
+    ComponentDto project = componentDbTester.insertMainBranch();
+    ComponentDto branch = componentDbTester.insertProjectBranch(project);
+    ComponentDto file = componentDbTester.insertComponent(newFileDto(branch));
+    DefaultIssue issue = issueDbTester.insertIssue(IssueTesting.newIssue(rule.getDefinition(), branch, file)).setSeverity(MAJOR).toDefaultIssue();
+    IssueChangeContext context = IssueChangeContext.createUser(new Date(), "john");
+    issueFieldsSetter.setSeverity(issue, BLOCKER, context);
+
+    underTest.saveIssue(dbTester.getSession(), issue, context, "increase severity");
+
+    verify(notificationManager).scheduleForSending(notificationArgumentCaptor.capture());
+    IssueChangeNotification issueChangeNotification = notificationArgumentCaptor.getValue();
+    assertThat(issueChangeNotification.getFieldValue("key")).isEqualTo(issue.key());
+    assertThat(issueChangeNotification.getFieldValue("projectKey")).isEqualTo(project.getDbKey());
+    assertThat(issueChangeNotification.getFieldValue("projectName")).isEqualTo(project.name());
+    assertThat(issueChangeNotification.getFieldValue("branch")).isEqualTo(branch.getBranch());
+  }
+
   @Test
   public void verify_notification_when_issue_is_linked_on_removed_rule() throws Exception {
     RuleDto rule = ruleDbTester.insertRule(newRuleDto().setStatus(RuleStatus.REMOVED));
index 0d02dcbd8cd1e1eebc2aca36f80d7b4c52598b1a..c3eae3dbea8edb285b12d14d9d1b452f581d1b20 100644 (file)
@@ -84,10 +84,19 @@ public class IssueChangeNotificationTest {
   }
 
   @Test
-  public void set_project() {
-    IssueChangeNotification result = notification.setProject("MyService", "My Service");
+  public void set_project_without_branch() {
+    IssueChangeNotification result = notification.setProject("MyService", "My Service", null);
     assertThat(result.getFieldValue("projectKey")).isEqualTo("MyService");
     assertThat(result.getFieldValue("projectName")).isEqualTo("My Service");
+    assertThat(result.getFieldValue("branch")).isNull();
+  }
+
+  @Test
+  public void set_project_with_branch() {
+    IssueChangeNotification result = notification.setProject("MyService", "My Service", "feature1");
+    assertThat(result.getFieldValue("projectKey")).isEqualTo("MyService");
+    assertThat(result.getFieldValue("projectName")).isEqualTo("My Service");
+    assertThat(result.getFieldValue("branch")).isEqualTo("feature1");
   }
 
   @Test
index 544c2304213728bc0141d72be09f6413530e5fd3..3e858e61915fa5c17381cca3f1e484923f1ab6b0 100644 (file)
@@ -147,13 +147,30 @@ public class IssueChangesEmailTemplateTest {
     assertThat(email.getFrom()).isNull();
   }
 
+  @Test
+  public void test_email_with_issue_on_branch() throws Exception {
+    Notification notification = generateNotification()
+      .setFieldValue("branch", "feature1");
+
+    EmailMessage email = underTest.format(notification);
+    assertThat(email.getMessageId()).isEqualTo("issue-changes/ABCDE");
+    assertThat(email.getSubject()).isEqualTo("Struts, change on issue #ABCDE");
+
+    String message = email.getMessage();
+    String expected = Resources.toString(Resources.getResource(
+      "org/sonar/server/issue/notification/IssueChangesEmailTemplateTest/email_with_issue_on_branch.txt"),
+      StandardCharsets.UTF_8);
+    expected = StringUtils.remove(expected, '\r');
+    assertThat(message).isEqualTo(expected);
+  }
+
   @Test
   public void notification_sender_should_be_the_author_of_change() {
     db.users().insertUser(newUserDto().setLogin("simon").setName("Simon"));
 
     Notification notification = new IssueChangeNotification()
       .setChangeAuthorLogin("simon")
-      .setProject("Struts", "org.apache:struts");
+      .setProject("Struts", "org.apache:struts", null);
 
     EmailMessage message = underTest.format(notification);
     assertThat(message.getFrom()).isEqualTo("Simon");
@@ -165,7 +182,7 @@ public class IssueChangesEmailTemplateTest {
 
     Notification notification = new IssueChangeNotification()
       .setChangeAuthorLogin("simon")
-      .setProject("Struts", "org.apache:struts");
+      .setProject("Struts", "org.apache:struts", null);
 
     EmailMessage message = underTest.format(notification);
     assertThat(message.getFrom()).isEqualTo("simon");
index 0c00dba4dce107261abbb8e968c22712724a6fda..8922e3fe0c634c3e0c4d026cd4c9f7cfa9c3f257 100644 (file)
@@ -95,7 +95,7 @@ public class MyNewIssuesEmailTemplateTest {
     EmailMessage message = underTest.format(notification);
 
     // TODO datetime to be completed when test is isolated from JVM timezone
-    assertStartsWithFile(message.getMessage(), getClass().getResource("MyNewIssuesEmailTemplateTest/email_with_all_details.txt"));
+    assertStartsWithFile(message.getMessage(), "MyNewIssuesEmailTemplateTest/email_with_all_details.txt");
   }
 
   @Test
@@ -123,7 +123,18 @@ public class MyNewIssuesEmailTemplateTest {
     EmailMessage message = underTest.format(notification);
 
     // TODO datetime to be completed when test is isolated from JVM timezone
-    assertStartsWithFile(message.getMessage(), getClass().getResource("MyNewIssuesEmailTemplateTest/email_with_no_assignee_tags_components.txt"));
+    assertStartsWithFile(message.getMessage(), "MyNewIssuesEmailTemplateTest/email_with_no_assignee_tags_components.txt");
+  }
+
+  @Test
+  public void format_email_with_issue_on_branch() throws Exception {
+    Notification notification = newNotification()
+      .setFieldValue("branch", "feature1");
+
+    EmailMessage message = underTest.format(notification);
+
+    // TODO datetime to be completed when test is isolated from JVM timezone
+    assertStartsWithFile(message.getMessage(), "MyNewIssuesEmailTemplateTest/email_with_issue_on_branch.txt");
   }
 
   @Test
@@ -176,8 +187,8 @@ public class MyNewIssuesEmailTemplateTest {
       .setFieldValue(RULE + ".2.count", "5");
   }
 
-  private static void assertStartsWithFile(String message, URL file) throws IOException {
-    String fileContent = IOUtils.toString(file, StandardCharsets.UTF_8);
+  private void assertStartsWithFile(String message, String resourcePath) throws IOException {
+    String fileContent = IOUtils.toString(getClass().getResource(resourcePath), StandardCharsets.UTF_8);
     assertThat(sanitizeString(message)).startsWith(sanitizeString(fileContent));
   }
 
index 341ccd92a1485868d45c9d2cec47395d332002e7..b2116b238fde2ac698040b7a5d994972d7db71ff 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.issue.notification;
 
+import java.io.IOException;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.StringUtils;
 import org.junit.Before;
@@ -112,8 +113,7 @@ public class NewIssuesEmailTemplateTest {
     EmailMessage message = template.format(notification);
 
     // TODO datetime to be completed when test is isolated from JVM timezone
-    String expectedContent = IOUtils.toString(getClass().getResource("NewIssuesEmailTemplateTest/email_with_all_details.txt"), StandardCharsets.UTF_8);
-    assertThat(message.getMessage()).startsWith(StringUtils.remove(expectedContent, '\r'));
+    assertStartsWithFile(message.getMessage(), "NewIssuesEmailTemplateTest/email_with_all_details.txt");
   }
 
   @Test
@@ -123,8 +123,18 @@ public class NewIssuesEmailTemplateTest {
     EmailMessage message = template.format(notification);
 
     // TODO datetime to be completed when test is isolated from JVM timezone
-    String expectedContent = IOUtils.toString(getClass().getResource("NewIssuesEmailTemplateTest/email_with_partial_details.txt"), StandardCharsets.UTF_8);
-    assertThat(message.getMessage()).startsWith(StringUtils.remove(expectedContent, '\r'));
+    assertStartsWithFile(message.getMessage(), "NewIssuesEmailTemplateTest/email_with_partial_details.txt");
+  }
+
+  @Test
+  public void format_email_with_issue_on_branch() throws Exception {
+    Notification notification = newNotification()
+      .setFieldValue("branch", "feature1");
+
+    EmailMessage message = template.format(notification);
+
+    // TODO datetime to be completed when test is isolated from JVM timezone
+    assertStartsWithFile(message.getMessage(), "NewIssuesEmailTemplateTest/email_with_issue_on_branch.txt");
   }
 
   @Test
@@ -184,4 +194,16 @@ public class NewIssuesEmailTemplateTest {
       .setFieldValue(RULE + ".2.label", "Rule the World (Java)")
       .setFieldValue(RULE + ".2.count", "5");
   }
+
+  private void assertStartsWithFile(String message, String resourcePath) throws IOException {
+    String fileContent = IOUtils.toString(getClass().getResource(resourcePath), StandardCharsets.UTF_8);
+    assertThat(sanitizeString(message)).startsWith(sanitizeString(fileContent));
+  }
+
+  /**
+   * sanitize EOL and tabs if git clone is badly configured
+   */
+  private static String sanitizeString(String s) {
+    return s.replaceAll("\\r\\n|\\r|\\s+", "");
+  }
 }
index 42d036f3e048b2f828d27556e3ba6ea8b39da004..5225acb63db7f68e6728b9fcad95818a90e0361e 100644 (file)
@@ -123,7 +123,7 @@ public class BulkChangeActionTest {
     issueWorkflow.start();
     rule = db.rules().insertRule(newRuleDto());
     organization = db.organizations().insert();
-    project = db.components().insertPrivateProject(organization);
+    project = db.components().insertMainBranch(organization);
     file = db.components().insertComponent(newFileDto(project));
     user = db.users().insertUser("john");
     when(system2.now()).thenReturn(NOW);
@@ -241,7 +241,6 @@ public class BulkChangeActionTest {
   public void send_notification() throws Exception {
     setUserProjectPermissions(USER);
     IssueDto issueDto = db.issues().insertIssue(newUnresolvedIssue().setType(BUG));
-    ArgumentCaptor<IssueChangeNotification> issueChangeNotificationCaptor = ArgumentCaptor.forClass(IssueChangeNotification.class);
 
     BulkChangeWsResponse response = call(BulkChangeRequest.builder()
       .setIssues(singletonList(issueDto.getKey()))
@@ -250,6 +249,8 @@ public class BulkChangeActionTest {
       .build());
 
     checkResponse(response, 1, 1, 0, 0);
+
+    ArgumentCaptor<IssueChangeNotification> issueChangeNotificationCaptor = ArgumentCaptor.forClass(IssueChangeNotification.class);
     verify(notificationManager).scheduleForSending(issueChangeNotificationCaptor.capture());
     assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("key")).isEqualTo(issueDto.getKey());
     assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("componentName")).isEqualTo(file.longName());
@@ -257,6 +258,35 @@ public class BulkChangeActionTest {
     assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("projectKey")).isEqualTo(project.getDbKey());
     assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("ruleName")).isEqualTo(rule.getName());
     assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("changeAuthor")).isEqualTo(user.getLogin());
+    assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("branch")).isNull();
+  }
+
+  @Test
+  public void send_notification_on_branch() throws Exception {
+    setUserProjectPermissions(USER);
+
+    String branchName = "feature1";
+    ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey(branchName));
+    ComponentDto fileOnBranch = db.components().insertComponent(newFileDto(branch));
+    IssueDto issueDto = db.issues().insertIssue(newUnresolvedIssue(rule, fileOnBranch, branch).setType(BUG));
+
+    BulkChangeWsResponse response = call(BulkChangeRequest.builder()
+      .setIssues(singletonList(issueDto.getKey()))
+      .setDoTransition("confirm")
+      .setSendNotifications(true)
+      .build());
+
+    checkResponse(response, 1, 1, 0, 0);
+
+    ArgumentCaptor<IssueChangeNotification> issueChangeNotificationCaptor = ArgumentCaptor.forClass(IssueChangeNotification.class);
+    verify(notificationManager).scheduleForSending(issueChangeNotificationCaptor.capture());
+    assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("key")).isEqualTo(issueDto.getKey());
+    assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("componentName")).isEqualTo(fileOnBranch.longName());
+    assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("projectName")).isEqualTo(project.longName());
+    assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("projectKey")).isEqualTo(project.getDbKey());
+    assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("ruleName")).isEqualTo(rule.getName());
+    assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("changeAuthor")).isEqualTo(user.getLogin());
+    assertThat(issueChangeNotificationCaptor.getValue().getFieldValue("branch")).isEqualTo(branchName);
   }
 
   @Test
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/notification/IssueChangesEmailTemplateTest/email_with_issue_on_branch.txt b/server/sonar-server/src/test/resources/org/sonar/server/issue/notification/IssueChangesEmailTemplateTest/email_with_issue_on_branch.txt
new file mode 100644 (file)
index 0000000..b9a5c6b
--- /dev/null
@@ -0,0 +1,6 @@
+Action
+Rule: Avoid Cycles
+Message: Has 3 cycles
+
+
+See it in SonarQube: http://nemo.sonarsource.org/project/issues?id=org.apache%3Astruts&issues=ABCDE&open=ABCDE&branch=feature1
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/notification/MyNewIssuesEmailTemplateTest/email_with_issue_on_branch.txt b/server/sonar-server/src/test/resources/org/sonar/server/issue/notification/MyNewIssuesEmailTemplateTest/email_with_issue_on_branch.txt
new file mode 100644 (file)
index 0000000..543e42e
--- /dev/null
@@ -0,0 +1,8 @@
+Project: Struts
+
+32 new issues (new debt: 1d3h)
+
+    Severity
+        Blocker: 0    Critical: 5    Major: 10    Minor: 3    Info: 1
+
+See it in SonarQube: http://nemo.sonarsource.org/issues?projectUuids=ABCDE&assignees=lo.gin&branch=feature1&createdAt=2010-05-1
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/notification/NewIssuesEmailTemplateTest/email_with_issue_on_branch.txt b/server/sonar-server/src/test/resources/org/sonar/server/issue/notification/NewIssuesEmailTemplateTest/email_with_issue_on_branch.txt
new file mode 100644 (file)
index 0000000..bc0afd3
--- /dev/null
@@ -0,0 +1,8 @@
+Project: Struts
+
+32 new issues (new debt: 1d3h)
+
+    Severity
+        Blocker: 0    Critical: 5    Major: 10    Minor: 3    Info: 1
+
+See it in SonarQube: http://nemo.sonarsource.org/project/issues?id=org.apache%3Astruts&branch=feature1&createdAt=2010-05-1