]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7024 add IT on Report analysis failure notifications
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Mon, 18 Sep 2017 13:26:40 +0000 (15:26 +0200)
committerEric Hartmann <hartmann.eric@gmail.Com>
Mon, 2 Oct 2017 11:03:35 +0000 (13:03 +0200)
tests/src/test/java/org/sonarqube/tests/Category6Suite.java
tests/src/test/java/org/sonarqube/tests/ce/ReportFailureNotificationTest.java [new file with mode: 0644]

index e1e4e666dc69a1571306da2373bbd68474f3f2d0..8cebafd58bffe3dbcc7bc105ba7931d3e64e3cd3 100644 (file)
@@ -26,6 +26,7 @@ import org.junit.ClassRule;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
 import org.sonarqube.tests.authorisation.PermissionTemplateTest;
+import org.sonarqube.tests.ce.ReportFailureNotificationTest;
 import org.sonarqube.tests.issue.IssueTagsTest;
 import org.sonarqube.tests.issue.OrganizationIssueAssignTest;
 import org.sonarqube.tests.issue.OrganizationIssuesPageTest;
@@ -81,7 +82,8 @@ import static util.ItUtils.xooPlugin;
   ProjectProvisioningTest.class,
   ProjectKeyUpdateTest.class,
   ProjectSearchTest.class,
