package uk.ac.ox.zoo.seeg.abraid.mp.publicsite.web.user.account;
import org.joda.time.DateTime;
import org.joda.time.DateTimeUtils;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.springframework.security.crypto.password.PasswordEncoder;
import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.Expert;
import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.PasswordResetRequest;
import uk.ac.ox.zoo.seeg.abraid.mp.common.service.core.DiseaseService;
import uk.ac.ox.zoo.seeg.abraid.mp.common.service.core.EmailService;
import uk.ac.ox.zoo.seeg.abraid.mp.common.service.core.ExpertService;
import uk.ac.ox.zoo.seeg.abraid.mp.publicsite.domain.JsonExpertDetails;
import uk.ac.ox.zoo.seeg.abraid.mp.publicsite.validator.ValidationException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import static com.googlecode.catchexception.CatchException.catchException;
import static com.googlecode.catchexception.CatchException.caughtException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.*;
import static uk.ac.ox.zoo.seeg.abraid.mp.testutils.GeneralTestUtils.captorForMapClass;
/**
* Tests for AccountControllerHelper.
* Copyright (c) 2014 University of Oxford
*/
public class AccountControllerHelperTest {
@Test
public void processExpertAsTransactionUpdatesAndSavesCorrectSingleExpertCorrectly() throws Exception {
// Arrange
ExpertService expertService = mock(ExpertService.class);
DiseaseService diseaseService = mock(DiseaseService.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, diseaseService, mock(EmailService.class), null);
JsonExpertDetails expertDto = mockExpert();
Expert expert = mockExpertDomain();
when(expertService.getExpertById(321)).thenReturn(expert); // Gets the correct expert
// Act
target.processExpertProfileUpdateAsTransaction(321, expertDto);
// Assert
verify(expert).setName(expertDto.getName());
verify(expert).setJobTitle(expertDto.getJobTitle());
verify(expert).setInstitution(expertDto.getInstitution());
verify(expert).setVisibilityRequested(expertDto.getVisibilityRequested());
verify(expertService).saveExpert(expert);
}
@Test
public void processExpertAsTransactionResetsVisibilityAndUpdatesTheTimestampOnChangedExperts() throws Exception {
// Arrange
DateTimeUtils.setCurrentMillisFixed(12345);
ExpertService expertService = mock(ExpertService.class);
DiseaseService diseaseService = mock(DiseaseService.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, diseaseService, mock(EmailService.class), null);
JsonExpertDetails expertDto = mockExpert();
Expert expert = mockExpertDomain();
when(expertService.getExpertById(321)).thenReturn(expert); // Gets the correct expert
// Act
target.processExpertProfileUpdateAsTransaction(321, expertDto);
// Assert
verify(expert).setVisibilityApproved(false);
verify(expert).setUpdatedDate(DateTime.now());
verify(expertService).saveExpert(expert);
}
@Test
public void processExpertAsTransactionDoesNotResetVisibilityOrUpdatesTheTimestampOnUnchangedExperts()
throws Exception {
// Arrange
ExpertService expertService = mock(ExpertService.class);
DiseaseService diseaseService = mock(DiseaseService.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, diseaseService, mock(EmailService.class), null);
JsonExpertDetails expertDto = mockExpert();
Expert expert = mock(Expert.class);
// These field must appear unchanged
when(expert.getName()).thenReturn("Hippocrates of Kos");
when(expert.getJobTitle()).thenReturn("Physician");
when(expert.getInstitution()).thenReturn("Classical Greece");
when(expert.getVisibilityRequested()).thenReturn(false);
// This field should be able to change without resetting visibility approval
when(expertDto.getDiseaseInterests()).thenReturn(Arrays.asList(1, 2, 3, 4, 5));
when(expertService.getExpertById(321)).thenReturn(expert); // Gets the correct expert
// Act
target.processExpertProfileUpdateAsTransaction(321, expertDto);
// Assert
verify(expert, times(0)).setVisibilityApproved(anyBoolean());
verify(expert, times(0)).setUpdatedDate(any(DateTime.class));
verify(expertService).saveExpert(expert);
}
@Test
public void processExpertAsTransactionThrowsValidationExceptionIfNoMatchingExpert() throws Exception {
// Arrange
ExpertService expertService = mock(ExpertService.class);
DiseaseService diseaseService = mock(DiseaseService.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, diseaseService, mock(EmailService.class), null);
JsonExpertDetails expert = mock(JsonExpertDetails.class);
when(expertService.getExpertById(anyInt())).thenReturn(null);
// Act
catchException(target).processExpertProfileUpdateAsTransaction(-1, expert);
// Assert
assertThat(caughtException()).isInstanceOf(ValidationException.class);
}
@Test
public void processExpertAsTransactionSendsEmailForUpdatedUserRequiringVisibilityApproval() throws Exception {
// Arrange
ExpertService expertService = mock(ExpertService.class);
DiseaseService diseaseService = mock(DiseaseService.class);
EmailService emailService = mock(EmailService.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, diseaseService, emailService, null);
JsonExpertDetails expertDto = mockExpert();
when(expertDto.getName()).thenReturn("asdfas");
Expert expert = mockExpertDomain();
when(expert.getVisibilityRequested()).thenReturn(true);
when(expertService.getExpertById(321)).thenReturn(expert); // Gets the correct expert
// Act
target.processExpertProfileUpdateAsTransaction(321, expertDto);
// Assert
verify(expertService).saveExpert(expert);
Map<String, Object> data = new HashMap<>();
data.put("expert", expert);
verify(emailService).sendEmailInBackground(
"Updated user requiring visibility sign off",
"account/updatedUserEmail.ftl",
data);
}
@Test
public void processExpertAsTransactionSkipsEmailForUpdatedUserNotRequiringVisibilityApproval() throws Exception {
// Arrange
ExpertService expertService = mock(ExpertService.class);
DiseaseService diseaseService = mock(DiseaseService.class);
EmailService emailService = mock(EmailService.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, diseaseService, emailService, null);
JsonExpertDetails expertDto = mockExpert();
when(expertDto.getName()).thenReturn("asdfas");
Expert expert = mockExpertDomain();
when(expert.getVisibilityRequested()).thenReturn(false);
when(expertService.getExpertById(321)).thenReturn(expert); // Gets the correct expert
// Act
target.processExpertProfileUpdateAsTransaction(321, expertDto);
// Assert
verify(expertService).saveExpert(expert);
verify(emailService, never()).sendEmailInBackground(anyString(), anyString(), anyMapOf(String.class, Object.class));
}
@Test
public void processExpertEmailChangeAsTransactionUpdatesAndSavesPasswordCorrectly() throws Exception {
// Arrange
ExpertService expertService = mock(ExpertService.class);
DiseaseService diseaseService = mock(DiseaseService.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, diseaseService, mock(EmailService.class), mock(PasswordEncoder.class));
Expert expert = mockExpertDomain();
when(expertService.getExpertById(321)).thenReturn(expert); // Gets the correct expert
// Act
target.processExpertEmailChangeAsTransaction(321, "newEmail");
// Assert
verify(expert).setEmail("newEmail");
verify(expertService).saveExpert(expert);
}
@Test
public void processExpertEmailChangeAsTransactionThrowsValidationExceptionIfNoMatchingExpert() throws Exception {
// Arrange
ExpertService expertService = mock(ExpertService.class);
DiseaseService diseaseService = mock(DiseaseService.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, diseaseService, mock(EmailService.class), mock(PasswordEncoder.class));
when(expertService.getExpertById(anyInt())).thenReturn(null);
// Act
catchException(target).processExpertEmailChangeAsTransaction(-1, "newEmail");
// Assert
assertThat(caughtException()).isInstanceOf(ValidationException.class);
}
@Test
public void processExpertPasswordChangeAsTransactionUpdatesAndSavesPasswordCorrectly() throws Exception {
// Arrange
ExpertService expertService = mock(ExpertService.class);
DiseaseService diseaseService = mock(DiseaseService.class);
PasswordEncoder passwordEncoder = mock(PasswordEncoder.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, diseaseService, mock(EmailService.class), passwordEncoder);
Expert expert = mockExpertDomain();
when(expertService.getExpertById(321)).thenReturn(expert); // Gets the correct expert
when(passwordEncoder.encode("password")).thenReturn("passwordHash");
// Act
target.processExpertPasswordChangeAsTransaction(321, "password");
// Assert
verify(expert).setPassword("passwordHash");
verify(expertService).saveExpert(expert);
}
@Test
public void processExpertPasswordChangeAsTransactionThrowsValidationExceptionIfNoMatchingExpert() throws Exception {
// Arrange
ExpertService expertService = mock(ExpertService.class);
DiseaseService diseaseService = mock(DiseaseService.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, diseaseService, mock(EmailService.class), mock(PasswordEncoder.class));
when(expertService.getExpertById(anyInt())).thenReturn(null);
// Act
catchException(target).processExpertPasswordChangeAsTransaction(-1, "password");
// Assert
assertThat(caughtException()).isInstanceOf(ValidationException.class);
}
@Test
public void processExpertPasswordResetRequestAsTransactionCreatesNewPasswordResetRequest() throws Exception {
// Arrange
ExpertService expertService = mock(ExpertService.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, null, mock(EmailService.class), null);
when(expertService.getExpertByEmail("email")).thenReturn(mock(Expert.class));
// Act
target.processExpertPasswordResetRequestAsTransaction("email", "url");
// Assert
verify(expertService).createAndSavePasswordResetRequest(eq("email"), anyString());
}
@Test
public void processExpertPasswordResetRequestAsTransactionSendsResetEmail() throws Exception {
// Arrange
ExpertService expertService = mock(ExpertService.class);
EmailService emailService = mock(EmailService.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, null, emailService, null);
when(expertService.getExpertByEmail("email")).thenReturn(mock(Expert.class));
when(expertService.createAndSavePasswordResetRequest(eq("email"), anyString())).thenReturn(4321);
// Act
target.processExpertPasswordResetRequestAsTransaction("email", "url");
// Assert
ArgumentCaptor<Map<String, Object>> captor = captorForMapClass();
verify(emailService).sendEmailInBackground(eq("email"), eq("ABRAID-MP Password Reset"), eq("account/passwordResetEmail.ftl"), captor.capture());
Map<String, Object> data = captor.getValue();
assertThat(data.get("url")).isEqualTo("url");
assertThat(data.get("id")).isEqualTo(4321);
assertThat(data.get("key")).isNotNull();
}
@Test
public void processExpertPasswordResetRequestAsTransactionThrowsValidationExceptionIfNoMatchingExpert() throws Exception {
// Arrange
ExpertService expertService = mock(ExpertService.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, null, mock(EmailService.class), null);
when(expertService.getExpertByEmail("email")).thenReturn(null);
// Act
catchException(target).processExpertPasswordResetRequestAsTransaction("email", "url");
// Assert
assertThat(caughtException()).isInstanceOf(ValidationException.class);
}
@Test
public void processExpertPasswordResetAsTransactionUpdatesExpertsPassword() throws Exception {
// Arrange
ExpertService expertService = mock(ExpertService.class);
PasswordEncoder passwordEncoder = mock(PasswordEncoder.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, null, mock(EmailService.class), passwordEncoder);
PasswordResetRequest resetRequest = mock(PasswordResetRequest.class);
Expert expert = mock(Expert.class);
when(expertService.getPasswordResetRequest(7)).thenReturn(resetRequest);
when(resetRequest.getExpert()).thenReturn(expert);
when(passwordEncoder.encode("password")).thenReturn("hashedPassword");
// Act
target.processExpertPasswordResetAsTransaction("password", 7);
// Assert
InOrder inOrder = inOrder(expert, expertService);
inOrder.verify(expert).setPassword("hashedPassword");
inOrder.verify(expertService).saveExpert(expert);
}
@Test
public void processExpertPasswordResetAsTransactionDeletesCompletedResetRequest() throws Exception {
// Arrange
ExpertService expertService = mock(ExpertService.class);
PasswordEncoder passwordEncoder = mock(PasswordEncoder.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, null, mock(EmailService.class), passwordEncoder);
PasswordResetRequest resetRequest = mock(PasswordResetRequest.class);
Expert expert = mock(Expert.class);
when(expertService.getPasswordResetRequest(7)).thenReturn(resetRequest);
when(resetRequest.getExpert()).thenReturn(expert);
when(passwordEncoder.encode("password")).thenReturn("hashedPassword");
// Act
target.processExpertPasswordResetAsTransaction("password", 7);
// Assert
verify(expertService).deletePasswordResetRequest(resetRequest);
}
@Test
public void processExpertPasswordResetAsTransactionThrowsValidationExceptionIfNoMatchingResetRequest() throws Exception {
// Arrange
ExpertService expertService = mock(ExpertService.class);
AccountControllerHelper target = new AccountControllerHelper(expertService, null, mock(EmailService.class), null);
when(expertService.getPasswordResetRequest(7)).thenReturn(null);
// Act
catchException(target).processExpertPasswordResetAsTransaction("password", 7);
// Assert
assertThat(caughtException()).isInstanceOf(ValidationException.class);
}
private static JsonExpertDetails mockExpert() {
JsonExpertDetails result = mock(JsonExpertDetails.class);
when(result.getName()).thenReturn("Hippocrates of Kos");
when(result.getJobTitle()).thenReturn("Physician");
when(result.getInstitution()).thenReturn("Classical Greece");
when(result.getVisibilityRequested()).thenReturn(false);
when(result.getDiseaseInterests()).thenReturn(Arrays.asList(1, 2, 3));
return result;
}
private static Expert mockExpertDomain() {
Expert result = mock(Expert.class);
when(result.getName()).thenReturn("");
when(result.getJobTitle()).thenReturn("");
when(result.getInstitution()).thenReturn("");
return result;
}
}