diff --git a/application/model/src/main/java/org/fuseri/model/dto/course/LanguageTypeDto.java b/application/model/src/main/java/org/fuseri/model/dto/course/LanguageTypeDto.java index 84214fd415d40511029aebd52f60bbc526ff8e7f..2b7f9fde7906edc96a7ccee7d2f926e5c83b3d0b 100644 --- a/application/model/src/main/java/org/fuseri/model/dto/course/LanguageTypeDto.java +++ b/application/model/src/main/java/org/fuseri/model/dto/course/LanguageTypeDto.java @@ -1,5 +1,8 @@ package org.fuseri.model.dto.course; +import lombok.AllArgsConstructor; + +@AllArgsConstructor public enum LanguageTypeDto { ENGLISH, GERMAN, diff --git a/application/model/src/main/java/org/fuseri/model/dto/user/AddressDto.java b/application/model/src/main/java/org/fuseri/model/dto/user/AddressDto.java index ff0af99960e0daa36f948a11bee5f9bdaf1721f0..bcbbbe4a3a2280476f47e72ab406052843a67b50 100644 --- a/application/model/src/main/java/org/fuseri/model/dto/user/AddressDto.java +++ b/application/model/src/main/java/org/fuseri/model/dto/user/AddressDto.java @@ -1,11 +1,15 @@ package org.fuseri.model.dto.user; import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; @Getter @Setter +@AllArgsConstructor +@NoArgsConstructor public class AddressDto { @NotBlank private String country; diff --git a/application/model/src/main/java/org/fuseri/model/dto/user/UserAddLanguageDto.java b/application/model/src/main/java/org/fuseri/model/dto/user/UserAddLanguageDto.java index ebe92f4f3068a6b04c2beec0c17b268b8a2b4c17..271ce6435594e1a25946db945556ad8c048943cc 100644 --- a/application/model/src/main/java/org/fuseri/model/dto/user/UserAddLanguageDto.java +++ b/application/model/src/main/java/org/fuseri/model/dto/user/UserAddLanguageDto.java @@ -1,14 +1,23 @@ package org.fuseri.model.dto.user; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; -import org.fuseri.model.dto.common.DomainObjectDto; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; import org.fuseri.model.dto.course.LanguageTypeDto; import org.fuseri.model.dto.course.ProficiencyLevelDto; -public class UserAddLanguageDto extends DomainObjectDto { +@Getter +@Setter +@AllArgsConstructor +public class UserAddLanguageDto { @NotNull + @Valid LanguageTypeDto language; + @NotNull + @Valid ProficiencyLevelDto proficiency; } diff --git a/application/model/src/main/java/org/fuseri/model/dto/user/UserCreateDto.java b/application/model/src/main/java/org/fuseri/model/dto/user/UserCreateDto.java index 403960952d468ad7afe797b014f0a627f65ceb49..9be887e9319946ecf9e73f1188e7c94201a1d874 100644 --- a/application/model/src/main/java/org/fuseri/model/dto/user/UserCreateDto.java +++ b/application/model/src/main/java/org/fuseri/model/dto/user/UserCreateDto.java @@ -1,14 +1,15 @@ package org.fuseri.model.dto.user; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; -import lombok.NonNull; -import org.fuseri.model.dto.common.DomainObjectDto; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; @Getter @Setter +@AllArgsConstructor public class UserCreateDto { @NotBlank @@ -22,5 +23,6 @@ public class UserCreateDto { @NotBlank private String lastName; @NotNull + @Valid private AddressDto address; } diff --git a/application/model/src/main/java/org/fuseri/model/dto/user/UserLoginDto.java b/application/model/src/main/java/org/fuseri/model/dto/user/UserLoginDto.java index 0aba85a7f95a3b40719101c8e332a10d472ef0b5..23972ce45017b07c3507535167952d16dc8ecd15 100644 --- a/application/model/src/main/java/org/fuseri/model/dto/user/UserLoginDto.java +++ b/application/model/src/main/java/org/fuseri/model/dto/user/UserLoginDto.java @@ -1,12 +1,13 @@ package org.fuseri.model.dto.user; import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; -import org.fuseri.model.dto.common.DomainObjectDto; @Getter @Setter +@AllArgsConstructor public class UserLoginDto { @NotBlank private String username; diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserController.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserController.java index 31cfd86e0d4f9b7ab652143468c7a61666609e00..d0c6429b245ad650d6e5fe747d9db2fb48e88681 100644 --- a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserController.java +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserController.java @@ -62,7 +62,7 @@ public class UserController { } @PostMapping("/login") - public String login(@RequestBody UserLoginDto dto) { + public String login(@Valid @RequestBody UserLoginDto dto) { return String.format("User %s has spawned", dto.getUsername()); } diff --git a/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/user/UserControllerTest.java b/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/user/UserControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..85aa5f33c134528abba0f729717a020278e440bb --- /dev/null +++ b/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/user/UserControllerTest.java @@ -0,0 +1,227 @@ +package org.fuseri.modulelanguageschool.user; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.fuseri.model.dto.course.LanguageTypeDto; +import org.fuseri.model.dto.course.ProficiencyLevelDto; +import org.fuseri.model.dto.user.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + + +@SpringBootTest +@AutoConfigureMockMvc +class UserControllerTest { + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private MockMvc mockMvc; + + private static final AddressDto ADDRESS_TO_CREATE = new AddressDto( + "Czechia", "Brno", "Masarykova", "45", "90033"); + + private static final List<AddressDto> INVALID_ADDRESSES = List.of( + new AddressDto("", "Brno", "Masarykova", "45", "90033"), + new AddressDto("Czechia", "", "Masarykova", "45", "90033"), + new AddressDto("Czechia", "Brno", "", "45", "90033"), + new AddressDto("Czechia", "Brno", "Masarykova", "", "90033"), + new AddressDto("Czechia", "Brno", "Masarykova", "45", ""), + new AddressDto(null, "Brno", "Masarykova", "45", "90033"), + new AddressDto("Czechia", null, "Masarykova", "45", "90033"), + new AddressDto("Czechia", "Brno", null, "45", "90033"), + new AddressDto("Czechia", "Brno", "Masarykova", null, "90033") + ); + + private static final UserCreateDto USER_TO_CREATE = new UserCreateDto( + "xnovak", "1c1bbf66-6585-4978-886b-b126335ff3af", + "xnovak@emample.com", "Peter", "Novak", ADDRESS_TO_CREATE); + + private static final UserLoginDto USER_TO_LOGIN = new UserLoginDto( + "xnovak", "1c1bbf66-6585-4978-886b-b126335ff3af"); + + private static Stream<UserCreateDto> invalidUsers() { + var invalidUsers = Stream.of( + new UserCreateDto("", "1c1bbf66-6585-4978-886b-b126335ff3af", + "xnovak@emample.com", "Peter", "Novak", ADDRESS_TO_CREATE), + new UserCreateDto("xnovak", "", + "xnovak@emample.com", "Peter", "Novak", ADDRESS_TO_CREATE), + new UserCreateDto("xnovak", "1c1bbf66-6585-4978-886b-b126335ff3af", + "", "Peter", "Novak", ADDRESS_TO_CREATE), + new UserCreateDto("xnovak", "1c1bbf66-6585-4978-886b-b126335ff3af", + "xnovak@emample.com", "", "Novak", ADDRESS_TO_CREATE), + new UserCreateDto("xnovak", "1c1bbf66-6585-4978-886b-b126335ff3af", + "xnovak@emample.com", "Peter", "", ADDRESS_TO_CREATE), + new UserCreateDto(null, "1c1bbf66-6585-4978-886b-b126335ff3af", + "xnovak@emample.com", "Peter", "Novak", ADDRESS_TO_CREATE), + new UserCreateDto("xnovak", null, + "xnovak@emample.com", "Peter", "Novak", ADDRESS_TO_CREATE), + new UserCreateDto("xnovak", "1c1bbf66-6585-4978-886b-b126335ff3af", + null, "Peter", "Novak", ADDRESS_TO_CREATE), + new UserCreateDto("xnovak", "1c1bbf66-6585-4978-886b-b126335ff3af", + "xnovak@emample.com", null, "Novak", ADDRESS_TO_CREATE), + new UserCreateDto("xnovak", "1c1bbf66-6585-4978-886b-b126335ff3af", + "xnovak@emample.com", "Peter", null, ADDRESS_TO_CREATE), + new UserCreateDto("xnovak", "1c1bbf66-6585-4978-886b-b126335ff3af", + "xnovak@emample.com", "Peter", "Novak", null) + ); + + var invalidAddressUsers = new ArrayList<UserCreateDto>(); + for (var invalidAddress : INVALID_ADDRESSES) { + invalidAddressUsers.add(new UserCreateDto("xnovak", "1c1bbf66-6585-4978-886b-b126335ff3af", + "xnovak@emample.com", "Peter", "Novak", invalidAddress)); + } + + return Stream.concat(invalidUsers, invalidAddressUsers.stream()); + } + + private static Stream<UserLoginDto> invalidLoginDtoStream() { + return Stream.of( + new UserLoginDto("", "1c1bbf66-6585-4978-886b-b126335ff3af"), + new UserLoginDto("xnovak", ""), + new UserLoginDto(null, "1c1bbf66-6585-4978-886b-b126335ff3af"), + new UserLoginDto("xnovak", null)); + } + + @Test + void create() throws Exception { + mockMvc.perform(post("/users") + .content(asJsonString(USER_TO_CREATE)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + } + + @ParameterizedTest + @MethodSource("invalidUsers") + void createInvalidUser(UserCreateDto user) throws Exception { + mockMvc.perform(post("/users") + .content(asJsonString(user)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is4xxClientError()); + } + + @Test + void findUser() throws Exception { + String response = mockMvc.perform(post("/users") + .content(asJsonString(USER_TO_CREATE)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + + String id = objectMapper.readValue(response, UserDto.class).getId(); + + mockMvc.perform(get("/users/{id}", id)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(id)); + } + + @Test + void findAll() throws Exception { + mockMvc.perform(get("/users/all") + .param("page", "0")) + .andExpect(status().isOk()); + } + + @Test + void deleteUser() throws Exception { + String response = mockMvc.perform(post("/users") + .content(asJsonString(USER_TO_CREATE)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + + String id = objectMapper.readValue(response, UserDto.class).getId(); + + mockMvc.perform(delete("/users/{id}", id) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + } + + @Test + void update() throws Exception { + String response = mockMvc.perform(post("/users") + .content(asJsonString(USER_TO_CREATE)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + + String id = objectMapper.readValue(response, UserDto.class).getId(); + + var updatedUsername = "novak"; + var userToUpdate = new UserCreateDto( + USER_TO_CREATE.getUsername(), USER_TO_CREATE.getPassword(), USER_TO_CREATE.getEmail(), + USER_TO_CREATE.getFirstName(), USER_TO_CREATE.getLastName(), USER_TO_CREATE.getAddress()); + userToUpdate.setUsername(updatedUsername); + + mockMvc.perform(put("/users/update/{id}", id) + .content(asJsonString(userToUpdate)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.username").value(updatedUsername)); + } + + @Test + void login() throws Exception { + mockMvc.perform(post("/users/login") + .content(asJsonString(USER_TO_LOGIN)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + } + + @ParameterizedTest + @MethodSource("invalidLoginDtoStream") + void loginInvalidDto(UserLoginDto loginDto) throws Exception { + mockMvc.perform(post("/users/login") + .content(asJsonString(loginDto)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is4xxClientError()); + } + + @Test + void logout() throws Exception { + String response = mockMvc.perform(post("/users") + .content(asJsonString(USER_TO_CREATE)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + + String id = objectMapper.readValue(response, UserDto.class).getId(); + + mockMvc.perform(post("/users/logout/{id}", id)) + .andExpect(status().isOk()); + } + + @Test + void getFinished() throws Exception { + mockMvc.perform(get("/users/finished/{id}", "1c1bbf66-6585-4978-886b-b126335ff3af")) + .andExpect(status().isOk()); + } + + @Test + void getEnrolled() throws Exception { + mockMvc.perform(get("/users/enrolled/{id}", "1c1bbf66-6585-4978-886b-b126335ff3af")) + .andExpect(status().isOk()); + } + + @Test + void addLanguage() throws Exception { + mockMvc.perform(put("/users/addLanguage/{id}", "1c1bbf66-6585-4978-886b-b126335ff3af") + .content(asJsonString(new UserAddLanguageDto(LanguageTypeDto.ENGLISH, ProficiencyLevelDto.B2))) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + } + + private static String asJsonString(final Object obj) throws JsonProcessingException { + return new ObjectMapper().writeValueAsString(obj); + } +} \ No newline at end of file