diff --git a/pom.xml b/pom.xml index 6a83d1a..cef314e 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ org.testcontainers testcontainers-bom - 1.18.0 + 1.19.3 pom import diff --git a/src/main/java/org/bookstore/dto/book/CreateBookRequestDto.java b/src/main/java/org/bookstore/dto/book/CreateBookRequestDto.java index 10cf4c8..621ecd0 100644 --- a/src/main/java/org/bookstore/dto/book/CreateBookRequestDto.java +++ b/src/main/java/org/bookstore/dto/book/CreateBookRequestDto.java @@ -24,5 +24,6 @@ public record CreateBookRequestDto( String description, @Size(max = 255, message = "Maximum allowed size 255 characters") String coverImage, + @NotNull Set categoryIds) { } diff --git a/src/test/java/org/bookstore/controller/BookControllerTest.java b/src/test/java/org/bookstore/controller/BookControllerTest.java index 38dbc8b..070d5ed 100644 --- a/src/test/java/org/bookstore/controller/BookControllerTest.java +++ b/src/test/java/org/bookstore/controller/BookControllerTest.java @@ -60,7 +60,7 @@ static void beforeAll(@Autowired WebApplicationContext applicationContext) { "classpath:/scripts/books/delete-books.sql", "classpath:/scripts/categories/create-categories.sql" }) - @WithMockUser(username = "admin", authorities = {"USER", "ADMIN"}) + @WithMockUser(username = "admin", authorities = {"ADMIN"}) void createBook_validRequestDto_returnsDto() throws Exception { CreateBookRequestDto request = new CreateBookRequestDto( "title", @@ -73,7 +73,7 @@ void createBook_validRequestDto_returnsDto() throws Exception { ); MvcResult result = mockMvc.perform(post("/books") - .content(objectMapper.writeValueAsBytes(request)) + .content(objectMapper.writeValueAsString(request)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andReturn(); @@ -151,7 +151,7 @@ void updateBookById_existingBook_BookDto() throws Exception { ); MvcResult result = mockMvc.perform(put("/books/" + id) - .content(objectMapper.writeValueAsBytes(request)) + .content(objectMapper.writeValueAsString(request)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); @@ -166,11 +166,34 @@ void updateBookById_existingBook_BookDto() throws Exception { .hasFieldOrPropertyWithValue("coverImage", "image_changed"); } + @Test + @DisplayName("Create book with invalid request dto") + @Sql(scripts = { + "classpath:/scripts/books/delete-books.sql", + }) + @WithMockUser(username = "admin", authorities = {"ADMIN"}) + void createBook_invalidRequestDto_Exception() throws Exception { + CreateBookRequestDto request = new CreateBookRequestDto( + "title", + "author", + "12345678900", + BigDecimal.valueOf(-100), + "descr", + "image", + Set.of(1L) + ); + + mockMvc.perform(post("/books") + .content(objectMapper.writeValueAsString(request)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()); + } + @Test @DisplayName("Update non-existing book") @WithMockUser(username = "admin", authorities = {"USER", "ADMIN"}) @Sql(scripts = "classpath:/scripts/books/delete-books.sql") - void updateBookById_nonExistingBook_exception() throws Exception { + void updateBookById_nonExistingBook_Exception() throws Exception { long id = 1; CreateBookRequestDto request = new CreateBookRequestDto( "title", @@ -183,10 +206,9 @@ void updateBookById_nonExistingBook_exception() throws Exception { ); mockMvc.perform(put("/books/" + id) - .content(objectMapper.writeValueAsBytes(request)) + .content(objectMapper.writeValueAsString(request)) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isNotFound()) - .andReturn(); + .andExpect(status().isNotFound()); } @Test @@ -194,8 +216,7 @@ void updateBookById_nonExistingBook_exception() throws Exception { @WithMockUser(username = "admin", authorities = {"USER", "ADMIN"}) void deleteById_existingBook_Ok() throws Exception { mockMvc.perform(delete("/books/1")) - .andExpect(status().isNoContent()) - .andReturn(); + .andExpect(status().isNoContent()); } @Test @@ -203,8 +224,7 @@ void deleteById_existingBook_Ok() throws Exception { @WithMockUser(username = "admin", authorities = {"USER", "ADMIN"}) void findById_nonExistingBook_Exception() throws Exception { mockMvc.perform(get("/books/100")) - .andExpect(status().isNotFound()) - .andReturn(); + .andExpect(status().isNotFound()); } @Test @@ -223,11 +243,9 @@ void updateById_nonExistingBook_Exception() throws Exception { ); mockMvc.perform(put("/books/" + id) - .content(objectMapper.writeValueAsBytes(request)) + .content(objectMapper.writeValueAsString(request)) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isNotFound()) - .andReturn(); - + .andExpect(status().isNotFound()); } @Test @@ -235,7 +253,6 @@ void updateById_nonExistingBook_Exception() throws Exception { @WithMockUser(username = "admin", authorities = {"USER", "ADMIN"}) void deleteById_nonExistingBook_Exception() throws Exception { mockMvc.perform(delete("/books/100")) - .andExpect(status().isNotFound()) - .andReturn(); + .andExpect(status().isNotFound()); } } diff --git a/src/test/java/org/bookstore/controller/CategoryControllerTest.java b/src/test/java/org/bookstore/controller/CategoryControllerTest.java index 9c52657..9174e3e 100644 --- a/src/test/java/org/bookstore/controller/CategoryControllerTest.java +++ b/src/test/java/org/bookstore/controller/CategoryControllerTest.java @@ -59,7 +59,7 @@ void createCategory_validDto_CategoryDto() throws Exception { ); MvcResult result = mockMvc.perform(post("/categories") - .content(objectMapper.writeValueAsBytes(request)) + .content(objectMapper.writeValueAsString(request)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); @@ -122,7 +122,7 @@ void updateCategory_validDto_CategoryDto() throws Exception { "some new scary text" ); MvcResult result = mockMvc.perform(put("/categories/" + id) - .content(objectMapper.writeValueAsBytes(updateRequest)) + .content(objectMapper.writeValueAsString(updateRequest)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); @@ -142,8 +142,7 @@ void updateCategory_validDto_CategoryDto() throws Exception { @WithMockUser(username = "admin", authorities = {"USER", "ADMIN"}) void deleteById_existingBook_Ok() throws Exception { mockMvc.perform(delete("/categories/1")) - .andExpect(status().isOk()) - .andReturn(); + .andExpect(status().isOk()); } @Test @@ -151,8 +150,7 @@ void deleteById_existingBook_Ok() throws Exception { @WithMockUser(username = "admin", authorities = {"USER", "ADMIN"}) void findById_nonExistingCategory_Exception() throws Exception { mockMvc.perform(get("/categories/100")) - .andExpect(status().isNotFound()) - .andReturn(); + .andExpect(status().isNotFound()); } @Test @@ -166,10 +164,9 @@ void updateById_nonExistingCategory_Exception() throws Exception { ); mockMvc.perform(put("/categories/" + id) - .content(objectMapper.writeValueAsBytes(request)) + .content(objectMapper.writeValueAsString(request)) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isNotFound()) - .andReturn(); + .andExpect(status().isNotFound()); } @Test @@ -177,7 +174,6 @@ void updateById_nonExistingCategory_Exception() throws Exception { @WithMockUser(username = "admin", authorities = {"USER", "ADMIN"}) void deleteById_nonExistingCategory_Exception() throws Exception { mockMvc.perform(delete("/categories/100")) - .andExpect(status().isNotFound()) - .andReturn(); + .andExpect(status().isNotFound()); } } diff --git a/src/test/java/org/bookstore/service/BookServiceTest.java b/src/test/java/org/bookstore/service/BookServiceTest.java index 2ec7d73..ab441c7 100644 --- a/src/test/java/org/bookstore/service/BookServiceTest.java +++ b/src/test/java/org/bookstore/service/BookServiceTest.java @@ -1,12 +1,15 @@ package org.bookstore.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.when; import java.math.BigDecimal; +import java.util.Optional; import java.util.Set; import org.bookstore.dto.book.BookDto; import org.bookstore.dto.book.CreateBookRequestDto; +import org.bookstore.exceptions.EntityNotFoundException; import org.bookstore.mapper.BookMapper; import org.bookstore.mapper.impl.BookMapperImpl; import org.bookstore.model.Book; @@ -37,22 +40,13 @@ public class BookServiceTest { private BookServiceImpl bookService; @Test - @DisplayName("Save new book") - void saveBook_validBookDto_BookDto() { + @DisplayName("Save valid book") + void save_validRequestDto_BookDto() { Category category = new Category(); category.setId(1L); category.setName("name"); category.setDescription("desc"); - Book book = new Book(); - book.setAuthor("author"); - book.setTitle("title"); - book.setCategories(Set.of(category)); - book.setIsbn("isbn-000001"); - book.setPrice(BigDecimal.TEN); - book.setDescription("descr"); - book.setCoverImage("image"); - CreateBookRequestDto request = new CreateBookRequestDto( "title", "author", @@ -63,6 +57,15 @@ void saveBook_validBookDto_BookDto() { Set.of(1L) ); + Book book = new Book(); + book.setAuthor(request.author()); + book.setTitle(request.title()); + book.setCategories(Set.of(category)); + book.setIsbn(request.isbn()); + book.setPrice(request.price()); + book.setDescription(request.description()); + book.setCoverImage(request.coverImage()); + when(bookRepository.save(book)).thenReturn(book); when(bookMapper.toEntity(request)).thenReturn(book); when(categoryRepository.getReferenceById(1L)).thenReturn(category); @@ -76,4 +79,80 @@ void saveBook_validBookDto_BookDto() { .hasFieldOrPropertyWithValue("coverImage", request.coverImage()) .hasFieldOrPropertyWithValue("categoriesIds", request.categoryIds()); } + + @Test + @DisplayName("Get book with valid id") + void getBookById_validId_BookDto() { + Book book = new Book(); + book.setId(1L); + book.setAuthor("Author"); + book.setTitle("Title"); + book.setIsbn("553322"); + book.setPrice(BigDecimal.valueOf(125.55)); + book.setDescription("some desc"); + book.setCoverImage("some url"); + + when(bookRepository.findById(1L)).thenReturn(Optional.of(book)); + + BookDto actual = bookService.findById(1L); + + assertThat(actual) + .hasFieldOrPropertyWithValue("id", book.getId()) + .hasFieldOrPropertyWithValue("title", book.getTitle()) + .hasFieldOrPropertyWithValue("author", book.getAuthor()) + .hasFieldOrPropertyWithValue("price", book.getPrice()) + .hasFieldOrPropertyWithValue("description", book.getDescription()) + .hasFieldOrPropertyWithValue("coverImage", book.getCoverImage()); + } + + @Test + @DisplayName("Get book with not existing id") + void getBookById_notValidId_Exception() { + Long id = 100L; + when(bookRepository.findById(id)).thenReturn(Optional.empty()); + + assertThatThrownBy(() -> bookService.findById(id)) + .isInstanceOf(EntityNotFoundException.class); + } + + @Test + @DisplayName("Update existing book with valid request") + void updateBook_validRequestDto_BookDto() { + Long id = 1L; + CreateBookRequestDto requestDto = new CreateBookRequestDto( + "Title", + "Author", + "553322", + BigDecimal.valueOf(125.55), + "some desc", + "some url", + Set.of(id)); + + Category category = new Category(); + category.setId(id); + + Book book = new Book(); + book.setId(id); + book.setAuthor(requestDto.author()); + book.setTitle(requestDto.title()); + book.setIsbn(requestDto.isbn()); + book.setPrice(requestDto.price()); + book.setDescription(requestDto.description()); + book.setCoverImage(requestDto.coverImage()); + book.setCategories(Set.of(category)); + + when(bookRepository.findById(id)).thenReturn(Optional.of(book)); + when(bookRepository.save(book)).thenReturn(book); + + BookDto actual = bookService.updateBookById(id, requestDto); + + assertThat(actual) + .hasFieldOrPropertyWithValue("id", id) + .hasFieldOrPropertyWithValue("title", requestDto.title()) + .hasFieldOrPropertyWithValue("author", requestDto.author()) + .hasFieldOrPropertyWithValue("price", requestDto.price()) + .hasFieldOrPropertyWithValue("description", requestDto.description()) + .hasFieldOrPropertyWithValue("coverImage", requestDto.coverImage()) + .hasFieldOrPropertyWithValue("categoriesIds", requestDto.categoryIds()); + } } diff --git a/src/test/java/org/bookstore/service/CategoryServiceTest.java b/src/test/java/org/bookstore/service/CategoryServiceTest.java index 18ade97..2cbb368 100644 --- a/src/test/java/org/bookstore/service/CategoryServiceTest.java +++ b/src/test/java/org/bookstore/service/CategoryServiceTest.java @@ -1,10 +1,13 @@ package org.bookstore.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.when; +import java.util.Optional; import org.bookstore.dto.category.CategoryDto; import org.bookstore.dto.category.CreateCategoryRequestDto; +import org.bookstore.exceptions.EntityNotFoundException; import org.bookstore.mapper.CategoryMapper; import org.bookstore.mapper.impl.CategoryMapperImpl; import org.bookstore.model.Category; @@ -22,29 +25,93 @@ public class CategoryServiceTest { @Spy private CategoryMapper categoryMapper = new CategoryMapperImpl(); - @Mock private CategoryRepository categoryRepository; @InjectMocks private CategoryServiceImpl categoryService; @Test - @DisplayName("Save new category") - void saveCategory_validCategoryDto_CategoryDto() { - Category category = new Category(); - category.setDescription("descr"); - category.setName("name"); - - CreateCategoryRequestDto request = new CreateCategoryRequestDto( + @DisplayName("Save new category with valid request") + void save_validRequestDto_CategoryDto() { + CreateCategoryRequestDto requestDto = new CreateCategoryRequestDto( "name", - "descr" + "some desc" ); + Category category = new Category(); + category.setName(requestDto.name()); + category.setDescription(requestDto.description()); + when(categoryRepository.save(category)).thenReturn(category); - when(categoryMapper.toEntity(request)).thenReturn(category); - CategoryDto dto = categoryService.save(request); - assertThat(dto) - .hasFieldOrPropertyWithValue("name", request.name()) - .hasFieldOrPropertyWithValue("description", request.description()); + + CategoryDto actual = categoryService.save(requestDto); + assertThat(actual) + .hasFieldOrPropertyWithValue("name", requestDto.name()) + .hasFieldOrPropertyWithValue("description", requestDto.description()); + } + + @Test + @DisplayName("Update existing category with valid request") + void update_validRequestDto_CategoryDto() { + Category category = new Category(); + category.setName("name1"); + category.setDescription("desc1"); + category.setId(1L); + + CreateCategoryRequestDto requestDto = new CreateCategoryRequestDto("name2", "desc2"); + + Category requestCategory = new Category(); + requestCategory.setId(1L); + requestCategory.setName(requestDto.name()); + requestCategory.setDescription(requestDto.description()); + + when(categoryRepository.findById(1L)).thenReturn(Optional.of(category)); + when(categoryRepository.save(category)).thenReturn(requestCategory); + + CategoryDto actual = categoryService.update(1L, requestDto); + + assertThat(actual) + .hasFieldOrPropertyWithValue("name", "name2") + .hasFieldOrPropertyWithValue("description", "desc2"); + } + + @Test + @DisplayName("Update not existing category") + void update_notValidRequestDto_Exception() { + Long id = 100L; + CreateCategoryRequestDto requestDto = new CreateCategoryRequestDto( + "name", + "desc"); + + when(categoryRepository.findById(id)).thenReturn(Optional.empty()); + assertThatThrownBy(() -> categoryService.update(id, requestDto)) + .isInstanceOf(EntityNotFoundException.class); + } + + @Test + @DisplayName("Get category by valid id") + void getById_validId_CategoryDto() { + Category category = new Category(); + category.setName("name"); + category.setDescription("desc"); + category.setId(1L); + + when(categoryRepository.findById(1L)).thenReturn(Optional.of(category)); + + CategoryDto actual = categoryService.getById(1L); + + assertThat(actual) + .hasFieldOrPropertyWithValue("name", "name") + .hasFieldOrPropertyWithValue("description", "desc"); + } + + @Test + @DisplayName("Get category by not existing id") + void getById_notValidId_Exception() { + Long id = 100L; + when(categoryRepository.findById(id)).thenReturn(Optional.empty()); + + assertThatThrownBy(() -> categoryService.getById(id)) + .isInstanceOf(EntityNotFoundException.class); } }