-  PermissionTemplateTest.class
+  PermissionTemplateTest.class,
+  ReportFailureNotificationTest.class
 })
 public class Category6Suite {
 
diff --git a/tests/src/test/java/org/sonarqube/tests/ce/ReportFailureNotificationTest.java b/tests/src/test/java/org/sonarqube/tests/ce/ReportFailureNotificationTest.java
new file mode 100644 (file)
index 0000000..877b133
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonarqube.tests.ce;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
+import javax.mail.Address;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonarqube.tests.Category6Suite;
+import org.sonarqube.tests.Tester;
+import org.sonarqube.ws.WsProjects;
+import org.sonarqube.ws.WsUsers;
+import org.sonarqube.ws.client.PostRequest;
+import org.sonarqube.ws.client.WsClient;
+import org.subethamail.wiser.Wiser;
+import org.subethamail.wiser.WiserMessage;
+
+import static java.lang.String.valueOf;
+import static org.assertj.core.api.Assertions.assertThat;
+import static util.ItUtils.newUserWsClient;
+import static util.ItUtils.projectDir;
+
+public class ReportFailureNotificationTest {
+  @ClassRule
+  public static final Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
+
+  @Rule
+  public Tester tester = new Tester(orchestrator).disableOrganizations();
+
+  private static Wiser smtpServer;
+
+  @BeforeClass
+  public static void before() throws Exception {
+    smtpServer = new Wiser(0);
+    smtpServer.start();
+    System.out.println("SMTP Server port: " + smtpServer.getServer().getPort());
+  }
+
+  @AfterClass
+  public static void stop() {
+    if (smtpServer != null) {
+      smtpServer.stop();
+    }
+  }
+
+  @Before
+  public void prepare() throws Exception {
+    tester.settings().setGlobalSettings(
+      "email.smtp_host.secured", smtpServer.getServer().getHostName(),
+      "email.smtp_port.secured", valueOf(smtpServer.getServer().getPort()));
+
+    smtpServer.getMessages().clear();
+
+    tester.elasticsearch().unlockWrites("components");
+  }
+
+  @After
+  public void restoreElasticSearch() throws Exception {
+    tester.elasticsearch().unlockWrites("components");
+  }
+
+  @Test
+  public void send_notification_on_report_processing_failures_to_global_and_project_subscribers() throws Exception {
+    WsUsers.CreateWsResponse.User user1 = tester.users().generate(t -> t.setPassword("user1").setEmail("user1@bar.com"));
+    WsUsers.CreateWsResponse.User user2 = tester.users().generate(t -> t.setPassword("user2").setEmail("user2@bar.com"));
+    WsUsers.CreateWsResponse.User user3 = tester.users().generate(t -> t.setPassword("user3").setEmail("user3@bar.com"));
+    WsProjects.CreateWsResponse.Project project1 = tester.projects().generate(null, t -> t.setName("Project1"));
+    WsProjects.CreateWsResponse.Project project2 = tester.projects().generate(null, t -> t.setName("Project2"));
+    WsProjects.CreateWsResponse.Project project3 = tester.projects().generate(null, t -> t.setName("Project3"));
+
+    // analyses successful and no-one subscribed => no email
+    executeAnalysis(project1);
+    executeAnalysis(project2);
+    executeAnalysis(project3);
+    assertThat(waitForEmails()).isEmpty();
+
+    // analyses failed and no-one subscribed => no email
+    tester.elasticsearch().lockWrites("components");
+    executeAnalysis(project1);
+    executeAnalysis(project2);
+    executeAnalysis(project3);
+    assertThat(waitForEmails()).isEmpty();
+
+    // Add notifications to users: user1 globally, user2 for project1, user3 for project2
+    subscribeToReportFailures(user1, "user1", null);
+    subscribeToReportFailures(user2, "user2", project1);
+    subscribeToReportFailures(user3, "user3", project2);
+
+    // analysis failed and 1 global subscriber + 1 project subscriber => 2 emails
+    executeAnalysis(project1);
+    List<MimeMessage> messages = waitForEmails();
+    assertThat(messages.stream().flatMap(toToRecipients()).collect(Collectors.toSet()))
+      .containsOnly("user1@bar.com", "user2@bar.com");
+    assertSubjectAndContent(project1, messages);
+    // analysis failed and 1 global subscriber + 1 project subscriber => 2 emails
+    executeAnalysis(project2);
+    messages = waitForEmails();
+    assertThat(messages.stream().flatMap(toToRecipients()).collect(Collectors.toSet()))
+      .containsOnly("user1@bar.com", "user3@bar.com");
+    assertSubjectAndContent(project2, messages);
+    // analysis fails and 1 global subscriber => 1 email
+    executeAnalysis(project3);
+    messages = waitForEmails();
+    assertThat(messages.stream().flatMap(toToRecipients()).collect(Collectors.toSet()))
+      .containsOnly("user1@bar.com");
+    assertSubjectAndContent(project3, messages);
+
+    // global and project subscribed but analysis is successful => no email
+    tester.elasticsearch().unlockWrites("components");
+    executeAnalysis(project1);
+    executeAnalysis(project2);
+    executeAnalysis(project3);
+    assertThat(waitForEmails()).isEmpty();
+
+    // Add notifications to users: user1 globally, user2 for project1, user3 for project2
+    unsubscribeFromReportFailures(user1, "user1", null);
+    unsubscribeFromReportFailures(user2, "user2", project1);
+    unsubscribeFromReportFailures(user3, "user3", project2);
+
+    // analyses fail and no-one subscribed => no email
+    tester.elasticsearch().lockWrites("components");
+    executeAnalysis(project1);
+    executeAnalysis(project2);
+    executeAnalysis(project3);
+    assertThat(waitForEmails()).isEmpty();
+  }
+
+  private static void assertSubjectAndContent(WsProjects.CreateWsResponse.Project project, List<MimeMessage> messages) {
+    assertThat(messages.stream().map(toSubject()).collect(Collectors.toSet()))
+      .containsOnly("[SONARQUBE] " + project.getName() + ": Background task in failure");
+    Set<String> content = messages.stream().map(toContent()).collect(Collectors.toSet());
+    assertThat(content).hasSize(1);
+    assertThat(content.iterator().next())
+      .contains("Project:\t" + project.getName(),
+        "Background task:\t",
+        "Submission time:\t",
+        "Failure time:\t",
+        "Error message:\tUnrecoverable indexation failures",
+        "More details at: http://localhost:9000/project/background_tasks?id=" + project.getKey());
+  }
+
+  private static Function<MimeMessage, Stream<String>> toToRecipients() {
+    return t -> {
+      try {
+        return Arrays.stream(t.getRecipients(Message.RecipientType.TO)).map(Address::toString);
+      } catch (MessagingException e) {
+        return Stream.of();
+      }
+    };
+  }
+
+  private static Function<MimeMessage, String> toSubject() {
+    return t -> {
+      try {
+        return t.getSubject();
+      } catch (MessagingException e) {
+        return null;
+      }
+    };
+  }
+
+  private static Function<MimeMessage, String> toContent() {
+    return t -> {
+      try {
+        return (String) t.getContent();
+      } catch (IOException | MessagingException e) {
+        return null;
+      }
+    };
+  }
+
+  private void subscribeToReportFailures(WsUsers.CreateWsResponse.User user1, String password, @Nullable WsProjects.CreateWsResponse.Project project) {
+    WsClient wsClient = newUserWsClient(orchestrator, user1.getLogin(), password);
+    PostRequest request = new PostRequest("api/notifications/add")
+      .setParam("type", "CeReportTaskFailure")
+      .setParam("channel", "EmailNotificationChannel");
+    if (project != null) {
+      request.setParam("project", project.getKey());
+    }
+    wsClient.wsConnector().call(request)
+      .failIfNotSuccessful();
+  }
+
+  private void unsubscribeFromReportFailures(WsUsers.CreateWsResponse.User user1, String password, @Nullable WsProjects.CreateWsResponse.Project project) {
+    WsClient wsClient = newUserWsClient(orchestrator, user1.getLogin(), password);
+    PostRequest request = new PostRequest("api/notifications/remove")
+      .setParam("type", "CeReportTaskFailure")
+      .setParam("channel", "EmailNotificationChannel");
+    if (project != null) {
+      request.setParam("project", project.getKey());
+    }
+    wsClient.wsConnector().call(request)
+      .failIfNotSuccessful();
+  }
+
+  private void executeAnalysis(WsProjects.CreateWsResponse.Project project) {
+    SonarScanner sonarScanner = SonarScanner.create(projectDir("shared/xoo-sample"),
+      "sonar.projectKey", project.getKey(),
+      "sonar.projectName", project.getName());
+    orchestrator.executeBuild(sonarScanner);
+  }
+
+  private static List<MimeMessage> waitForEmails() throws InterruptedException {
+    System.out.println("Waiting for new emails...");
+    for (int i = 0; i < 10; i++) {
+      Thread.sleep(1_000);
+      List<WiserMessage> res = new ArrayList<>(smtpServer.getMessages());
+      // shortcut, we expect at most 2 emails at a time
+      if (res.size() >= 2) {
+        smtpServer.getMessages().clear();
+        return toMimeMessages(res);
+      }
+    }
+    List<WiserMessage> res = new ArrayList<>(smtpServer.getMessages());
+    smtpServer.getMessages().clear();
+    return toMimeMessages(res);
+  }
+
+  private static List<MimeMessage> toMimeMessages(List<WiserMessage> res) {
+    return res.stream().map(t -> {
+      try {
+        return t.getMimeMessage();
+      } catch (MessagingException e) {
+        return null;
+      }
+    }).filter(t -> t != null)
+      .collect(Collectors.toList());
+  }
+}