aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Mandrikov <mandrikov@gmail.com>2011-07-18 16:41:00 +0400
committerEvgeny Mandrikov <mandrikov@gmail.com>2011-07-18 23:34:06 +0400
commitded6ace3a7761468a97b6ac7fc7b924cd1147b96 (patch)
tree053dd641fd2e2457e118c50ca006cd0478d1f9b4
parentba62b82ea636be85d3c3c316174f2c8e4ad551ff (diff)
downloadsonarqube-ded6ace3a7761468a97b6ac7fc7b924cd1147b96.tar.gz
sonarqube-ded6ace3a7761468a97b6ac7fc7b924cd1147b96.zip
SONAR-2600,SONAR-2601 Add ability to send emails
Add simple page with SMTP configuration and with ability to send test email. When SMTP configured - send notifications via email.
-rw-r--r--sonar-server/pom.xml11
-rw-r--r--sonar-server/src/main/java/org/sonar/server/notifications/email/EmailConfiguration.java81
-rw-r--r--sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessage.java7
-rw-r--r--sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java114
-rw-r--r--sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewEmailTemplate.java2
-rw-r--r--sonar-server/src/main/java/org/sonar/server/platform/Platform.java4
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/email_configuration_controller.rb64
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/email_configuration/index.html.erb110
-rw-r--r--sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java171
9 files changed, 488 insertions, 76 deletions
diff --git a/sonar-server/pom.xml b/sonar-server/pom.xml
index 53441136766..df611dabce4 100644
--- a/sonar-server/pom.xml
+++ b/sonar-server/pom.xml
@@ -155,6 +155,11 @@
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-util</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-email</artifactId>
+ <version>1.2</version>
+ </dependency>
<!-- unit tests -->
<dependency>
@@ -162,6 +167,12 @@
<artifactId>sonar-testing-harness</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>dumbster</groupId>
+ <artifactId>dumbster</artifactId>
+ <version>1.6</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailConfiguration.java b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailConfiguration.java
new file mode 100644
index 00000000000..e16e536cd0c
--- /dev/null
+++ b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailConfiguration.java
@@ -0,0 +1,81 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.server.notifications.email;
+
+import org.apache.commons.configuration.Configuration;
+import org.sonar.api.ServerComponent;
+
+/**
+ * Ruby uses constants from this class.
+ *
+ * @since 2.10
+ */
+public class EmailConfiguration implements ServerComponent {
+
+ public static final String SMTP_HOST = "email.smtp_host";
+ public static final String SMTP_HOST_DEFAULT = "";
+ public static final String SMTP_PORT = "email.smtp_port";
+ public static final String SMTP_PORT_DEFAULT = "25";
+ public static final String SMTP_USE_TLS = "email.smtp_use_tls";
+ public static final boolean SMTP_USE_TLS_DEFAULT = false;
+ public static final String SMTP_USERNAME = "email.smtp_username";
+ public static final String SMTP_USERNAME_DEFAULT = "";
+ public static final String SMTP_PASSWORD = "email.smtp_password";
+ public static final String SMTP_PASSWORD_DEFAULT = "";
+ public static final String FROM = "email.from";
+ public static final String FROM_DEFAULT = "noreply@nowhere";
+ public static final String PREFIX = "email.prefix";
+ public static final String PREFIX_DEFAULT = "[SONAR]";
+
+ private Configuration configuration;
+
+ public EmailConfiguration(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ public String getSmtpHost() {
+ return configuration.getString(SMTP_HOST, SMTP_HOST_DEFAULT);
+ }
+
+ public String getSmtpPort() {
+ return configuration.getString(SMTP_PORT, SMTP_PORT_DEFAULT);
+ }
+
+ public boolean isUseTLS() {
+ return configuration.getBoolean(SMTP_USE_TLS, SMTP_USE_TLS_DEFAULT);
+ }
+
+ public String getSmtpUsername() {
+ return configuration.getString(SMTP_USERNAME, SMTP_USERNAME_DEFAULT);
+ }
+
+ public String getSmtpPassword() {
+ return configuration.getString(SMTP_PASSWORD, SMTP_PASSWORD_DEFAULT);
+ }
+
+ public String getFrom() {
+ return configuration.getString(FROM, FROM_DEFAULT);
+ }
+
+ public String getPrefix() {
+ return configuration.getString(PREFIX, PREFIX_DEFAULT);
+ }
+
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessage.java b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessage.java
index c64b3b3e175..c2227e6506b 100644
--- a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessage.java
+++ b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailMessage.java
@@ -19,6 +19,8 @@
*/
package org.sonar.server.notifications.email;
+import org.apache.commons.lang.builder.ToStringBuilder;
+
import java.io.Serializable;
/**
@@ -107,4 +109,9 @@ public class EmailMessage implements Serializable {
return messageId;
}
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this).toString();
+ }
+
}
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java
index a0b12e14a13..7c07f56f063 100644
--- a/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java
+++ b/sonar-server/src/main/java/org/sonar/server/notifications/email/EmailNotificationChannel.java
@@ -19,9 +19,14 @@
*/
package org.sonar.server.notifications.email;
-import org.codehaus.plexus.util.StringUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.mail.Email;
+import org.apache.commons.mail.EmailException;
+import org.apache.commons.mail.SimpleEmail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.sonar.api.database.model.User;
+import org.sonar.jpa.session.DatabaseSessionFactory;
import org.sonar.server.notifications.Notification;
import org.sonar.server.notifications.NotificationChannel;
@@ -41,14 +46,27 @@ public class EmailNotificationChannel extends NotificationChannel {
private static final Logger LOG = LoggerFactory.getLogger(EmailNotificationChannel.class);
- private static final String FROM_DEFAULT = "Sonar";
- private static final String SUBJECT_PREFIX = "[Sonar]";
+ /**
+ * @see Email#setSocketConnectionTimeout(int)
+ * @see Email#setSocketTimeout(int)
+ */
+ private static final int SOCKET_TIMEOUT = 30000;
+
+ private static final String FROM_NAME_DEFAULT = "Sonar";
private static final String SUBJECT_DEFAULT = "Notification";
+ private EmailConfiguration configuration;
private EmailMessageTemplate[] templates;
+ private DatabaseSessionFactory sessionFactory;
- public EmailNotificationChannel(EmailMessageTemplate[] templates) {
+ public EmailNotificationChannel(EmailConfiguration configuration, EmailMessageTemplate[] templates, DatabaseSessionFactory sessionFactory) {
+ this.configuration = configuration;
this.templates = templates;
+ this.sessionFactory = sessionFactory;
+ }
+
+ private User getUserById(Integer id) {
+ return sessionFactory.getSession().getEntity(User.class, id);
}
@Override
@@ -56,7 +74,8 @@ public class EmailNotificationChannel extends NotificationChannel {
for (EmailMessageTemplate template : templates) {
EmailMessage email = template.format(notification);
if (email != null) {
- email.setTo(userId.toString()); // TODO should be valid email@address
+ User user = getUserById(userId);
+ email.setTo(user.getEmail());
return email;
}
}
@@ -65,47 +84,68 @@ public class EmailNotificationChannel extends NotificationChannel {
@Override
public void deliver(Serializable notificationData) {
- EmailMessage email = (EmailMessage) notificationData;
- LOG.info("Email:\n{}", create(email));
+ if (StringUtils.isBlank(configuration.getSmtpHost())) {
+ LOG.warn("SMTP host was not configured - email will not be sent");
+ return;
+ }
+ try {
+ send((EmailMessage) notificationData);
+ } catch (EmailException e) {
+ LOG.error("Unable to send email", e);
+ }
}
- /**
- * Visibility has been relaxed for tests.
- */
- String create(EmailMessage email) {
+ private void send(EmailMessage emailMessage) throws EmailException {
+ LOG.info("Sending email: {}", emailMessage);
// TODO
- String serverUrl = "http://nemo.sonarsource.org";
String domain = "nemo.sonarsource.org";
String listId = "<sonar." + domain + ">";
- String from = StringUtils.defaultString(email.getFrom(), FROM_DEFAULT) + " <noreply@" + domain + ">";
- String subject = SUBJECT_PREFIX + " " + StringUtils.defaultString(email.getSubject(), SUBJECT_DEFAULT);
- String permalink = null;
- StringBuilder sb = new StringBuilder();
- if (StringUtils.isNotEmpty(email.getMessageId())) {
- subject = "Re: " + subject;
- String messageId = "<" + email.getMessageId() + "@" + domain + ">";
- appendHeader(sb, "Message-Id", messageId);
- appendHeader(sb, "In-Reply-To", messageId);
- appendHeader(sb, "References", messageId);
- permalink = serverUrl + '/' + email.getMessageId();
+ String serverUrl = "http://nemo.sonarsource.org";
+
+ SimpleEmail email = new SimpleEmail();
+ /*
+ * Set headers for proper threading:
+ * GMail will not group messages, even if they have same subject, but don't have "In-Reply-To" and "References" headers.
+ * TODO investigate threading in other clients like KMail, Thunderbird, Outlook
+ */
+ if (StringUtils.isNotEmpty(emailMessage.getMessageId())) {
+ String messageId = "<" + emailMessage.getMessageId() + "@" + domain + ">";
+ email.addHeader("In-Reply-To", messageId);
+ email.addHeader("References", messageId);
}
- appendHeader(sb, "List-Id", listId);
- appendHeader(sb, "List-Archive", serverUrl);
- appendHeader(sb, "From", from);
- appendHeader(sb, "To", email.getTo());
- appendHeader(sb, "Subject", subject);
- sb.append('\n')
- .append(email.getMessage()).append('\n');
- if (permalink != null) {
- sb.append('\n')
- .append("--\n")
- .append("View it in Sonar: ").append(permalink);
+ // Set headers for proper filtering
+ email.addHeader("List-Id", listId);
+ email.addHeader("List-Archive", serverUrl);
+ // Set general information
+ email.setFrom(configuration.getFrom(), StringUtils.defaultIfBlank(emailMessage.getFrom(), FROM_NAME_DEFAULT));
+ email.addTo(emailMessage.getTo(), " ");
+ String subject = StringUtils.defaultIfBlank(StringUtils.trimToEmpty(configuration.getPrefix()) + " ", "")
+ + StringUtils.defaultString(emailMessage.getSubject(), SUBJECT_DEFAULT);
+ email.setSubject(subject);
+ email.setMsg(emailMessage.getMessage());
+ // Send
+ email.setHostName(configuration.getSmtpHost());
+ email.setSmtpPort(Integer.parseInt(configuration.getSmtpPort()));
+ email.setTLS(configuration.isUseTLS());
+ if (StringUtils.isNotBlank(configuration.getSmtpUsername()) || StringUtils.isNotBlank(configuration.getSmtpPassword())) {
+ email.setAuthentication(configuration.getSmtpUsername(), configuration.getSmtpPassword());
}
- return sb.toString();
+ email.setSocketConnectionTimeout(SOCKET_TIMEOUT);
+ email.setSocketTimeout(SOCKET_TIMEOUT);
+ email.send();
}
- private void appendHeader(StringBuilder sb, String header, String value) {
- sb.append(header).append(": ").append(value).append('\n');
+ /**
+ * Send test email. This method called from Ruby.
+ *
+ * @throws EmailException when unable to send
+ */
+ public void sendTestEmail(String toAddress, String subject, String message) throws EmailException {
+ EmailMessage emailMessage = new EmailMessage();
+ emailMessage.setTo(toAddress);
+ emailMessage.setSubject(subject);
+ emailMessage.setMessage(message);
+ send(emailMessage);
}
}
diff --git a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewEmailTemplate.java b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewEmailTemplate.java
index eb982500128..460db32c9a4 100644
--- a/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewEmailTemplate.java
+++ b/sonar-server/src/main/java/org/sonar/server/notifications/reviews/CommentOnReviewEmailTemplate.java
@@ -35,7 +35,7 @@ public class CommentOnReviewEmailTemplate extends EmailMessageTemplate {
EmailMessage email = new EmailMessage()
.setFrom(event.getAuthor().getName())
.setMessageId("review/" + event.getReview().getId())
- .setSubject(event.getReview().getTitle())
+ .setSubject("Review #" + event.getReview().getId())
.setMessage(event.getComment());
return email;
}
diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java
index 160602d419e..49bcf2d6aa4 100644
--- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java
+++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java
@@ -58,6 +58,7 @@ import org.sonar.server.mavendeployer.MavenRepository;
import org.sonar.server.notifications.NotificationManager;
import org.sonar.server.notifications.NotificationQueue;
import org.sonar.server.notifications.NotificationService;
+import org.sonar.server.notifications.email.EmailConfiguration;
import org.sonar.server.notifications.email.EmailNotificationChannel;
import org.sonar.server.notifications.reviews.CommentOnReviewAssignedToMe;
import org.sonar.server.notifications.reviews.CommentOnReviewCreatedByMe;
@@ -196,7 +197,8 @@ public final class Platform {
servicesContainer.as(Characteristics.CACHE).addComponent(NotificationService.class);
servicesContainer.as(Characteristics.CACHE).addComponent(NotificationManager.class);
servicesContainer.as(Characteristics.CACHE).addComponent(ReviewsNotificationManager.class);
- // FIXME next four lines here just for tests:
+ // FIXME next five lines here just for tests:
+ servicesContainer.as(Characteristics.CACHE).addComponent(EmailConfiguration.class);
servicesContainer.as(Characteristics.CACHE).addComponent(EmailNotificationChannel.class);
servicesContainer.as(Characteristics.CACHE).addComponent(CommentOnReviewEmailTemplate.class);
servicesContainer.as(Characteristics.CACHE).addComponent(CommentOnReviewAssignedToMe.class);
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/email_configuration_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/email_configuration_controller.rb
new file mode 100644
index 00000000000..77c3172f3b2
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/email_configuration_controller.rb
@@ -0,0 +1,64 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2011 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# Sonar 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.
+#
+# Sonar 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 Sonar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+#
+class EmailConfigurationController < ApplicationController
+
+ SECTION=Navigation::SECTION_CONFIGURATION
+ before_filter :admin_required
+
+ EmailConfiguration = org.sonar.server.notifications.email.EmailConfiguration
+
+ def index
+ @smtp_host = Property.value(EmailConfiguration::SMTP_HOST, nil, EmailConfiguration::SMTP_HOST_DEFAULT)
+ @smtp_port = Property.value(EmailConfiguration::SMTP_PORT, nil, EmailConfiguration::SMTP_PORT_DEFAULT)
+ @smtp_use_tls = Property.value(EmailConfiguration::SMTP_USE_TLS, nil, EmailConfiguration::SMTP_USE_TLS_DEFAULT) == 'true'
+ @smtp_username = Property.value(EmailConfiguration::SMTP_USERNAME, nil, EmailConfiguration::SMTP_USERNAME_DEFAULT)
+ @smtp_password = Property.value(EmailConfiguration::SMTP_PASSWORD, nil, EmailConfiguration::SMTP_PASSWORD_DEFAULT)
+ @email_from = Property.value(EmailConfiguration::FROM, nil, EmailConfiguration::FROM_DEFAULT)
+ @email_prefix = Property.value(EmailConfiguration::PREFIX, nil, EmailConfiguration::PREFIX_DEFAULT)
+ end
+
+ def save
+ Property.set(EmailConfiguration::SMTP_HOST, params[:smtp_host])
+ Property.set(EmailConfiguration::SMTP_PORT, params[:smtp_port])
+ Property.set(EmailConfiguration::SMTP_USE_TLS, params[:smtp_use_tls] == 'true')
+ Property.set(EmailConfiguration::SMTP_USERNAME, params[:smtp_username])
+ Property.set(EmailConfiguration::SMTP_PASSWORD, params[:smtp_password])
+ Property.set(EmailConfiguration::FROM, params[:email_from])
+ Property.set(EmailConfiguration::PREFIX, params[:email_prefix])
+ redirect_to :action => 'index'
+ end
+
+ def send_test_email
+ to_address = params[:to_address]
+ subject = params[:subject]
+ message = params[:message]
+ if to_address.blank?
+ flash[:notice] = 'You must provide address where to send test email'
+ else
+ begin
+ java_facade.getCoreComponentByClassname('org.sonar.server.notifications.email.EmailNotificationChannel').sendTestEmail(to_address, subject, message)
+ rescue Exception => e
+ flash[:error] = e.message
+ end
+ end
+ redirect_to :action => 'index'
+ end
+
+end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/email_configuration/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/email_configuration/index.html.erb
new file mode 100644
index 00000000000..d59ec3a089b
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/email_configuration/index.html.erb
@@ -0,0 +1,110 @@
+<h1>Email configuration</h1>
+<% form_tag({:action => 'save'}) do -%>
+ <table>
+ <tr>
+ <td nowrap>
+ <label for="smtp_host"><b>SMTP Host:</b></label>
+ </td>
+ <td class="sep"> </td>
+ <td align="left">
+ <%= text_field_tag 'smtp_host', @smtp_host %>
+ </td>
+ </tr>
+
+ <tr>
+ <td nowrap>
+ <label for="smtp_port"><b>SMTP Port:</b></label>
+ </td>
+ <td class="sep"> </td>
+ <td align="left">
+ <%= text_field_tag 'smtp_port', @smtp_port %>
+ </td>
+ </tr>
+
+ <tr>
+ <td nowrap>
+ <label for="smtp_port"><b>Use TLS:</b></label>
+ </td>
+ <td class="sep"> </td>
+ <td align="left">
+ <%= check_box_tag 'smtp_use_tls', 'true', @smtp_use_tls %>
+ </td>
+ </tr>
+
+ <tr>
+ <td nowrap>
+ <label for="smtp_username"><b>SMTP Username:</b></label>
+ </td>
+ <td class="sep"> </td>
+ <td align="left">
+ <%= text_field_tag 'smtp_username', @smtp_username %>
+ </td>
+ </tr>
+
+ <tr>
+ <td nowrap>
+ <label for="smtp_password"><b>SMTP Password:</b></label>
+ </td>
+ <td class="sep"> </td>
+ <td align="left">
+ <%= password_field_tag 'smtp_password', @smtp_password %>
+ </td>
+ </tr>
+
+ <tr>
+ <td nowrap>
+ <label for="email_from"><b>From address:</b></label>
+ </td>
+ <td class="sep"> </td>
+ <td align="left">
+ <%= text_field_tag 'email_from', @email_from %>
+ </td>
+ </tr>
+
+ <tr>
+ <td nowrap>
+ <label for="email_prefix"><b>Email prefix:</b></label>
+ </td>
+ <td class="sep"> </td>
+ <td align="left">
+ <%= text_field_tag 'email_prefix', @email_prefix %>
+ </td>
+ </tr>
+ </table>
+ <%= submit_tag %>
+<% end -%>
+<br/>
+
+<h1>Send Test Email</h1>
+<% form_tag({:action => 'send_test_email'}) do -%>
+ <table>
+ <tr>
+ <td nowrap>
+ <label for="to_address"><b>To:</b></label>
+ </td>
+ <td class="sep"> </td>
+ <td align="left">
+ <%= text_field_tag 'to_address', current_user.email %>
+ </td>
+ </tr>
+ <tr>
+ <td nowrap>
+ <label for="subject"><b>Subject:</b></label>
+ </td>
+ <td class="sep"> </td>
+ <td align="left">
+ <%= text_field_tag 'subject', 'Test Message from Sonar' %>
+ </td>
+ </tr>
+ <tr>
+ <td nowrap>
+ <label for="message"><b>Message:</b></label>
+ </td>
+ <td class="sep"> </td>
+ <td align="left">
+ <%= text_area_tag 'message', 'This is a test message from Sonar.' %>
+ </td>
+ </tr>
+ <table>
+ <%= submit_tag 'Send' %>
+<% end -%>
diff --git a/sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java b/sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java
index 4c9c3544f4d..0b51f3e989b 100644
--- a/sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/notifications/email/EmailNotificationChannelTest.java
@@ -20,63 +20,160 @@
package org.sonar.server.notifications.email;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import com.dumbster.smtp.SimpleSmtpServer;
+import com.dumbster.smtp.SmtpMessage;
+import org.apache.commons.mail.EmailException;
+import org.junit.After;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
+import java.io.IOException;
+import java.net.ServerSocket;
+
public class EmailNotificationChannelTest {
+ private static int port;
+
+ private SimpleSmtpServer server;
+
+ private EmailConfiguration configuration;
private EmailNotificationChannel channel;
+ @BeforeClass
+ public static void selectPort() {
+ port = getNextAvailablePort();
+ }
+
+ private static int getNextAvailablePort() {
+ try {
+ ServerSocket socket = new ServerSocket(0);
+ int unusedPort = socket.getLocalPort();
+ socket.close();
+ return unusedPort;
+ } catch (IOException e) {
+ throw new RuntimeException("Error getting an available port from system", e);
+ }
+ }
+
@Before
public void setUp() {
- channel = new EmailNotificationChannel(null);
+ server = SimpleSmtpServer.start(port);
+ configuration = mock(EmailConfiguration.class);
+ channel = new EmailNotificationChannel(configuration, null, null);
+ }
+
+ @After
+ public void tearDown() {
+ if (!server.isStopped()) {
+ server.stop();
+ }
+ }
+
+ @Test
+ public void shouldSendTestEmail() throws Exception {
+ configure();
+ channel.sendTestEmail("user@nowhere", "Test Message from Sonar", "This is a test message from Sonar.");
+
+ assertThat(server.getReceivedEmailSize(), is(1));
+ SmtpMessage email = (SmtpMessage) server.getReceivedEmail().next();
+
+ assertThat(email.getHeaderValue("From"), is("Sonar <server@nowhere>"));
+ assertThat(email.getHeaderValue("To"), is("<user@nowhere>"));
+ assertThat(email.getHeaderValue("Subject"), is("[SONAR] Test Message from Sonar"));
+ assertThat(email.getBody(), is("This is a test message from Sonar."));
+ }
+
+ @Test(expected = EmailException.class)
+ public void shouldThrowAnExceptionWhenUnableToSendTestEmail() throws Exception {
+ configure();
+ server.stop();
+
+ channel.sendTestEmail("user@nowhere", "Test Message from Sonar", "This is a test message from Sonar.");
}
@Test
- public void shouldCreateEmail() {
- EmailMessage email = new EmailMessage()
+ public void shouldNotSendEmailWhenHostnameNotConfigured() throws Exception {
+ EmailMessage emailMessage = new EmailMessage()
+ .setTo("user@nowhere")
+ .setSubject("Foo")
+ .setMessage("Bar");
+ channel.deliver(emailMessage);
+ assertThat(server.getReceivedEmailSize(), is(0));
+ }
+
+ @Test
+ public void shouldSendThreadedEmail() throws Exception {
+ configure();
+ EmailMessage emailMessage = new EmailMessage()
.setMessageId("reviews/view/1")
- .setFrom("Evgeny Mandrikov")
- .setTo("simon.brandhof@sonarcource.com")
+ .setFrom("Full Username")
+ .setTo("user@nowhere")
.setSubject("Review #3")
.setMessage("I'll take care of this violation.");
- String expected = "" +
- "Message-Id: <reviews/view/1@nemo.sonarsource.org>\n" +
- "In-Reply-To: <reviews/view/1@nemo.sonarsource.org>\n" +
- "References: <reviews/view/1@nemo.sonarsource.org>\n" +
- "List-Id: <sonar.nemo.sonarsource.org>\n" +
- "List-Archive: http://nemo.sonarsource.org\n" +
- "From: Evgeny Mandrikov <noreply@nemo.sonarsource.org>\n" +
- "To: simon.brandhof@sonarcource.com\n" +
- "Subject: Re: [Sonar] Review #3\n" +
- "\n" +
- "I'll take care of this violation.\n" +
- "\n" +
- "--\n" +
- "View it in Sonar: http://nemo.sonarsource.org/reviews/view/1";
- String message = channel.create(email);
- System.out.println(message);
- assertThat(message, is(expected));
+ channel.deliver(emailMessage);
+
+ assertThat(server.getReceivedEmailSize(), is(1));
+ SmtpMessage email = (SmtpMessage) server.getReceivedEmail().next();
+
+ assertThat(email.getHeaderValue("In-Reply-To"), is("<reviews/view/1@nemo.sonarsource.org>"));
+ assertThat(email.getHeaderValue("References"), is("<reviews/view/1@nemo.sonarsource.org>"));
+
+ assertThat(email.getHeaderValue("List-Id"), is("<sonar.nemo.sonarsource.org>"));
+ assertThat(email.getHeaderValue("List-Archive"), is("http://nemo.sonarsource.org"));
+
+ assertThat(email.getHeaderValue("From"), is("Full Username <server@nowhere>"));
+ assertThat(email.getHeaderValue("To"), is("<user@nowhere>"));
+ assertThat(email.getHeaderValue("Subject"), is("[SONAR] Review #3"));
+ assertThat(email.getBody(), is("I'll take care of this violation."));
}
@Test
- public void shouldCreateDefaultEmail() {
- EmailMessage email = new EmailMessage()
- .setTo("simon.brandhof@sonarcource.com")
- .setMessage("Message");
- String expected = "" +
- "List-Id: <sonar.nemo.sonarsource.org>\n" +
- "List-Archive: http://nemo.sonarsource.org\n" +
- "From: Sonar <noreply@nemo.sonarsource.org>\n" +
- "To: simon.brandhof@sonarcource.com\n" +
- "Subject: [Sonar] Notification\n" +
- "\n" +
- "Message\n";
- String message = channel.create(email);
- System.out.println(message);
- assertThat(message, is(expected));
+ public void shouldSendNonThreadedEmail() {
+ configure();
+ EmailMessage emailMessage = new EmailMessage()
+ .setTo("user@nowhere")
+ .setSubject("Foo")
+ .setMessage("Bar");
+ channel.deliver(emailMessage);
+
+ assertThat(server.getReceivedEmailSize(), is(1));
+ SmtpMessage email = (SmtpMessage) server.getReceivedEmail().next();
+
+ assertThat(email.getHeaderValue("In-Reply-To"), nullValue());
+ assertThat(email.getHeaderValue("References"), nullValue());
+
+ assertThat(email.getHeaderValue("List-Id"), is("<sonar.nemo.sonarsource.org>"));
+ assertThat(email.getHeaderValue("List-Archive"), is("http://nemo.sonarsource.org"));
+
+ assertThat(email.getHeaderValue("From"), is("Sonar <server@nowhere>"));
+ assertThat(email.getHeaderValue("To"), is("<user@nowhere>"));
+ assertThat(email.getHeaderValue("Subject"), is("[SONAR] Foo"));
+ assertThat(email.getBody(), is("Bar"));
+ }
+
+ @Test
+ public void shouldNotThrowAnExceptionWhenUnableToSendEmail() {
+ configure();
+ server.stop();
+
+ EmailMessage emailMessage = new EmailMessage()
+ .setTo("user@nowhere")
+ .setSubject("Foo")
+ .setMessage("Bar");
+ channel.deliver(emailMessage);
+ }
+
+ private void configure() {
+ when(configuration.getSmtpHost()).thenReturn("localhost");
+ when(configuration.getSmtpPort()).thenReturn(Integer.toString(port));
+ when(configuration.getFrom()).thenReturn("server@nowhere");
+ when(configuration.getPrefix()).thenReturn("[SONAR]");
}
}