package de.asideas.crowdsource.service; import de.asideas.crowdsource.config.mail.MailTemplateConfig; import de.asideas.crowdsource.domain.model.CommentEntity; import de.asideas.crowdsource.domain.model.ProjectEntity; import de.asideas.crowdsource.domain.model.UserEntity; import de.asideas.crowdsource.domain.service.user.UserNotificationService; import de.asideas.crowdsource.domain.shared.ProjectStatus; import de.asideas.crowdsource.repository.UserRepository; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.util.ReflectionTestUtils; import java.util.Arrays; import java.util.List; import java.util.stream.IntStream; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.core.Is.is; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {MailTemplateConfig.class, MailTest.Config.class}) @SuppressWarnings("Duplicates") public class MailTest { private static final String ADMIN_EMAIL = "some.admin@email.com"; @Autowired private UserNotificationService userNotificationService; @Autowired private JavaMailSender javaMailSender; @Autowired private AsyncTaskExecutor taskExecutorSmtp; @Autowired private UserRepository userRepository; @Before public void setUp() { ReflectionTestUtils.setField(userNotificationService, "applicationUrl", "https://crowd.asideas.de"); reset(javaMailSender, taskExecutorSmtp); // Emulate async execution synchronously to immedately be able to verify invocations of javaMailSender doAnswer(invocation -> { final Runnable runnable = (Runnable) invocation.getArguments()[0]; runnable.run(); return null; }).when(taskExecutorSmtp).submit(isA(Runnable.class)); } @Test public void testSendActivationMail() { UserEntity user = aProjectCreator(); user.setActivationToken("activationTok3n"); userNotificationService.sendActivationMail(user); SimpleMailMessage mail = getMessageFromMailSender(); assertThat(mail.getFrom(), is(UserNotificationService.FROM_ADDRESS)); assertThat(mail.getTo(), arrayContaining(user.getEmail())); assertThat(mail.getSubject(), is(UserNotificationService.SUBJECT_ACTIVATION)); assertThat(replaceLineBreaksIfWindows(mail.getText()), is( "Hallo Some Creator,\n\n" + "Du hast Dich gerade auf der CrowdSource Platform angemeldet.\n" + "Um Deine Registrierung abzuschließen, öffne bitte diesen Link und setze Dein Passwort:\n\n" + "https://crowd.asideas.de#/signup/some.creator@email.com/activation/activationTok3n\n\n" + "Mit freundlichen Grüßen\nDein CrowdSource Team")); } @Test public void testSendPasswordRecoveryMail() { UserEntity user = aProjectCreator(); user.setActivationToken("activationTok3n"); userNotificationService.sendPasswordRecoveryMail(user); SimpleMailMessage mail = getMessageFromMailSender(); assertThat(mail.getFrom(), is(UserNotificationService.FROM_ADDRESS)); assertThat(mail.getTo(), arrayContaining(user.getEmail())); assertThat(mail.getSubject(), is(UserNotificationService.SUBJECT_PASSWORD_FORGOTTEN)); assertThat(replaceLineBreaksIfWindows(mail.getText()), is( "Hallo Some Creator,\n\n" + "Du hast soeben ein neues Passwort für Dein Konto bei der CrowdSource Plattform angefordert.\n\n" + "Bitte öffne diesen Link:\n\n" + "https://crowd.asideas.de#/login/password-recovery/some.creator@email.com/activation/activationTok3n\n\n" + "und setze Dein neues Passwort.\n\n" + "Mit freundlichen Grüßen\n" + "Dein CrowdSource Team")); } @Test public void testSendUserNotificationMailForPublished() { UserEntity user = aProjectCreator(); userNotificationService.notifyCreatorOnProjectStatusUpdate(project("proj3ctId", ProjectStatus.PUBLISHED, user, "My Super Project")); SimpleMailMessage mail = getMessageFromMailSender(); assertThat(mail.getFrom(), is(UserNotificationService.FROM_ADDRESS)); assertThat(mail.getTo(), arrayContaining(user.getEmail())); assertThat(mail.getSubject(), is(UserNotificationService.SUBJECT_PROJECT_PUBLISHED)); assertThat(replaceLineBreaksIfWindows(mail.getText()), is( "Hallo Some Creator,\n\n" + "Dein Projekt wurde erfolgreich freigegeben!\n" + "Weitere Informationen hinsichtlich des Prozesses kannst Du der FAQ entnehmen.\n\n" + "Zu Deinem Projekt:\n\n" + "https://crowd.asideas.de#/project/proj3ctId\n\n" + "Mit freundlichen Grüßen\n" + "Dein CrowdSource Team")); } @Test public void testSendUserNotificationMailForRejected() { UserEntity user = aProjectCreator(); userNotificationService.notifyCreatorOnProjectStatusUpdate(project("proj3ctId", ProjectStatus.REJECTED, user, "My Super Project")); SimpleMailMessage mail = getMessageFromMailSender(); assertThat(mail.getFrom(), is(UserNotificationService.FROM_ADDRESS)); assertThat(mail.getTo(), arrayContaining(user.getEmail())); assertThat(mail.getSubject(), is(UserNotificationService.SUBJECT_PROJECT_REJECTED)); assertThat(replaceLineBreaksIfWindows(mail.getText()), is( "Hallo Some Creator,\n\n" + "Dein Projekt wurde leider abgelehnt.\n" + "Das CrowdSource Team wird in Kürze mit Dir in Kontakt treten, um die nächsten Schritte zu besprechen.\n\n" + "Zu Deinem Projekt:\n\n" + "https://crowd.asideas.de#/project/proj3ctId\n\n" + "Mit freundlichen Grüßen\nDein CrowdSource Team")); } @Test public void testSendUserNotificationMailForFallback() { UserEntity user = aProjectCreator(); userNotificationService.notifyCreatorOnProjectStatusUpdate(project("proj3ctId", ProjectStatus.PROPOSED, user, "My Super Project")); SimpleMailMessage mail = getMessageFromMailSender(); assertThat(mail.getFrom(), is(UserNotificationService.FROM_ADDRESS)); assertThat(mail.getTo(), arrayContaining(user.getEmail())); assertThat(mail.getSubject(), is("Der Zustand des Projekts My Super Project hat sich geändert!")); assertThat(mail.getText(), is("Das Projekt My Super Project wurde in den Zustand PROPOSED versetzt.")); } @Test public void testNotifyAdminOnProjectCreation() { UserEntity user = aProjectCreator(); userNotificationService.notifyAdminOnProjectCreation(project("proj3ctId", ProjectStatus.PUBLISHED, user, "My Super Project"), ADMIN_EMAIL); SimpleMailMessage mail = getMessageFromMailSender(); assertThat(mail.getFrom(), is(UserNotificationService.FROM_ADDRESS)); assertThat(mail.getTo(), arrayContaining(ADMIN_EMAIL)); assertThat(mail.getSubject(), is(UserNotificationService.SUBJECT_PROJECT_CREATED)); assertThat(replaceLineBreaksIfWindows(mail.getText()), is( "Hallo Admin,\n\n" + "es liegt ein neues Projekt zur Freigabe vor:\n\n" + "https://crowd.asideas.de#/project/proj3ctId\n\n" + "Mit freundlichen Grüßen\n" + "Dein CrowdSource Team")); } @Test public void notifyCreatorAndAdminOnProjectModification() { final UserEntity creator = aProjectCreator(); final UserEntity modifier = aUser("test_id_modifier"); final UserEntity admin = aUser("test_id_admin_0", "admin.0@email.com"); final List<UserEntity> admins = Arrays.asList(admin); final ProjectEntity project = project("proj3ctId", ProjectStatus.PUBLISHED, creator, "My Super Project"); when(userRepository.findAllAdminUsers()).thenReturn(admins); userNotificationService.notifyCreatorAndAdminOnProjectModification(project, modifier); final List<SimpleMailMessage> capturedMessages = getMessagesFromMailSender(); assertThat(capturedMessages.size(), is(3)); assertCreatorModifierAndAdminNotifiedOfProjectEdit(creator, modifier, admin, capturedMessages); capturedMessages.stream().forEach(mail -> { assertThat(mail.getFrom(), is(UserNotificationService.FROM_ADDRESS)); assertThat(mail.getSubject(), is(UserNotificationService.SUBJECT_PROJECT_MODIFIED)); }); } @Test public void notifyCreatorOnComment_mailShouldContainAbridgedCommentAndItsCreatorName() { final UserEntity creator = aProjectCreator(); final UserEntity commentingUser = aUser("test_id_modifier"); final ProjectEntity project = project("proj3ctId", ProjectStatus.PUBLISHED, creator, "My Super Project"); final String projectLink = "https://crowd.asideas.de#/project/proj3ctId"; final String testComment = aTestComment(UserNotificationService.COMMENT_EXCERPT_LENGTH + 5); final CommentEntity comment = new CommentEntity(project, commentingUser, testComment); userNotificationService.notifyCreatorOnComment(comment); assertCommentMessage(comment, projectLink, getMessageFromMailSender()); } private void assertCommentMessage(CommentEntity comment, String projectLink, SimpleMailMessage actualMessage) { final String expectedMessage = "Hallo %s,\n\n" + "zu Deinem Projekt \"%s\" wurde ein Kommentar von %s hinzugefügt:\n\n" + "\"%s\"\n\n" + "Zu Deinem Projekt:\n\n%s\n\n" + "Mit freundlichen Grüßen\n" + "Dein CrowdSource Team"; assertThat(actualMessage.getText(), is(String.format(expectedMessage, comment.getProject().getCreator().fullNameFromEmail(), comment.getProject().getTitle(), comment.getUser().fullNameFromEmail(), comment.getComment().substring(0, UserNotificationService.COMMENT_EXCERPT_LENGTH) + " ...", projectLink))); assertThat(actualMessage.getFrom(), is(UserNotificationService.FROM_ADDRESS)); assertThat(actualMessage.getSubject(), is(UserNotificationService.SUBJECT_PROJECT_COMMENTED)); } private UserEntity aProjectCreator() { return aUser("test_id_Creator", "some.creator@email.com"); } private UserEntity aUser(String userId) { final UserEntity res = new UserEntity("some.one@email.com"); res.setId(userId); return res; } private UserEntity aUser(String userId, String email) { final UserEntity res = new UserEntity(email); res.setId(userId); return res; } private void assertCreatorModifierAndAdminNotifiedOfProjectEdit(UserEntity creator, UserEntity modifier, UserEntity admin, List<SimpleMailMessage> capturedMessages) { final String projectLink = "https://crowd.asideas.de#/project/proj3ctId"; final String expMessage = "Hallo %s,\n\n" + "das folgende Projekt wurde von %s editiert.\n" + "Weitere Informationen hinsichtlich des Prozesses kannst Du der FAQ entnehmen.\n\n" + "Zum Projekt:\n\n%s\n\n" + "Mit freundlichen Grüßen\nDein CrowdSource Team"; final SimpleMailMessage creatorMail = capturedMessages.stream().filter(m -> m.getTo()[0].equals(creator.getEmail())).findFirst().get(); final SimpleMailMessage modifierMail = capturedMessages.stream().filter(m -> m.getTo()[0].equals(modifier.getEmail())).findFirst().get(); final SimpleMailMessage adminMail = capturedMessages.stream().filter(m -> m.getTo()[0].equals(admin.getEmail())).findFirst().get(); assertThat(creatorMail.getText(), is(String.format(expMessage, creator.fullNameFromEmail(), modifier.fullNameFromEmail(), projectLink))); assertThat(modifierMail.getText(), is(String.format(expMessage, modifier.fullNameFromEmail(), modifier.fullNameFromEmail(), projectLink))); assertThat(adminMail.getText(), is(String.format(expMessage, admin.fullNameFromEmail(), modifier.fullNameFromEmail(), projectLink))); } private SimpleMailMessage getMessageFromMailSender() { final List<SimpleMailMessage> messagesFromMailSender = getMessagesFromMailSender(); if (messagesFromMailSender.isEmpty()) { return null; } return messagesFromMailSender.get(messagesFromMailSender.size() - 1); } private List<SimpleMailMessage> getMessagesFromMailSender() { ArgumentCaptor<SimpleMailMessage> messageCaptor = ArgumentCaptor.forClass(SimpleMailMessage.class); verify(javaMailSender, atLeastOnce()).send(messageCaptor.capture()); return messageCaptor.getAllValues(); } private ProjectEntity project(String id, ProjectStatus status, UserEntity user, String title) { final ProjectEntity project = new ProjectEntity(); project.setId(id); project.setCreator(user); project.setTitle(title); project.setStatus(status); return project; } private String replaceLineBreaksIfWindows(String message) { if (System.getProperty("os.name").toLowerCase().contains("win")) { return message.replace("\r\n", "\n"); } return message; } private String aTestComment(int length){ StringBuilder res = new StringBuilder(); IntStream.range(0, length).forEach(i-> res.append(i % 10)); return res.toString(); } @Configuration static class Config { @Bean public UserNotificationService userNotificationService() { return new UserNotificationService(); } @Bean public JavaMailSender javaMailSender() { return mock(JavaMailSender.class); } @Bean public AsyncTaskExecutor taskExecutorSmtp() { return mock(AsyncTaskExecutor.class); } @Bean public UserRepository userRepository() { return mock(UserRepository.class); } } }