package com.constellio.model.services.emails;
import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from;
import static com.constellio.sdk.tests.TestUtils.assertThatRecord;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import javax.mail.Message;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.LocalDateTime;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import com.constellio.app.modules.rm.RMEmailTemplateConstants;
import com.constellio.model.conf.email.EmailConfigurationsManager;
import com.constellio.model.conf.email.EmailServerConfiguration;
import com.constellio.model.entities.records.wrappers.EmailToSend;
import com.constellio.model.entities.structures.EmailAddress;
import com.constellio.model.services.emails.EmailServicesException.EmailPermanentException;
import com.constellio.model.services.emails.EmailServicesException.EmailServerException;
import com.constellio.model.services.emails.EmailServicesException.EmailTempException;
import com.constellio.model.services.records.RecordServices;
import com.constellio.model.services.records.RecordServicesException;
import com.constellio.model.services.records.SchemasRecordsServices;
import com.constellio.model.services.search.SearchServices;
import com.constellio.model.services.search.query.logical.LogicalSearchQuery;
import com.constellio.model.services.search.query.logical.condition.LogicalSearchCondition;
import com.constellio.sdk.SDKPasswords;
import com.constellio.sdk.tests.ConstellioTest;
// TODO Nouha: Fix test
public class EmailQueueManagerAcceptanceTest extends ConstellioTest {
EmailQueueManager emailQueueManager;
@Mock EmailServices emailServices;
private SearchServices searchServices;
private RecordServices recordServices;
LocalDateTime now = new LocalDateTime();
SchemasRecordsServices schemas;
private SchemasRecordsServices businessSchemas;
EmailConfigurationsManager emailConfigurationsManager;
EmailAddress testEmail;
@Before
public void setUp()
throws Exception {
prepareSystem(
withZeCollection().withConstellioRMModule().withAllTestUsers(),
withCollection(businessCollection).withConstellioRMModule().withAllTestUsers()
);
givenTimeIs(now);
emailConfigurationsManager = getModelLayerFactory().getEmailConfigurationsManager();
emailQueueManager = spy(new EmailQueueManager(getModelLayerFactory(), emailServices));
searchServices = getModelLayerFactory().newSearchServices();
recordServices = getModelLayerFactory().newRecordServices();
schemas = new SchemasRecordsServices(zeCollection, getModelLayerFactory());
businessSchemas = new SchemasRecordsServices(businessCollection, getModelLayerFactory());
testEmail = new EmailAddress("name", SDKPasswords.testPOP3Username());
getModelLayerFactory().getEmailConfigurationsManager().addEmailServerConfiguration(new SmtpServerTestConfig(),
zeCollection);
getModelLayerFactory().getEmailConfigurationsManager().addEmailServerConfiguration(new SmtpServerTestConfig(),
businessCollection);
}
@Test
public void givenRecordWithMaxTryThenWhenNotSentCorrectlyThenDeleted()
throws EmailServerException {
addEmailToSend(EmailQueueManager.MAX_TRY_SEND, now.minusDays(1));
when(emailServices.openSession(any(EmailServerConfiguration.class))).thenThrow(new EmailServerException(new Exception()));
emailQueueManager.sendEmails();
assertNoEmailToSend();
}
@Test
public void givenRecordWithNumberOfTryLowerThanMaxTryThenWhenNotSentCorrectlyThenPostponed()
throws EmailServerException {
addEmailToSend(EmailQueueManager.MAX_TRY_SEND - 1, now.minusDays(1));
when(emailServices.openSession(any(EmailServerConfiguration.class))).thenThrow(new EmailServerException(new Exception()));
emailQueueManager.sendEmails();
assertOneEmailToSendWithDateAndWithNumberOfTry(now.plusDays(1), EmailQueueManager.MAX_TRY_SEND);
}
@Test
public void givenValidRecordWhenSentCorrectlyThenRemoved()
throws Exception {
addEmailToSend(0, now.minusDays(1), SDKPasswords.testPOP3Username());
emailQueueManager.sendEmails();
verify(emailServices, times(1)).sendEmail(any(Message.class));
assertNoEmailToSend();
}
@Test
public void givenTwoValidRecordsInZeCollectionAndThreeValidEmailInBusinessCollectionWhenSentCorrectlyThenRemoved()
throws Exception {
EmailQueueManager.SEND_EMAIL_BATCH = 1;
addEmailToSend(0, now.minusDays(2));
addEmailToSend(1, now.minusDays(1));
addEmailToSendInBusinessCollection(1, now.minusDays(1));
addEmailToSendInBusinessCollection(0, now.minusDays(1));
addEmailToSendInBusinessCollection(1, now.minusDays(3));
assertThat(searchServices.getResultsCount(from(schemas.emailToSend()).returnAll())).isEqualTo(2);
assertThat(searchServices.getResultsCount(from(businessSchemas.emailToSend()).returnAll())).isEqualTo(3);
emailQueueManager.sendEmails();
verify(emailServices, times(5)).sendEmail(any(Message.class));
assertNoEmailToSend();
}
@Test
public void givenEmailWithBlankFromThenRemoved()
throws Exception {
addEmailToSend(0, now.minusDays(1), "");
getModelLayerFactory().getEmailConfigurationsManager().updateEmailServerConfiguration(new SmtpServerTestConfig() {
@Override
public String getDefaultSenderEmail() {
return null;
}
}, zeCollection, true);
emailQueueManager.sendEmails();
verify(emailServices, times(0)).sendEmail(any(Message.class));
assertNoEmailToSend();
}
@Test
public void givenValidRecordWhenEmailTempExceptionThenPostponed()
throws Exception {
addEmailToSend(0, now.minusDays(1), SDKPasswords.testPOP3Username());
doThrow(new EmailTempException(new Exception())).when(emailServices)
.sendEmail(any(Message.class));
emailQueueManager.sendEmails();
assertOneEmailToSendWithDateAndWithNumberOfTry(now.plusDays(1), 1);
}
@Test
public void givenValidRecordWhenEmailPermanentExceptionThenDeleted()
throws Exception {
addEmailToSend(0, now.minusDays(1));
doThrow(new EmailPermanentException(new Exception())).when(emailServices)
.sendEmail(any(Message.class));
emailQueueManager.sendEmails();
assertNoEmailToSend();
}
@Test
public void givenSmtpServerIsDisabledThenEmailToSendDeletedAndNoInteractionWithEmailServices()
throws Exception {
emailConfigurationsManager.updateEmailServerConfiguration(new SmtpServerTestConfig().setEnabled(false), zeCollection, true);
addEmailToSend(0, now.minusDays(1));
addEmailToSend(0, now.minusDays(1));
addEmailToSend(0, now.minusDays(1));
emailQueueManager.sendEmails();
assertNoEmailToSend();
verifyZeroInteractions(emailServices);
}
@Test
public void given19EmailsFailedToBeSentWhenThe20ththrowAPermanentExceptionThenDisableSmtpServer()
throws Exception {
doThrow(EmailPermanentException.class).when(emailServices).sendEmail(any(Message.class));
for (int i = 0; i < EmailQueueManager.MAXIMUM_FAILURES_BEFORE_DISABLING_SMTP_SERVER - 1; i++) {
addEmailToSend(0, now.minusDays(1));
}
emailQueueManager.sendEmails();
assertThat(emailConfigurationsManager.getEmailConfiguration(zeCollection, false).isEnabled()).isTrue();
addEmailToSend(0, now.minusDays(1));
emailQueueManager.sendEmails();
assertThat(emailConfigurationsManager.getEmailConfiguration(zeCollection, false).isEnabled()).isFalse();
}
@Test
public void given19EmailsFailedToBeSentWhenThe20ththrowATemporaryExceptionThenDisableSmtpServer()
throws Exception {
doThrow(EmailTempException.class).when(emailServices).sendEmail(any(Message.class));
for (int i = 0; i < EmailQueueManager.MAXIMUM_FAILURES_BEFORE_DISABLING_SMTP_SERVER - 1; i++) {
addEmailToSend(0, now.minusDays(1));
}
emailQueueManager.sendEmails();
assertThat(emailConfigurationsManager.getEmailConfiguration(zeCollection, false).isEnabled()).isTrue();
addEmailToSend(0, now.minusDays(1));
emailQueueManager.sendEmails();
assertThat(emailConfigurationsManager.getEmailConfiguration(zeCollection, false).isEnabled()).isFalse();
}
@Test
public void given19EmailsFailedToBeSentWhenThe20thSucceedThenSmtpServerStillEnabled()
throws Exception {
doThrow(EmailPermanentException.class).when(emailServices).sendEmail(any(Message.class));
for (int i = 0; i < EmailQueueManager.MAXIMUM_FAILURES_BEFORE_DISABLING_SMTP_SERVER - 1; i++) {
addEmailToSend(0, now.minusDays(1));
}
emailQueueManager.sendEmails();
assertThat(emailConfigurationsManager.getEmailConfiguration(zeCollection, false).isEnabled()).isTrue();
doNothing().when(emailServices).sendEmail(any(Message.class));
addEmailToSend(0, now.minusDays(1));
emailQueueManager.sendEmails();
assertThat(emailConfigurationsManager.getEmailConfiguration(zeCollection, false).isEnabled()).isTrue();
doThrow(EmailPermanentException.class).when(emailServices).sendEmail(any(Message.class));
addEmailToSend(0, now.minusDays(1));
emailQueueManager.sendEmails();
assertThat(emailConfigurationsManager.getEmailConfiguration(zeCollection, false).isEnabled()).isTrue();
}
private void assertNoEmailToSend() {
LogicalSearchCondition condition = from(schemas.emailToSend()).returnAll();
LogicalSearchQuery query = new LogicalSearchQuery(condition);
assertThat(searchServices.getResultsCount(query)).isEqualTo(0);
}
private void assertOneEmailToSendWithDateAndWithNumberOfTry(LocalDateTime expectedDate, double expectedNumberOfTry) {
assertThatRecord(
searchServices.searchSingleResult(from(schemas.emailToSend()).returnAll()))
.hasMetadataValue(schemas.emailToSend().getMetadata(EmailToSend.SEND_ON), expectedDate)
.hasMetadataValue(schemas.emailToSend().getMetadata(EmailToSend.TRYING_COUNT), expectedNumberOfTry);
}
private EmailToSend addEmailToSend(double numberOfTry, LocalDateTime sendDate, String fromEmail) {
EmailToSend email = schemas.newEmailToSend().setTryingCount(numberOfTry).setSendOn(sendDate)
.setTemplate(RMEmailTemplateConstants.REMIND_BORROW_TEMPLATE_ID);
if (StringUtils.isNotBlank(fromEmail)) {
EmailAddress from = new EmailAddress(fromEmail, fromEmail);
email = email.setFrom(from);
}
try {
recordServices.add(email);
} catch (RecordServicesException e) {
throw new RuntimeException(e);
}
return email;
}
private EmailToSend addEmailToSend(int numberOfTry, LocalDateTime sendDate) {
return addEmailToSend(numberOfTry, sendDate, null);
}
private EmailToSend addEmailToSendInBusinessCollection(double numberOfTry, LocalDateTime sendDate) {
EmailToSend email = businessSchemas.newEmailToSend().setTryingCount(numberOfTry).setSendOn(sendDate)
.setTemplate(RMEmailTemplateConstants.REMIND_BORROW_TEMPLATE_ID);
try {
recordServices.add(email);
} catch (RecordServicesException e) {
throw new RuntimeException(e);
}
return email;
}
}