/*
* Copyright 2017 ThoughtWorks, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.thoughtworks.go.config;
import com.thoughtworks.go.domain.materials.ValidationBean;
import com.thoughtworks.go.security.GoCipher;
import com.thoughtworks.go.util.GoConstants;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.mockito.ArgumentCaptor;
import javax.mail.Address;
import javax.mail.Transport;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.*;
@RunWith(BlockJUnit4ClassRunner.class)
public class GoSmtpMailSenderTest {
private static final String SMTP_CONNECTIONTIMEOUT_PROPERTY = "mail.smtp.connectiontimeout";
private static final String SMTP_TIMEOUT_PROPERTY = "mail.smtp.timeout";
public static final String MAIL_FROM_PROPERTY = "mail.from";
public static final String MAIL_TRANSPORT_PROTOCOL_PROPERTY = "mail.transport.protocol";
private static final String MAIL_SMTP_STARTTLS_PROPERTY = "mail.smtp.starttls.enable";
private final String hostName = "smtp.company.test";
private final String from = "from.cruise.test@gmail.com";
private final String to = "cruise.test.admin@gmail.com";
private final String subject = "Subject";
private final String body = "Body";
private final int port = 25;
private final String username = "cruise2";
private final String password = "password123";
@After
public void tearDown() throws Exception {
FakeMailSession.tearDown();
}
@Test
public void testShouldNotSendOutTestEmailToAdminstrator() {
GoSmtpMailSender sender = new GoSmtpMailSender(hostName, 465, to, password, true, from, to);
ValidationBean bean = sender.send(subject, body, to);
assertThat(bean, is(not(ValidationBean.valid())));
}
@Test
public void shouldNotSendTestEmailToSmtpServerIfTlsConfiguredIncorrect() {
GoSmtpMailSender sender = new GoSmtpMailSender(hostName, 25, username, password, true, from, to);
ValidationBean bean = sender.send(subject, body, to);
assertThat(bean, is(not(ValidationBean.valid())));
}
@Test
public void shouldSendAMailWhenValidationSucceeds() throws Exception {
FakeMailSession mailSession = FakeMailSession.setupFor(username, password, from, to, subject, body);
GoSmtpMailSender sender = new GoSmtpMailSender(hostName, port, username, password, true, from, to);
ValidationBean bean = sender.send(subject, body, to);
assertThat(bean, is(ValidationBean.valid()));
mailSession.verifyThatConnectionWasMadeTo(hostName, port, username, password);
mailSession.verifyMessageWasSent();
mailSession.verifyTransportWasClosed();
}
@Test
public void shouldSet_MailFrom_Property() throws Exception {
FakeMailSession mailSession = FakeMailSession.setupFor(username, password, from, to, subject, body);
GoSmtpMailSender sender = new GoSmtpMailSender(hostName, port, username, password, true, from, to);
ValidationBean bean = sender.send(subject, body, to);
assertThat(bean, is(ValidationBean.valid()));
mailSession.verifyMessageWasSent();
mailSession.verifyProperty(MAIL_FROM_PROPERTY, from);
}
@Test
public void shouldSetDefaultMailTimeoutPropertiesWhenNoOverridingValuesAreProvided() throws Exception {
FakeMailSession mailSession = FakeMailSession.setupFor(username, password, from, to, subject, body);
GoSmtpMailSender sender = new GoSmtpMailSender(hostName, port, username, password, true, from, to);
ValidationBean bean = sender.send(subject, body, to);
assertThat(bean, is(ValidationBean.valid()));
mailSession.verifyMessageWasSent();
mailSession.verifyProperty(SMTP_CONNECTIONTIMEOUT_PROPERTY, GoConstants.DEFAULT_TIMEOUT);
mailSession.verifyProperty(SMTP_TIMEOUT_PROPERTY, GoConstants.DEFAULT_TIMEOUT);
}
@Test
public void shouldNotOverrideSMTPConnectionTimeoutPropertyIfItIsSetAtSystemLevel() throws Exception {
FakeMailSession mailSession = FakeMailSession.setupFor(username, password, from, to, subject, body);
GoSmtpMailSender sender = new GoSmtpMailSender(hostName, port, username, password, true, from, to);
sendMailWithPropertySetTo(sender, SMTP_CONNECTIONTIMEOUT_PROPERTY, "12345");
mailSession.verifyMessageWasSent();
mailSession.verifyPropertyDoesNotExist(SMTP_CONNECTIONTIMEOUT_PROPERTY);
mailSession.verifyProperty(SMTP_TIMEOUT_PROPERTY, GoConstants.DEFAULT_TIMEOUT);
}
@Test
public void shouldNotOverrideSMTPTimeoutPropertyIfItIsSetAtSystemLevel() throws Exception {
FakeMailSession mailSession = FakeMailSession.setupFor(username, password, from, to, subject, body);
GoSmtpMailSender sender = new GoSmtpMailSender(hostName, port, username, password, true, from, to);
sendMailWithPropertySetTo(sender, SMTP_TIMEOUT_PROPERTY, "12345");
mailSession.verifyMessageWasSent();
mailSession.verifyProperty(SMTP_CONNECTIONTIMEOUT_PROPERTY, GoConstants.DEFAULT_TIMEOUT);
mailSession.verifyPropertyDoesNotExist(SMTP_TIMEOUT_PROPERTY);
}
@Test
public void shouldSetProtocolToSMTPSWhenSMTPSIsEnabled() throws Exception {
FakeMailSession mailSession = FakeMailSession.setupFor(username, password, from, to, subject, body);
boolean isSMTPSEnabled = true;
GoSmtpMailSender sender = new GoSmtpMailSender(hostName, port, username, password, isSMTPSEnabled, from, to);
ValidationBean bean = sender.send(subject, body, to);
assertThat(bean, is(ValidationBean.valid()));
mailSession.verifyMessageWasSent();
mailSession.verifyProperty(MAIL_TRANSPORT_PROTOCOL_PROPERTY, "smtps");
}
@Test
public void shouldSetProtocolToSMTPWhenSMTPSIsDisabled() throws Exception {
FakeMailSession mailSession = FakeMailSession.setupFor(username, password, from, to, subject, body);
boolean isSMTPSEnabled = false;
GoSmtpMailSender sender = new GoSmtpMailSender(hostName, port, username, password, isSMTPSEnabled, from, to);
ValidationBean bean = sender.send(subject, body, to);
assertThat(bean, is(ValidationBean.valid()));
mailSession.verifyMessageWasSent();
mailSession.verifyProperty(MAIL_TRANSPORT_PROTOCOL_PROPERTY, "smtp");
}
@Test
public void shouldEnableSMTPS_With_StartTLS_WhenThePropertyIsSet() throws Exception {
FakeMailSession mailSession = FakeMailSession.setupFor(username, password, from, to, subject, body);
GoSmtpMailSender sender = new GoSmtpMailSender(hostName, port, username, password, false, from, to);
sendMailWithPropertySetTo(sender, MAIL_SMTP_STARTTLS_PROPERTY, "any-non-null-value");
mailSession.verifyMessageWasSent();
mailSession.verifyProperty(MAIL_SMTP_STARTTLS_PROPERTY, "true");
}
@Test
public void shouldNotEnableSMTPS_With_StartTLS_WhenThePropertyIsNotSet() throws Exception {
FakeMailSession mailSession = FakeMailSession.setupFor(username, password, from, to, subject, body);
GoSmtpMailSender sender = new GoSmtpMailSender(hostName, port, username, password, false, from, to);
sendMailWithPropertySetTo(sender, MAIL_SMTP_STARTTLS_PROPERTY, null);
mailSession.verifyMessageWasSent();
mailSession.verifyPropertyDoesNotExist(MAIL_SMTP_STARTTLS_PROPERTY);
}
@Test
public void shouldCreateSmtpMailSender() throws Exception {
MailHost mailHost = new MailHost(hostName, 25, "smtpuser", "password", true, true, "cruise@me.com", "jez@me.com");
GoMailSender sender = GoSmtpMailSender.createSender(mailHost);
GoMailSender goSmtpMailSender = new GoSmtpMailSender(hostName, 25, "smtpuser", "password", true, "cruise@me.com", "jez@me.com");
GoMailSender backgroundSender = new BackgroundMailSender(goSmtpMailSender);
assertThat(sender, is(backgroundSender));
}
@Test
public void shouldUseTheNewPasswordIfThePasswordIsChanged() {
MailHost mailHost = new MailHost(hostName, 25, "smtpuser", "password", "encrypted_password", true, true, "cruise@me.com", "jez@me.com");
GoMailSender sender = GoSmtpMailSender.createSender(mailHost);
assertThat(sender, is(new BackgroundMailSender(new GoSmtpMailSender(hostName, 25, "smtpuser", "password", true, "cruise@me.com", "jez@me.com"))));
}
@Test
public void shouldUseTheEncryptedPasswordIfThePasswordIsNotChanged() throws InvalidCipherTextException {
String encryptedPassword = new GoCipher().encrypt("encrypted_password");
MailHost mailHost = new MailHost(hostName, 25, "smtpuser", "password", encryptedPassword, false, true, "cruise@me.com", "jez@me.com");
GoMailSender sender = GoSmtpMailSender.createSender(mailHost);
assertThat(sender, is(new BackgroundMailSender(new GoSmtpMailSender(hostName, 25, "smtpuser", "encrypted_password", true, "cruise@me.com", "jez@me.com"))));
}
private void sendMailWithPropertySetTo(GoSmtpMailSender sender, String property, String value) {
String oldValue = System.getProperty(property);
if (value == null) {
System.clearProperty(property);
} else {
System.setProperty(property, value);
}
try {
sender.send(subject, body, to);
} finally {
if (oldValue == null) {
System.clearProperty(property);
} else {
System.setProperty(property, oldValue);
}
}
}
private static class FakeMailSession {
private Transport transport = mock(Transport.class);
private MimeMessage message = mock(MimeMessage.class);
private MailSession session = mock(MailSession.class);
public static void tearDown() {
MailSession.fakeSessionJustForTestsSinceSessionClassIsFinal = null;
}
public static FakeMailSession setupFor(String username, String password, String from, String to, String subject, String body) throws Exception {
return new FakeMailSession(username, password, from, to, subject, body);
}
public FakeMailSession(String username, String password, String from, String to, String subject, String body) throws Exception {
MailSession.fakeSessionJustForTestsSinceSessionClassIsFinal = session;
when(session.getTransport()).thenReturn(transport);
when(session.createWith(any(Properties.class), eq(username), eq(password))).thenReturn(session);
when(session.createMessage(from, to, subject, body)).thenReturn(message);
}
public void verifyMessageWasSent() throws Exception {
verify(transport).sendMessage(eq(message), any(Address[].class));
}
public void verifyThatConnectionWasMadeTo(String hostName, int port, String username, String password) throws Exception {
verify(transport).connect(hostName, port, username, password);
}
public void verifyProperty(String expectedProperty, Object expectedValueOfProperty) {
Properties properties = getPropertiesUsedInSession();
if (!expectedValueOfProperty.equals(properties.get(expectedProperty))) {
fail("Could not find property: " + expectedProperty + " with value: " + expectedValueOfProperty + ". Properties found were: " + properties);
}
}
public void verifyPropertyDoesNotExist(String propertyWhichShouldNotExist) {
Properties properties = getPropertiesUsedInSession();
if (properties.containsKey(propertyWhichShouldNotExist)) {
fail("Did not expect to find property: " + propertyWhichShouldNotExist + ". Found it in properties list: " + properties);
}
}
public void verifyTransportWasClosed() throws Exception {
verify(transport).close();
}
private Properties getPropertiesUsedInSession() {
ArgumentCaptor<Properties> propertyCaptor = ArgumentCaptor.forClass(Properties.class);
verify(session).createWith(propertyCaptor.capture(), any(String.class), any(String.class));
return propertyCaptor.getValue();
}
}
}