diff --git a/src/main/java/ttv/poltoraha/pivka/controller/AuthorController.java b/src/main/java/ttv/poltoraha/pivka/controller/AuthorController.java index 3811627..bb674c8 100644 --- a/src/main/java/ttv/poltoraha/pivka/controller/AuthorController.java +++ b/src/main/java/ttv/poltoraha/pivka/controller/AuthorController.java @@ -1,10 +1,16 @@ package ttv.poltoraha.pivka.controller; import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; +import ttv.poltoraha.pivka.dao.dto.AuthorDto; import ttv.poltoraha.pivka.entity.Author; import ttv.poltoraha.pivka.entity.Book; +import ttv.poltoraha.pivka.exception.AuthorAlreadyExistsException; +import ttv.poltoraha.pivka.exception.AuthorNotFoundException; import ttv.poltoraha.pivka.service.AuthorService; +import ttv.poltoraha.pivka.serviceImpl.AuthorServiceImpl; import java.util.List; @@ -14,20 +20,44 @@ @RequestMapping("/author") @RequiredArgsConstructor public class AuthorController { + private final AuthorService authorService; + private static final Logger logger = LoggerFactory.getLogger(AuthorController.class); + @PostMapping("/create") - public void createAuthor(@RequestBody Author author) { - authorService.create(author); + public void createAuthor(@RequestBody AuthorDto authorDto) { + logger.info("Получен запрос на создание автора: {}", authorDto); + try { + authorService.create(authorDto); + logger.info("Автор успешно создан: {}", authorDto); + } catch (AuthorAlreadyExistsException exception) { + logger.error("Ошибка при создании автора", exception); + throw exception; + } } - @PostMapping("/delete") + @DeleteMapping("/delete") public void deleteAuthorById(@RequestParam Integer id) { - authorService.delete(id); + logger.info("Получен запрос на создание автора с ID: {}", id); + try { + authorService.delete(id); + logger.info("Автор с ID успешно удален: {}", id); + } catch (AuthorNotFoundException exception) { + logger.error("Ошибка при удаление автора с ID: {}",id, exception); + throw exception; + } } @PostMapping("/add/books") public void addBooksToAuthor(@RequestParam Integer id, @RequestBody List books) { - authorService.addBooks(id, books); + logger.info("Получен запрос на добавление списка книг для автора с ID {}:", id); + try { + authorService.addBooks(id, books); + logger.info("Список книг для автора с ID успешно добавлен: {}", id); + } catch (AuthorNotFoundException exception) { + logger.error("Ошибка при добавлении списка книг для автора с ID: {}",id, exception); + throw exception; + } } } diff --git a/src/main/java/ttv/poltoraha/pivka/dao/dto/AuthorDto.java b/src/main/java/ttv/poltoraha/pivka/dao/dto/AuthorDto.java new file mode 100644 index 0000000..3d6309f --- /dev/null +++ b/src/main/java/ttv/poltoraha/pivka/dao/dto/AuthorDto.java @@ -0,0 +1,15 @@ +package ttv.poltoraha.pivka.dao.dto; + +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +@Data +@Builder +@Getter +@Setter +public class AuthorDto { + private String fullName; + private Double avgRating; +} diff --git a/src/main/java/ttv/poltoraha/pivka/entity/Author.java b/src/main/java/ttv/poltoraha/pivka/entity/Author.java index 70ef950..f19d5ad 100644 --- a/src/main/java/ttv/poltoraha/pivka/entity/Author.java +++ b/src/main/java/ttv/poltoraha/pivka/entity/Author.java @@ -1,15 +1,16 @@ package ttv.poltoraha.pivka.entity; import jakarta.persistence.*; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; +import lombok.*; import java.util.List; // Энтити - это привязка класса к конкретной табличке в БД -@Entity(name="author") @Data +@Entity(name="author") +@Builder @ToString +@AllArgsConstructor +@NoArgsConstructor public class Author { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/ttv/poltoraha/pivka/entity/Chapter.java b/src/main/java/ttv/poltoraha/pivka/entity/Chapter.java new file mode 100644 index 0000000..f9a391c --- /dev/null +++ b/src/main/java/ttv/poltoraha/pivka/entity/Chapter.java @@ -0,0 +1,17 @@ +package ttv.poltoraha.pivka.entity; + +import jakarta.persistence.*; + +@Entity +@Table(name = "chapter") +public class Chapter { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + private String title; + private Integer pageNumber; + + @ManyToOne + @JoinColumn(name = "book_id") + private Book book; +} diff --git a/src/main/java/ttv/poltoraha/pivka/exception/AuthorAlreadyExistsException.java b/src/main/java/ttv/poltoraha/pivka/exception/AuthorAlreadyExistsException.java new file mode 100644 index 0000000..e352711 --- /dev/null +++ b/src/main/java/ttv/poltoraha/pivka/exception/AuthorAlreadyExistsException.java @@ -0,0 +1,7 @@ +package ttv.poltoraha.pivka.exception; + +public class AuthorAlreadyExistsException extends RuntimeException { + public AuthorAlreadyExistsException(String message) { + super(message); + } +} diff --git a/src/main/java/ttv/poltoraha/pivka/exception/AuthorNotFoundException.java b/src/main/java/ttv/poltoraha/pivka/exception/AuthorNotFoundException.java new file mode 100644 index 0000000..c42557b --- /dev/null +++ b/src/main/java/ttv/poltoraha/pivka/exception/AuthorNotFoundException.java @@ -0,0 +1,7 @@ +package ttv.poltoraha.pivka.exception; + +public class AuthorNotFoundException extends RuntimeException { + public AuthorNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/ttv/poltoraha/pivka/exception/index.html b/src/main/java/ttv/poltoraha/pivka/exception/index.html new file mode 100644 index 0000000..97d9103 --- /dev/null +++ b/src/main/java/ttv/poltoraha/pivka/exception/index.html @@ -0,0 +1,311 @@ + + + + + + ООО СК "МЕРЕДИАН" - Строительная компания + + + +
+ +
Строим будущее вместе
+
+ + + +
+

Профессиональное строительство под ключ

+

Качество, надежность и соблюдение сроков - наши главные принципы

+ Оставить заявку +
+ +
+

О компании

+

ООО СК "МЕРЕДИАН" - это современная строительная компания, которая уже более 10 лет успешно реализует проекты различной сложности. Наша команда профессионалов гарантирует качественное выполнение работ на всех этапах строительства.

+

Мы специализируемся на строительстве жилых и коммерческих объектов, реконструкции зданий и капитальном ремонте. Наши клиенты - это частные лица, крупные компании и государственные учреждения.

+
+ +
+

Наши услуги

+
+
+ Строительство домов +

Строительство домов

+

Полный цикл строительства частных домов и коттеджей под ключ с использованием современных материалов и технологий.

+
+
+ Коммерческое строительство +

Коммерческое строительство

+

Возведение офисных зданий, торговых центров, складов и других коммерческих объектов любой сложности.

+
+
+ Реконструкция и ремонт +

Реконструкция и ремонт

+

Качественная реконструкция зданий, капитальный и косметический ремонт помещений любого назначения.

+
+
+
+ +
+

Наши проекты

+ +
+ +
+

Свяжитесь с нами

+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+ + + + diff --git a/src/main/java/ttv/poltoraha/pivka/mapping/AuthorMapper.java b/src/main/java/ttv/poltoraha/pivka/mapping/AuthorMapper.java new file mode 100644 index 0000000..b62f5eb --- /dev/null +++ b/src/main/java/ttv/poltoraha/pivka/mapping/AuthorMapper.java @@ -0,0 +1,21 @@ +package ttv.poltoraha.pivka.mapping; + +import ttv.poltoraha.pivka.dao.dto.AuthorDto; +import ttv.poltoraha.pivka.entity.Author; + +public class AuthorMapper { + + public Author mapFromDtoToEntity(AuthorDto dto) { + return Author.builder() + .avgRating(dto.getAvgRating()) + .fullName(dto.getFullName()) + .build(); + } + + public AuthorDto mapFromEntityToDto(Author entity) { + return AuthorDto.builder() + .avgRating(entity.getAvgRating()) + .fullName(entity.getFullName()) + .build(); + } +} diff --git a/src/main/java/ttv/poltoraha/pivka/repository/AuthorRepository.java b/src/main/java/ttv/poltoraha/pivka/repository/AuthorRepository.java index 87de14a..fa21989 100644 --- a/src/main/java/ttv/poltoraha/pivka/repository/AuthorRepository.java +++ b/src/main/java/ttv/poltoraha/pivka/repository/AuthorRepository.java @@ -8,6 +8,7 @@ import ttv.poltoraha.pivka.entity.Author; import java.util.List; +import java.util.Optional; // Репозиторий - это интерфейс, позволяющий нам писать изолированные от sql логики запросы в БД @Repository @@ -20,4 +21,6 @@ public interface AuthorRepository extends CrudRepository { @Query("SELECT DISTINCT a FROM author a JOIN a.books b WHERE b.tags LIKE %:tag% ORDER BY a.avgRating DESC, a.id ASC") List findTopAuthorsByTag(@Param("tag") String tag); + + public Optional findAuthorByFullName(String fullName); } diff --git a/src/main/java/ttv/poltoraha/pivka/repository/ChapterRepository.java b/src/main/java/ttv/poltoraha/pivka/repository/ChapterRepository.java new file mode 100644 index 0000000..9f45e6a --- /dev/null +++ b/src/main/java/ttv/poltoraha/pivka/repository/ChapterRepository.java @@ -0,0 +1,9 @@ +package ttv.poltoraha.pivka.repository; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; +import ttv.poltoraha.pivka.entity.Chapter; + +@Repository +public interface ChapterRepository extends CrudRepository { +} diff --git a/src/main/java/ttv/poltoraha/pivka/repository/QuoteRepository.java b/src/main/java/ttv/poltoraha/pivka/repository/QuoteRepository.java new file mode 100644 index 0000000..8a65d21 --- /dev/null +++ b/src/main/java/ttv/poltoraha/pivka/repository/QuoteRepository.java @@ -0,0 +1,8 @@ +package ttv.poltoraha.pivka.repository; + +import org.springframework.data.repository.CrudRepository; +import ttv.poltoraha.pivka.entity.Quote; + +public interface QuoteRepository extends CrudRepository { + +} diff --git a/src/main/java/ttv/poltoraha/pivka/service/AuthorService.java b/src/main/java/ttv/poltoraha/pivka/service/AuthorService.java index 082afa2..6cfb677 100644 --- a/src/main/java/ttv/poltoraha/pivka/service/AuthorService.java +++ b/src/main/java/ttv/poltoraha/pivka/service/AuthorService.java @@ -1,5 +1,6 @@ package ttv.poltoraha.pivka.service; +import ttv.poltoraha.pivka.dao.dto.AuthorDto; import ttv.poltoraha.pivka.entity.Author; import ttv.poltoraha.pivka.entity.Book; @@ -7,7 +8,7 @@ // Сервис - это классы с основной логикой. Хорошей практикой является создание интерфейса. // Интерфейс позволяет нам удобно расширять функционал или делать несколько реализаций без изменения, например, вызова метода в контроллере public interface AuthorService { - public void create(Author author); + public void create(AuthorDto authorDto); public void delete(Integer id); public void addBook(Integer id, Book book); public void addBooks(Integer id, List books); diff --git a/src/main/java/ttv/poltoraha/pivka/serviceImpl/AuthorServiceImpl.java b/src/main/java/ttv/poltoraha/pivka/serviceImpl/AuthorServiceImpl.java index 5344bec..2f476bd 100644 --- a/src/main/java/ttv/poltoraha/pivka/serviceImpl/AuthorServiceImpl.java +++ b/src/main/java/ttv/poltoraha/pivka/serviceImpl/AuthorServiceImpl.java @@ -1,13 +1,22 @@ package ttv.poltoraha.pivka.serviceImpl; +import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j; +import lombok.extern.slf4j.Slf4j; import lombok.val; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import ttv.poltoraha.pivka.dao.dto.AuthorDto; import ttv.poltoraha.pivka.entity.Author; import ttv.poltoraha.pivka.entity.Book; +import ttv.poltoraha.pivka.exception.AuthorAlreadyExistsException; +import ttv.poltoraha.pivka.exception.AuthorNotFoundException; +import ttv.poltoraha.pivka.mapping.AuthorMapper; import ttv.poltoraha.pivka.repository.AuthorRepository; import ttv.poltoraha.pivka.service.AuthorService; @@ -15,41 +24,59 @@ // Имплементации интерфейсов с бизнес-логикой @Service +@Transactional @RequiredArgsConstructor public class AuthorServiceImpl implements AuthorService { + private final AuthorRepository authorRepository; + private final AuthorMapper authorMapper; + + private static final Logger logger = LoggerFactory.getLogger(AuthorServiceImpl.class); - // todo как будто надо насрать всякими мапперами @Override - @Transactional - public void create(Author author) { - authorRepository.save(author); + public void create(AuthorDto authorDto) { + logger.debug("Проверка существования автора: {}", authorDto.getFullName()); + authorRepository.findAuthorByFullName(authorDto.getFullName()).ifPresent( + author -> { + throw new AuthorAlreadyExistsException(String.format( + "Author with fullName = %s already exists", authorDto.getFullName()) + ); + } + ); + logger.debug("Создание нового автора"); + Author savedAuthor = authorMapper.mapFromDtoToEntity(authorDto); + authorRepository.save(savedAuthor); + logger.info("Автор сохранен в БД с ID: {}", savedAuthor.getId()); } @Override - @Transactional public void delete(Integer id) { - authorRepository.deleteById(id); + logger.debug("Проверка существования автора с ID: {}", id); + Author obtainedAuthor = getOrThrow(id); + logger.debug("Удаление нового автора"); + authorRepository.deleteById(obtainedAuthor.getId()); + logger.info("Автор удален из БД с ID: {}", id); } @Override - @Transactional public void addBooks(Integer id, List books) { + logger.debug("Проверка существования автора с ID: {}", id); val author = getOrThrow(id); - + logger.debug("Добавление книг для автора с ID: {}", id); author.getBooks().addAll(books); + logger.info("Книги добавлены в список книг автора с ID: {}", id); } @Override - @Transactional public void addBook(Integer id, Book book) { + logger.debug("Проверка существования автора с ID: {}", id); val author = getOrThrow(id); - + logger.debug("Добавление книги для автора с ID: {}", id); author.getBooks().add(book); + logger.info("Книга добавлены в список книг автора с ID: {}", id); } @Override - @Transactional public List getTopAuthorsByTag(String tag, int count) { Pageable pageable = PageRequest.of(0, count); val authors = authorRepository.findTopAuthorsByTag(tag); @@ -58,13 +85,10 @@ public List getTopAuthorsByTag(String tag, int count) { } private Author getOrThrow(Integer id) { - val optionalAuthor = authorRepository.findById(id); - val author = optionalAuthor.orElse(null); - - if (author == null) { - throw new RuntimeException("Author with id = " + id + " not found"); - } - - return author; + return authorRepository.findById(id) + .orElseThrow(() -> { + logger.warn("Не найден автор с ID: {}", id); + throw new AuthorNotFoundException("Author with id = " + id + " not found"); + }); } } diff --git a/src/main/java/ttv/poltoraha/pivka/serviceImpl/ReaderServiceImpl.java b/src/main/java/ttv/poltoraha/pivka/serviceImpl/ReaderServiceImpl.java index 7648630..0f24e30 100644 --- a/src/main/java/ttv/poltoraha/pivka/serviceImpl/ReaderServiceImpl.java +++ b/src/main/java/ttv/poltoraha/pivka/serviceImpl/ReaderServiceImpl.java @@ -9,6 +9,7 @@ import ttv.poltoraha.pivka.entity.Reader; import ttv.poltoraha.pivka.entity.Reading; import ttv.poltoraha.pivka.repository.BookRepository; +import ttv.poltoraha.pivka.repository.QuoteRepository; import ttv.poltoraha.pivka.repository.ReaderRepository; import ttv.poltoraha.pivka.service.ReaderService; import util.MyUtility; @@ -19,6 +20,8 @@ public class ReaderServiceImpl implements ReaderService { private final ReaderRepository readerRepository; private final BookRepository bookRepository; + private final QuoteRepository quoteRepository; + @Override public void createQuote(String username, Integer book_id, String text) { val newQuote = new Quote(); @@ -33,7 +36,8 @@ public void createQuote(String username, Integer book_id, String text) { reader.getQuotes().add(newQuote); // todo потенциально лучше сейвить quoteRepository. Чем меньше вложенностей у сохраняемой сущности - тем эффективнее это будет происходить. - readerRepository.save(reader); +// readerRepository.save(reader); + quoteRepository.save(newQuote); } @Override diff --git a/src/main/java/ttv/poltoraha/pivka/serviceImpl/RecommendationServiceImpl.java b/src/main/java/ttv/poltoraha/pivka/serviceImpl/RecommendationServiceImpl.java index b94366a..693f5f9 100644 --- a/src/main/java/ttv/poltoraha/pivka/serviceImpl/RecommendationServiceImpl.java +++ b/src/main/java/ttv/poltoraha/pivka/serviceImpl/RecommendationServiceImpl.java @@ -98,7 +98,7 @@ public List recommendBook(String username) { */ @Override public List recommendQuoteByBook(Integer book_id) { - if (bookRepository.existsById(book_id)) { + if (!bookRepository.existsById(book_id)) { throw new EntityNotFoundException(String.format("Entity book with id = %s was not found", book_id)); } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 2039a45..1d71afb 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,6 +1,11 @@ logging: level: org.apache.kafka: WARN # Уровень логирования для Kafka + ttv.poltoraha.pivka: DEBUG + pattern: + console: %d{yyyy-MM-dd HH:mm:ss} - %logger{36} - %msg%n + + spring: application: diff --git a/src/main/resources/db/changelog/v1.0.4_add_chapter_tables.xml b/src/main/resources/db/changelog/v1.0.4_add_chapter_tables.xml new file mode 100644 index 0000000..bfe50f7 --- /dev/null +++ b/src/main/resources/db/changelog/v1.0.4_add_chapter_tables.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/liquibase-master.xml b/src/main/resources/db/liquibase-master.xml index 7c7b37c..bdf2bb1 100644 --- a/src/main/resources/db/liquibase-master.xml +++ b/src/main/resources/db/liquibase-master.xml @@ -7,6 +7,7 @@ + diff --git a/src/test/java/ttv/poltoraha/pivka/app/repository/AuthorRepositoryTest.java b/src/test/java/ttv/poltoraha/pivka/app/repository/AuthorRepositoryTest.java index 0bfdd39..c606ead 100644 --- a/src/test/java/ttv/poltoraha/pivka/app/repository/AuthorRepositoryTest.java +++ b/src/test/java/ttv/poltoraha/pivka/app/repository/AuthorRepositoryTest.java @@ -7,7 +7,4 @@ @SpringBootTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY) // Используйте H2 вместо реальной БД @Transactional // Обеспечивает откат транзакций после каждого теста -public class AuthorRepositoryTest { - - -} +public class AuthorRepositoryTest { } diff --git a/src/test/java/ttv/poltoraha/pivka/app/serviceImpl/AuthorServiceImplTest.java b/src/test/java/ttv/poltoraha/pivka/app/serviceImpl/AuthorServiceImplTest.java new file mode 100644 index 0000000..82dccd3 --- /dev/null +++ b/src/test/java/ttv/poltoraha/pivka/app/serviceImpl/AuthorServiceImplTest.java @@ -0,0 +1,192 @@ +package ttv.poltoraha.pivka.app.serviceImpl; + +import org.junit.jupiter.api.DisplayName; +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.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import ttv.poltoraha.pivka.app.util.DataAuthorUtils; +import ttv.poltoraha.pivka.dao.dto.AuthorDto; +import ttv.poltoraha.pivka.entity.Author; +import ttv.poltoraha.pivka.entity.Book; +import ttv.poltoraha.pivka.exception.AuthorAlreadyExistsException; +import ttv.poltoraha.pivka.exception.AuthorNotFoundException; +import ttv.poltoraha.pivka.mapping.AuthorMapper; +import ttv.poltoraha.pivka.repository.AuthorRepository; +import ttv.poltoraha.pivka.serviceImpl.AuthorServiceImpl; + +import java.util.*; + +import static org.assertj.core.api.Assertions.assertThat; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.*; + +//@SpringBootTest +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY) // Используйте H2 вместо реальной БД +@Transactional // Обеспечивает откат транзакций после каждого теста +@ExtendWith(MockitoExtension.class) +public class AuthorServiceImplTest { + + @Mock + private AuthorRepository authorRepository; + @Mock + private AuthorMapper authorMapper; + @InjectMocks + private AuthorServiceImpl serviceUnderTest; + + @Test + @DisplayName("Test create author functionality") + public void givenAuthorToCreate_whenCreateNewAuthor_thenRepositoryIsCalled() { + //given + AuthorDto authorToCreate = DataAuthorUtils.getLevTolstoyTransientDto(); + Author expectedAuthor = DataAuthorUtils.getLevTolstoyPersisted(); + + given(authorRepository.findAuthorByFullName(anyString())) + .willReturn(Optional.empty()); + + given(authorMapper.mapFromDtoToEntity(any(AuthorDto.class))) + .willReturn(expectedAuthor); + + given(authorRepository.save(any(Author.class))) + .willReturn(expectedAuthor); + //when + serviceUnderTest.create(authorToCreate); + //then + verify(authorRepository, times(1)).save(any(Author.class)); + } + + @Test + @DisplayName("Test create author with duplicate full name functionality") + public void givenAuthorToCreateWithDuplicateFullName_whenCreateNewAuthor_thenExceptionIsThrow() { + //given + AuthorDto authorToCreate = DataAuthorUtils.getLevTolstoyTransientDto(); + given(authorRepository.findAuthorByFullName(authorToCreate.getFullName())) + .willReturn(Optional.of(DataAuthorUtils.getLevTolstoyPersisted())); + //when + assertThrows( + AuthorAlreadyExistsException.class, () -> serviceUnderTest.create(authorToCreate) + ); + //then + verify(authorRepository, never()).save(any(Author.class)); + } + + @Test + @DisplayName("Test delete author by id functionality") + public void givenId_whenDeleteById_thenRepositoryIsCalled() { + //given + given(authorRepository.findById(anyInt())) + .willReturn(Optional.of(DataAuthorUtils.getLevTolstoyPersisted())); + //when + serviceUnderTest.delete(1); + //then + verify(authorRepository, times(1)).deleteById(anyInt()); + } + + @Test + @DisplayName("Test delete author by incorrect id functionality") + public void givenIncorrectId_whenDeleteById_thenExceptionIsThrow() { + //given + given(authorRepository.findById(anyInt())) + .willReturn(Optional.empty()); + //when + assertThrows( + AuthorNotFoundException.class, () -> serviceUnderTest.delete(1) + ); + //then + verify(authorRepository, never()).deleteById(anyInt()); + } + + @Test + @DisplayName("Test add book for author by id functionality") + public void givenBookForAuthorById_whenAddBookByAuthorId_thenBookAddedInListBook() { + // given + Author author = spy(Author.builder() + .id(1) + .fullName("Лев Толстой") + .books(new ArrayList<>()) + .build()); + + Book book = Book.builder() + .author(author) + .article("War and Peace") + .build(); + + given(authorRepository.findById(anyInt())) + .willReturn(Optional.of(author)); + + // when + serviceUnderTest.addBook(1, book); + + // then + assertThat(author.getBooks()) + .hasSize(1) + .containsExactly(book); + assertThat(book.getAuthor()).isEqualTo(author); + } + + @Test + @DisplayName("Test add book for author by incorrect id functionality") + public void givenBookForAuthorByIncorrectId_whenAddBookByIncorrectAuthorId_thenExceptionIsThrow() { + //given + //when + //then + } + + @Test + @DisplayName("Test add books for author by id functionality") + public void givenBooksForAuthorById_whenAddBooksByAuthorId_thenBooksAddedInListBooks() { + // given + Author author = spy(Author.builder() + .id(1) + .fullName("Лев Толстой") + .books(new ArrayList<>()) + .build()); + + Book book1 = Book.builder() + .author(author) + .article("War and Peace") + .build(); + + Book book2 = Book.builder() + .author(author) + .article("Spider man") + .build(); + + Book book3 = Book.builder() + .author(author) + .article("Clean code") + .build(); + + List books = new ArrayList<>(Arrays.asList(book1,book2, book3)); + + given(authorRepository.findById(anyInt())) + .willReturn(Optional.of(author)); + + // when + serviceUnderTest.addBooks(1, books); + + // then + assertThat(CollectionUtils.isEmpty(author.getBooks())).isFalse(); + assertThat(author.getBooks()).hasSize(3); + } + + @Test + @DisplayName("Test add books for author by incorrect id functionality") + public void givenBooksForAuthorByIncorrectId_whenAddBooksByIncorrectAuthorId_thenExceptionIsThrow() { + //given + //when + //then + } + + +} + diff --git a/src/test/java/ttv/poltoraha/pivka/app/util/DataAuthorUtils.java b/src/test/java/ttv/poltoraha/pivka/app/util/DataAuthorUtils.java new file mode 100644 index 0000000..148fc6d --- /dev/null +++ b/src/test/java/ttv/poltoraha/pivka/app/util/DataAuthorUtils.java @@ -0,0 +1,32 @@ +package ttv.poltoraha.pivka.app.util; + +import ttv.poltoraha.pivka.dao.dto.AuthorDto; +import ttv.poltoraha.pivka.entity.Author; + +public class DataAuthorUtils { + + public static Author getLevTolstoyTransient() { + return Author.builder() + .fullName("Lev Nilolaevich Tolctoy") + .avgRating(10.0) + .build(); + } + + public static AuthorDto getLevTolstoyTransientDto() { + return AuthorDto.builder() + .fullName("Lev Nilolaevich Tolctoy") + .avgRating(10.0) + .build(); + } + + + public static Author getLevTolstoyPersisted() { + return Author.builder() + .id(1) + .fullName("Lev Nilolaevich Tolctoy") + .avgRating(10.0) + .build(); + } + + +}