diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java index 5e3966c..330c31a 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java @@ -21,8 +21,8 @@ import org.springframework.web.bind.annotation.*; public class ButtonDimmerController extends InputDeviceConnectionController { - private DeviceService deviceService; - private ButtonDimmerRepository buttonDimmerRepository; + private final DeviceService deviceService; + private final ButtonDimmerRepository buttonDimmerRepository; @Autowired protected ButtonDimmerController( diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java index f458f62..a0fe8d1 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java @@ -126,11 +126,8 @@ public class UserAccountController { throw new UserNotFoundException(); } - ConfirmationToken token; - do { - token = new ConfirmationToken(toReset); - token.setResetPassword(true); - } while (confirmationTokenRepository.findByConfirmToken(token.getConfirmToken()) != null); + ConfirmationToken token = new ConfirmationToken(toReset); + token.setResetPassword(true); // Delete existing email password reset tokens confirmationTokenRepository.deleteByUserAndResetPassword(toReset, true); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/InitPasswordResetRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/InitPasswordResetRequest.java index dca0d79..4148e18 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/InitPasswordResetRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/InitPasswordResetRequest.java @@ -3,10 +3,14 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; import javax.validation.constraints.Email; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Pattern; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; /** DTO for password reset request */ @Data +@AllArgsConstructor +@NoArgsConstructor public class InitPasswordResetRequest { /** * The user's email (validated according to criteria used in >input type="email"<> diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/PasswordResetRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/PasswordResetRequest.java index 3d9ad7e..e533c49 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/PasswordResetRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/PasswordResetRequest.java @@ -3,10 +3,14 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; /** DTO for password reset request */ @Data +@NoArgsConstructor +@AllArgsConstructor public class PasswordResetRequest { @NotNull private String confirmationToken; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatService.java index 5eeec6c..9065826 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatService.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatService.java @@ -14,13 +14,25 @@ import org.springframework.stereotype.Component; @Component public class ThermostatService { - @Autowired private SensorSocketEndpoint endpoint; + private final SensorSocketEndpoint endpoint; - @Autowired private DeviceService deviceService; + private final DeviceService deviceService; - @Autowired private ThermostatPopulationService thermostatPopulationService; + private final ThermostatPopulationService thermostatPopulationService; - @Autowired private ThermostatRepository thermostatRepository; + private final ThermostatRepository thermostatRepository; + + @Autowired + public ThermostatService( + SensorSocketEndpoint endpoint, + DeviceService deviceService, + ThermostatPopulationService thermostatPopulationService, + ThermostatRepository thermostatRepository) { + this.endpoint = endpoint; + this.deviceService = deviceService; + this.thermostatPopulationService = thermostatPopulationService; + this.thermostatRepository = thermostatRepository; + } private void randomJitter(Thermostat thermostat) { updateValueForThermostat( diff --git a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountControllerTests.java b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountControllerTests.java index eeb11c7..3269550 100644 --- a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountControllerTests.java +++ b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountControllerTests.java @@ -1,17 +1,33 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; -import static org.mockito.Mockito.when; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import ch.usi.inf.sa4.sanmarinoes.smarthut.config.EmailConfigurationService; +import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.InitPasswordResetRequest; +import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.PasswordResetRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException; +import ch.usi.inf.sa4.sanmarinoes.smarthut.error.EmailTokenNotFoundException; +import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UserNotFoundException; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationToken; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository; +import ch.usi.inf.sa4.sanmarinoes.smarthut.service.EmailSenderService; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.test.context.support.WithMockUser; @ExtendWith(MockitoExtension.class) @@ -22,6 +38,14 @@ public class UserAccountControllerTests { @Mock private UserRepository userRepository; + @Mock private EmailSenderService emailSenderService; + + @Mock private EmailConfigurationService emailConfigurationService; + + @Mock private ConfirmationTokenRepository confirmationTokenRepository; + + @Mock private BCryptPasswordEncoder passwordEncoder; + @Test public void testRegisterUser() { final UserRegistrationRequest registrationRequest = new UserRegistrationRequest(); @@ -42,4 +66,98 @@ public class UserAccountControllerTests { Assertions.assertThatThrownBy(() -> userAccountController.registerUser(registrationRequest)) .isInstanceOf(DuplicateRegistrationException.class); } + + @Test + public void testResetPassword() throws EmailTokenNotFoundException { + final User u = new User(); + final ConfirmationToken c = new ConfirmationToken(u); + c.setResetPassword(false); + + when(userRepository.save(u)).thenReturn(u); + + when(confirmationTokenRepository.findByConfirmToken("token")).thenReturn(c); + when(confirmationTokenRepository.findByConfirmToken("token2")).thenReturn(null); + doNothing().when(confirmationTokenRepository).delete(c); + when(passwordEncoder.encode("password")).thenReturn("encoded"); + + assertThatThrownBy( + () -> + userAccountController.resetPassword( + new PasswordResetRequest("token2", "password"))) + .isInstanceOf(EmailTokenNotFoundException.class); + + assertThatThrownBy( + () -> + userAccountController.resetPassword( + new PasswordResetRequest("token", "password"))) + .isInstanceOf(EmailTokenNotFoundException.class); + + c.setResetPassword(true); + + userAccountController.resetPassword(new PasswordResetRequest("token", "password")); + + assertThat(u.getPassword()).isEqualTo("encoded"); + } + + @Test + public void testInitResetPassword() throws UserNotFoundException { + when(emailConfigurationService.getResetPasswordSubject()).thenReturn("password reset"); + when(emailConfigurationService.getResetPassword()).thenReturn("reset-password"); + when(emailConfigurationService.getResetPasswordPath()).thenReturn("path"); + + final User u = new User(); + u.setEmail("info@theshell.com"); + + boolean[] done = new boolean[1]; + + doAnswer( + i -> { + final SimpleMailMessage m = i.getArgument(0); + assertThat(m.getSubject()).isEqualTo("password reset"); + assertThat(m.getText()).startsWith("reset-password path"); + done[0] = true; + return null; + }) + .when(emailSenderService) + .sendEmail(any()); + + doNothing().when(confirmationTokenRepository).deleteByUserAndResetPassword(u, true); + when(confirmationTokenRepository.save(any())).thenAnswer(i -> i.getArgument(0)); + + when(userRepository.findByEmailIgnoreCase("info@theshell.ch")).thenReturn(u); + when(userRepository.findByEmailIgnoreCase("info@vimtok.com")).thenReturn(null); + + assertThatThrownBy( + () -> + userAccountController.initResetPassword( + new InitPasswordResetRequest("info@vimtok.com"))) + .isInstanceOf(UserNotFoundException.class); + userAccountController.initResetPassword(new InitPasswordResetRequest("info@theshell.ch")); + + assertThat(done[0]).isTrue(); + } + + @Test + public void testConfirmUserAccount() throws EmailTokenNotFoundException, IOException { + final User u = new User(); + final ConfirmationToken c = new ConfirmationToken(u); + c.setResetPassword(true); + + when(confirmationTokenRepository.findByConfirmToken("token")).thenReturn(null); + when(confirmationTokenRepository.findByConfirmToken(c.getConfirmToken())).thenReturn(c); + when(userRepository.save(u)).thenReturn(u); + final HttpServletResponse r = new MockHttpServletResponse(); + + assertThatThrownBy(() -> userAccountController.confirmUserAccount("token", r)) + .isInstanceOf(EmailTokenNotFoundException.class); + assertThatThrownBy(() -> userAccountController.confirmUserAccount(c.getConfirmToken(), r)) + .isInstanceOf(EmailTokenNotFoundException.class); + + c.setResetPassword(false); + + when(emailConfigurationService.getRegistrationRedirect()).thenReturn("malusa.html"); + + userAccountController.confirmUserAccount(c.getConfirmToken(), r); + assertThat(u.isEnabled()).isTrue(); + } } diff --git a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatServiceTests.java b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatServiceTests.java new file mode 100644 index 0000000..05a0447 --- /dev/null +++ b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatServiceTests.java @@ -0,0 +1,66 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Thermostat; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ThermostatRepository; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User; +import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint; +import java.math.BigDecimal; +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class ThermostatServiceTests { + + @InjectMocks private ThermostatService thermostatService; + + @Mock private ThermostatRepository thermostatRepository; + + @Mock private DeviceService deviceService; + + @Mock private ThermostatPopulationService thermostatPopulationService; + + @Mock private SensorSocketEndpoint endpoint; + + @Test + public void testFakeUpdateAll() { + when(deviceService.saveAsOwner(any(), any())).thenAnswer(i -> i.getArgument(0)); + when(thermostatRepository.findUser(1L)).thenReturn(new User()); + + Thermostat t = new Thermostat(); + t.setOn(true); + t.setId(1L); + t.setTargetTemperature(BigDecimal.valueOf(17.0)); + + when(thermostatRepository.findAll()).thenReturn(List.of(t)); + doAnswer( + i -> { + ((Thermostat) i.getArgument(0)) + .setMeasuredTemperature(BigDecimal.valueOf(18.0)); + return null; + }) + .when(thermostatPopulationService) + .populateMeasuredTemperature(t); + + doNothing().when(endpoint).queueDeviceUpdate(any(), any(), eq(false), eq(null), eq(false)); + + Assertions.assertDoesNotThrow(() -> thermostatService.fakeUpdateAll()); + assertThat(t.getMode()).isNotEqualTo(Thermostat.Mode.OFF); + } + + @Test + public void testFindAll() { + final Thermostat t = new Thermostat(); + doNothing().when(thermostatPopulationService).populateMeasuredTemperature(t); + when(thermostatRepository.findAllByUsername("user")).thenReturn(List.of(t)); + assertThat(thermostatService.findAll("user")).containsExactly(t); + } +}