diff --git a/pom.xml b/pom.xml index ebdb155..d4dba1f 100644 --- a/pom.xml +++ b/pom.xml @@ -131,6 +131,10 @@ ${surefire.and.failsafe.report.dir} + + **/*ITCase.java + **/*ComponentTest.java + diff --git a/src/component-test/java/io/wkrzywiec/hexagonal/library/borrowing/MakeReservationComponentTest.java b/src/component-test/java/io/wkrzywiec/hexagonal/library/borrowing/MakeReservationComponentTest.java new file mode 100644 index 0000000..6fe28cb --- /dev/null +++ b/src/component-test/java/io/wkrzywiec/hexagonal/library/borrowing/MakeReservationComponentTest.java @@ -0,0 +1,85 @@ +package io.wkrzywiec.hexagonal.library.borrowing; + +import io.wkrzywiec.hexagonal.library.TestData; +import io.wkrzywiec.hexagonal.library.borrowing.model.BookReservationCommand; +import io.wkrzywiec.hexagonal.library.inventory.infrastructure.BookRepository; +import io.wkrzywiec.hexagonal.library.inventory.model.Book; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.jdbc.core.JdbcTemplate; + +import static io.restassured.RestAssured.given; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class MakeReservationComponentTest { + + @LocalServerPort + private int port; + + @Autowired + private BookRepository bookRepository; + + @Autowired + private JdbcTemplate jdbcTemplate; + + private String baseURL; + + @BeforeEach + public void init() { + this.baseURL = "http://localhost:" + port; + + Book book = bookRepository.save(TestData.homoDeusBook()); + jdbcTemplate.update( + "INSERT INTO available (book_id) VALUES (?)", + book.getIdAsLong()); + + jdbcTemplate.update( + "INSERT INTO user (first_name, last_name, email) VALUES (?, ?, ?)", + "John", + "Doe", + "john.doe@test.com"); + } + + @Test + @DisplayName("Reserve available book") + public void givenBookIsAvailable_thenMakeReservation_thenBookIsReserved() { + //given + Long homoDeusBookId = jdbcTemplate.queryForObject( + "SELECT id FROM book WHERE title = ?", + Long.class, + TestData.homoDeusBookTitle()); + + Long activeUserId = jdbcTemplate.queryForObject( + "SELECT id FROM user WHERE email = ?", + Long.class, + "john.doe@test.com"); + + + BookReservationCommand reservationCommand = + BookReservationCommand.builder() + .bookId(homoDeusBookId ) + .userId(activeUserId) + .build(); + + //when + given() + .contentType("application/json") + .body(reservationCommand) + .when() + .post( baseURL + "/reservations") + .prettyPeek() + .then(); + + Long reservationId = jdbcTemplate.queryForObject( + "SELECT id FROM reserved WHERE book_id = ?", + Long.class, + homoDeusBookId); + + assertTrue(reservationId > 0); + } +} diff --git a/src/component-test/java/io/wkrzywiec/hexagonal/library/inventory/AddNewBookTest.java b/src/component-test/java/io/wkrzywiec/hexagonal/library/inventory/AddNewBookComponentTest.java similarity index 88% rename from src/component-test/java/io/wkrzywiec/hexagonal/library/inventory/AddNewBookTest.java rename to src/component-test/java/io/wkrzywiec/hexagonal/library/inventory/AddNewBookComponentTest.java index 9b2a273..e1f638a 100644 --- a/src/component-test/java/io/wkrzywiec/hexagonal/library/inventory/AddNewBookTest.java +++ b/src/component-test/java/io/wkrzywiec/hexagonal/library/inventory/AddNewBookComponentTest.java @@ -4,7 +4,6 @@ import io.restassured.RestAssured; import io.restassured.response.ValidatableResponse; import io.wkrzywiec.hexagonal.library.TestData; import io.wkrzywiec.hexagonal.library.inventory.model.AddNewBookCommand; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -12,16 +11,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.http.HttpStatus; -import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import static io.restassured.RestAssured.given; import static org.hamcrest.Matchers.greaterThan; - import static org.junit.jupiter.api.Assertions.assertTrue; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -public class AddNewBookTest { +public class AddNewBookComponentTest { @LocalServerPort private int port; @@ -55,8 +52,8 @@ public class AddNewBookTest { } @Test - @DisplayName("Add new book to a database") - public void givenGoogleBooId_whenAddNewBook_thenBookIsSaved(){ + @DisplayName("Add new book to a database & make it available") + public void givenGoogleBooId_whenAddNewBook_thenBookIsSaved() { //given AddNewBookCommand addNewBookCommand = AddNewBookCommand.builder() @@ -77,6 +74,14 @@ public class AddNewBookTest { "SELECT id FROM book WHERE book_external_id = ?", Long.class, TestData.homoDeusBookGoogleId()); + assertTrue(savedBookId > 0); + + Long availableBookId = jdbc.queryForObject( + "SELECT id FROM available WHERE book_id = ?", + Long.class, + savedBookId); + + assertTrue(availableBookId > 0); } } diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/BorrowingDomainConfig.java b/src/main/java/io/wkrzywiec/hexagonal/library/BorrowingDomainConfig.java new file mode 100644 index 0000000..c6c1cf3 --- /dev/null +++ b/src/main/java/io/wkrzywiec/hexagonal/library/BorrowingDomainConfig.java @@ -0,0 +1,23 @@ +package io.wkrzywiec.hexagonal.library; + +import io.wkrzywiec.hexagonal.library.borrowing.BorrowingFacade; +import io.wkrzywiec.hexagonal.library.borrowing.infrastructure.BorrowingDatabaseAdapter; +import io.wkrzywiec.hexagonal.library.borrowing.ports.incoming.MakeBookAvailable; +import io.wkrzywiec.hexagonal.library.borrowing.ports.outgoing.BorrowingDatabase; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; + +@Configuration +public class BorrowingDomainConfig { + + @Bean + public BorrowingDatabase borrowingDatabase(JdbcTemplate jdbcTemplate) { + return new BorrowingDatabaseAdapter(jdbcTemplate); + } + + @Bean + public MakeBookAvailable makeBookAvailable(BorrowingDatabase database) { + return new BorrowingFacade(database); + } +} diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/InventoryDomainConfig.java b/src/main/java/io/wkrzywiec/hexagonal/library/InventoryDomainConfig.java new file mode 100644 index 0000000..71d3cad --- /dev/null +++ b/src/main/java/io/wkrzywiec/hexagonal/library/InventoryDomainConfig.java @@ -0,0 +1,29 @@ +package io.wkrzywiec.hexagonal.library; + +import io.wkrzywiec.hexagonal.library.inventory.InventoryFacade; +import io.wkrzywiec.hexagonal.library.inventory.infrastructure.BookRepository; +import io.wkrzywiec.hexagonal.library.inventory.infrastructure.GoogleBooksAdapter; +import io.wkrzywiec.hexagonal.library.inventory.infrastructure.InventoryDatabaseAdapter; +import io.wkrzywiec.hexagonal.library.inventory.infrastructure.SpringInventoryEventPublisherAdapter; +import io.wkrzywiec.hexagonal.library.inventory.ports.incoming.AddNewBook; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +class InventoryDomainConfig { + + @Bean + SpringInventoryEventPublisherAdapter springInventoryEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + return new SpringInventoryEventPublisherAdapter(applicationEventPublisher); + } + + @Bean + AddNewBook addNewBook(BookRepository repository, RestTemplate restTemplate, ApplicationEventPublisher applicationEventPublisher){ + return new InventoryFacade( + new InventoryDatabaseAdapter(repository), + new GoogleBooksAdapter(restTemplate), + springInventoryEventPublisher(applicationEventPublisher)); + } +} diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/LibraryHexagonalConfig.java b/src/main/java/io/wkrzywiec/hexagonal/library/LibraryHexagonalConfig.java new file mode 100644 index 0000000..12f0542 --- /dev/null +++ b/src/main/java/io/wkrzywiec/hexagonal/library/LibraryHexagonalConfig.java @@ -0,0 +1,14 @@ +package io.wkrzywiec.hexagonal.library; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class LibraryHexagonalConfig { + + @Bean + RestTemplate restTemplate(){ + return new RestTemplate(); + } +} diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/application/BorrowingController.java b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/application/BorrowingController.java deleted file mode 100644 index 4ee2eb3..0000000 --- a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/application/BorrowingController.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.wkrzywiec.hexagonal.library.borrowing.application; - -public class BorrowingController { -} diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/application/NewBookWasAddedEventHandler.java b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/application/NewBookWasAddedEventHandler.java new file mode 100644 index 0000000..c57f9b2 --- /dev/null +++ b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/application/NewBookWasAddedEventHandler.java @@ -0,0 +1,20 @@ +package io.wkrzywiec.hexagonal.library.borrowing.application; + +import io.wkrzywiec.hexagonal.library.borrowing.model.MakeBookAvailableCommand; +import io.wkrzywiec.hexagonal.library.borrowing.ports.incoming.MakeBookAvailable; +import io.wkrzywiec.hexagonal.library.inventory.model.NewBookWasAddedEvent; +import lombok.RequiredArgsConstructor; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Component +public class NewBookWasAddedEventHandler { + + private final MakeBookAvailable makeBookAvailable; + + @EventListener + public void handle(NewBookWasAddedEvent event){ + makeBookAvailable.handle(new MakeBookAvailableCommand(event.getBookIdAsLong())); + } +} diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/application/ReservationController.java b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/application/ReservationController.java new file mode 100644 index 0000000..3875333 --- /dev/null +++ b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/application/ReservationController.java @@ -0,0 +1,25 @@ +package io.wkrzywiec.hexagonal.library.borrowing.application; + +import io.wkrzywiec.hexagonal.library.borrowing.model.BookReservationCommand; +import io.wkrzywiec.hexagonal.library.borrowing.ports.incoming.ReserveBook; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/reservations") +@RequiredArgsConstructor +public class ReservationController { + + private final ReserveBook reserveBook; + + @PostMapping("") + public ResponseEntity makeReservation(@RequestBody BookReservationCommand reservationCommand){ + Long reservationId = reserveBook.handle(reservationCommand); + return new ResponseEntity<>("Reservation has been made with an id " + reservationId, HttpStatus.CREATED); + } +} diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/infrastructure/BorrowingDatabaseAdapter.java b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/infrastructure/BorrowingDatabaseAdapter.java index 876129b..8b88745 100644 --- a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/infrastructure/BorrowingDatabaseAdapter.java +++ b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/infrastructure/BorrowingDatabaseAdapter.java @@ -15,7 +15,7 @@ import java.util.List; import java.util.Optional; @AllArgsConstructor -class BorrowingDatabaseAdapter implements BorrowingDatabase { +public class BorrowingDatabaseAdapter implements BorrowingDatabase { private JdbcTemplate jdbcTemplate; diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/InventoryFacade.java b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/InventoryFacade.java index 4e77087..c1368db 100644 --- a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/InventoryFacade.java +++ b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/InventoryFacade.java @@ -2,23 +2,29 @@ package io.wkrzywiec.hexagonal.library.inventory; import io.wkrzywiec.hexagonal.library.inventory.model.AddNewBookCommand; import io.wkrzywiec.hexagonal.library.inventory.model.Book; +import io.wkrzywiec.hexagonal.library.inventory.model.NewBookWasAddedEvent; import io.wkrzywiec.hexagonal.library.inventory.ports.incoming.AddNewBook; +import io.wkrzywiec.hexagonal.library.inventory.ports.outgoing.InventoryEventPublisher; import io.wkrzywiec.hexagonal.library.inventory.ports.outgoing.GetBookDetails; import io.wkrzywiec.hexagonal.library.inventory.ports.outgoing.InventoryDatabase; + public class InventoryFacade implements AddNewBook{ private InventoryDatabase database; private GetBookDetails getBookDetails; + private InventoryEventPublisher eventPublisher; - public InventoryFacade(InventoryDatabase database, GetBookDetails getBookDetails) { + public InventoryFacade(InventoryDatabase database, GetBookDetails getBookDetails, InventoryEventPublisher eventPublisher) { this.database = database; this.getBookDetails = getBookDetails; + this.eventPublisher = eventPublisher; } @Override public void handle(AddNewBookCommand addNewBookCommand){ Book book = getBookDetails.handle(addNewBookCommand.getGoogleBookId()); - database.save(book); + Book savedBook = database.save(book); + eventPublisher.publishNewBookWasAddedEvent(new NewBookWasAddedEvent(savedBook.getIdAsLong())); } } diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/BookRepository.java b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/BookRepository.java index a6feb62..ae06c1c 100644 --- a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/BookRepository.java +++ b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/BookRepository.java @@ -5,5 +5,5 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; @Repository -interface BookRepository extends CrudRepository { +public interface BookRepository extends CrudRepository { } diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/GoogleBooksAdapter.java b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/GoogleBooksAdapter.java index d11a8eb..d2e2aad 100644 --- a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/GoogleBooksAdapter.java +++ b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/GoogleBooksAdapter.java @@ -24,7 +24,7 @@ import java.util.stream.StreamSupport; import static java.util.Optional.ofNullable; @RequiredArgsConstructor -class GoogleBooksAdapter implements GetBookDetails { +public class GoogleBooksAdapter implements GetBookDetails { private final RestTemplate restTemplate; diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/InventoryDatabaseAdapter.java b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/InventoryDatabaseAdapter.java index 07d5a39..0a47d0d 100644 --- a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/InventoryDatabaseAdapter.java +++ b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/InventoryDatabaseAdapter.java @@ -5,12 +5,12 @@ import io.wkrzywiec.hexagonal.library.inventory.ports.outgoing.InventoryDatabase import lombok.RequiredArgsConstructor; @RequiredArgsConstructor -class InventoryDatabaseAdapter implements InventoryDatabase { +public class InventoryDatabaseAdapter implements InventoryDatabase { private final BookRepository repository; @Override - public void save(Book book) { - repository.save(book); + public Book save(Book book) { + return repository.save(book); } } diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/InventoryDomainConfig.java b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/InventoryDomainConfig.java deleted file mode 100644 index 6016742..0000000 --- a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/InventoryDomainConfig.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.wkrzywiec.hexagonal.library.inventory.infrastructure; - -import io.wkrzywiec.hexagonal.library.inventory.InventoryFacade; -import io.wkrzywiec.hexagonal.library.inventory.ports.incoming.AddNewBook; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestTemplate; - -@Configuration -class InventoryDomainConfig { - - @Bean - RestTemplate restTemplate(){ - return new RestTemplate(); - } - - @Bean - AddNewBook addNewBook(BookRepository repository, RestTemplate restTemplate){ - return new InventoryFacade( - new InventoryDatabaseAdapter(repository), - new GoogleBooksAdapter(restTemplate)); - } -} diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/SpringInventoryEventPublisherAdapter.java b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/SpringInventoryEventPublisherAdapter.java new file mode 100644 index 0000000..6d9868d --- /dev/null +++ b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/SpringInventoryEventPublisherAdapter.java @@ -0,0 +1,17 @@ +package io.wkrzywiec.hexagonal.library.inventory.infrastructure; + +import io.wkrzywiec.hexagonal.library.inventory.model.NewBookWasAddedEvent; +import io.wkrzywiec.hexagonal.library.inventory.ports.outgoing.InventoryEventPublisher; +import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; + +@RequiredArgsConstructor +public class SpringInventoryEventPublisherAdapter implements InventoryEventPublisher { + + private final ApplicationEventPublisher applicationEventPublisher; + + @Override + public void publishNewBookWasAddedEvent(NewBookWasAddedEvent event) { + applicationEventPublisher.publishEvent(event); + } +} diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/model/Book.java b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/model/Book.java index b8c770b..4e81980 100644 --- a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/model/Book.java +++ b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/model/Book.java @@ -67,6 +67,10 @@ public class Book { this.imageLink = imageLink; } + public Long getIdAsLong(){ + return id; + } + private Book() { } } diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/model/NewBookWasAddedEvent.java b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/model/NewBookWasAddedEvent.java new file mode 100644 index 0000000..3a6c4f6 --- /dev/null +++ b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/model/NewBookWasAddedEvent.java @@ -0,0 +1,22 @@ +package io.wkrzywiec.hexagonal.library.inventory.model; + +import java.time.Instant; + +public class NewBookWasAddedEvent { + + private final Long bookId; + private final Instant timestamp; + + public NewBookWasAddedEvent(Long bookId) { + this.bookId = bookId; + timestamp = Instant.now(); + } + + public Long getBookIdAsLong() { + return bookId; + } + + public String getEventTimeStampAsString() { + return timestamp.toString(); + } +} diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/ports/outgoing/InventoryDatabase.java b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/ports/outgoing/InventoryDatabase.java index 2c5ddf8..943ebef 100644 --- a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/ports/outgoing/InventoryDatabase.java +++ b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/ports/outgoing/InventoryDatabase.java @@ -3,5 +3,5 @@ package io.wkrzywiec.hexagonal.library.inventory.ports.outgoing; import io.wkrzywiec.hexagonal.library.inventory.model.Book; public interface InventoryDatabase { - void save(Book book); + Book save(Book book); } diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/inventory/ports/outgoing/InventoryEventPublisher.java b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/ports/outgoing/InventoryEventPublisher.java new file mode 100644 index 0000000..fb47af3 --- /dev/null +++ b/src/main/java/io/wkrzywiec/hexagonal/library/inventory/ports/outgoing/InventoryEventPublisher.java @@ -0,0 +1,7 @@ +package io.wkrzywiec.hexagonal.library.inventory.ports.outgoing; + +import io.wkrzywiec.hexagonal.library.inventory.model.NewBookWasAddedEvent; + +public interface InventoryEventPublisher { + void publishNewBookWasAddedEvent(NewBookWasAddedEvent event); +} diff --git a/src/test/java/io/wkrzywiec/hexagonal/library/architecture/HexagonalArchitectureTest.java b/src/test/java/io/wkrzywiec/hexagonal/library/architecture/BorrowingArchitectureTest.java similarity index 56% rename from src/test/java/io/wkrzywiec/hexagonal/library/architecture/HexagonalArchitectureTest.java rename to src/test/java/io/wkrzywiec/hexagonal/library/architecture/BorrowingArchitectureTest.java index 0c6d95b..825af04 100644 --- a/src/test/java/io/wkrzywiec/hexagonal/library/architecture/HexagonalArchitectureTest.java +++ b/src/test/java/io/wkrzywiec/hexagonal/library/architecture/BorrowingArchitectureTest.java @@ -5,22 +5,13 @@ import com.tngtech.archunit.junit.AnalyzeClasses; import com.tngtech.archunit.junit.ArchTest; import com.tngtech.archunit.lang.ArchRule; import io.wkrzywiec.hexagonal.library.borrowing.BorrowingFacade; -import io.wkrzywiec.hexagonal.library.inventory.InventoryFacade; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClass; import static com.tngtech.archunit.library.Architectures.onionArchitecture; -@AnalyzeClasses(packages = {"io.wkrzywiec.hexagonal.library.inventory", - "io.wkrzywiec.hexagonal.library.borrowing"}, +@AnalyzeClasses(packages = {"io.wkrzywiec.hexagonal.library.borrowing"}, importOptions = { ImportOption.DoNotIncludeTests.class }) -public class HexagonalArchitectureTest { - - @ArchTest - public static final ArchRule hexagonalArchInInventoryDomain = onionArchitecture() - .domainModels("io.wkrzywiec.hexagonal.library.inventory.model..") - .domainServices("io.wkrzywiec.hexagonal.library.inventory..") - .applicationServices("io.wkrzywiec.hexagonal.library.inventory.application..") - .adapter("infrastructure", "io.wkrzywiec.hexagonal.library.inventory.infrastructure.."); +public class BorrowingArchitectureTest { @ArchTest public static final ArchRule hexagonalArchInBorrowingDomain = onionArchitecture() @@ -29,13 +20,6 @@ public class HexagonalArchitectureTest { .applicationServices("io.wkrzywiec.hexagonal.library.borrowing.application..") .adapter("infrastructure", "io.wkrzywiec.hexagonal.library.borrowing.infrastructure.."); - @ArchTest - public static final ArchRule noSpringDependenciesInInventoryFacade = - noClass(InventoryFacade.class) - .should() - .dependOnClassesThat() - .resideInAPackage("org.springframework.."); - @ArchTest public static final ArchRule noSpringDependenciesInBorrowingFacade = noClass(BorrowingFacade.class) diff --git a/src/test/java/io/wkrzywiec/hexagonal/library/architecture/InventoryArchitectureTest.java b/src/test/java/io/wkrzywiec/hexagonal/library/architecture/InventoryArchitectureTest.java new file mode 100644 index 0000000..24a5b0c --- /dev/null +++ b/src/test/java/io/wkrzywiec/hexagonal/library/architecture/InventoryArchitectureTest.java @@ -0,0 +1,30 @@ +package io.wkrzywiec.hexagonal.library.architecture; + +import com.tngtech.archunit.core.importer.ImportOption; +import com.tngtech.archunit.junit.AnalyzeClasses; +import com.tngtech.archunit.junit.ArchTest; +import com.tngtech.archunit.lang.ArchRule; +import io.wkrzywiec.hexagonal.library.inventory.InventoryFacade; + +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClass; +import static com.tngtech.archunit.library.Architectures.onionArchitecture; + +@AnalyzeClasses(packages = {"io.wkrzywiec.hexagonal.library.inventory"}, + importOptions = { ImportOption.DoNotIncludeTests.class }) +public class InventoryArchitectureTest { + + @ArchTest + public static final ArchRule hexagonalArchInInventoryDomain = onionArchitecture() + .domainModels("io.wkrzywiec.hexagonal.library.inventory.model..") + .domainServices("io.wkrzywiec.hexagonal.library.inventory..") + .applicationServices("io.wkrzywiec.hexagonal.library.inventory.application..") + .adapter("infrastructure", "io.wkrzywiec.hexagonal.library.inventory.infrastructure.."); + + @ArchTest + public static final ArchRule noSpringDependenciesInInventoryFacade = + noClass(InventoryFacade.class) + .should() + .dependOnClassesThat() + .resideInAPackage("org.springframework.."); + +} diff --git a/src/test/java/io/wkrzywiec/hexagonal/library/inventory/InMemoryInventoryDatabase.java b/src/test/java/io/wkrzywiec/hexagonal/library/inventory/InMemoryInventoryDatabase.java index da0da4f..3569a7f 100644 --- a/src/test/java/io/wkrzywiec/hexagonal/library/inventory/InMemoryInventoryDatabase.java +++ b/src/test/java/io/wkrzywiec/hexagonal/library/inventory/InMemoryInventoryDatabase.java @@ -2,6 +2,7 @@ package io.wkrzywiec.hexagonal.library.inventory; import io.wkrzywiec.hexagonal.library.inventory.model.Book; import io.wkrzywiec.hexagonal.library.inventory.ports.outgoing.InventoryDatabase; +import org.apache.commons.lang3.reflect.FieldUtils; import java.util.concurrent.ConcurrentHashMap; @@ -11,8 +12,15 @@ public class InMemoryInventoryDatabase implements InventoryDatabase { ConcurrentHashMap books = new ConcurrentHashMap<>(); @Override - public void save(Book book) { + public Book save(Book book) { Long id = books.size() + 1L; + + try { + FieldUtils.writeField(book, "id", id, true); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } books.put(id, book); + return book; } } diff --git a/src/test/java/io/wkrzywiec/hexagonal/library/inventory/InvenotryEventPublisherFake.java b/src/test/java/io/wkrzywiec/hexagonal/library/inventory/InvenotryEventPublisherFake.java new file mode 100644 index 0000000..fc215b1 --- /dev/null +++ b/src/test/java/io/wkrzywiec/hexagonal/library/inventory/InvenotryEventPublisherFake.java @@ -0,0 +1,10 @@ +package io.wkrzywiec.hexagonal.library.inventory; + +import io.wkrzywiec.hexagonal.library.inventory.model.NewBookWasAddedEvent; +import io.wkrzywiec.hexagonal.library.inventory.ports.outgoing.InventoryEventPublisher; + +public class InvenotryEventPublisherFake implements InventoryEventPublisher { + + @Override + public void publishNewBookWasAddedEvent(NewBookWasAddedEvent event) { } +} diff --git a/src/test/java/io/wkrzywiec/hexagonal/library/inventory/InventoryFacadeTest.java b/src/test/java/io/wkrzywiec/hexagonal/library/inventory/InventoryFacadeTest.java index c32798e..46838dc 100644 --- a/src/test/java/io/wkrzywiec/hexagonal/library/inventory/InventoryFacadeTest.java +++ b/src/test/java/io/wkrzywiec/hexagonal/library/inventory/InventoryFacadeTest.java @@ -4,30 +4,33 @@ import io.wkrzywiec.hexagonal.library.TestData; import io.wkrzywiec.hexagonal.library.inventory.model.AddNewBookCommand; import io.wkrzywiec.hexagonal.library.inventory.model.Book; import io.wkrzywiec.hexagonal.library.inventory.ports.outgoing.GetBookDetails; +import io.wkrzywiec.hexagonal.library.inventory.ports.outgoing.InventoryEventPublisher; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + public class InventoryFacadeTest { private GetBookDetails getBookDetails; private InMemoryInventoryDatabase database; + private InventoryEventPublisher eventPublisher; private InventoryFacade facade; @BeforeEach public void init() { database = new InMemoryInventoryDatabase(); getBookDetails = new GetBookDetailsFake(); - facade = new InventoryFacade(database, getBookDetails); + eventPublisher = new InvenotryEventPublisherFake(); + facade = new InventoryFacade(database, getBookDetails, eventPublisher); } @Test @DisplayName("Correctly save a new book in a repository") public void correctlySaveBook(){ //given - Book expectedBook = TestData.homoDeusBook(); AddNewBookCommand externalBookId = AddNewBookCommand .builder() .googleBookId(TestData.homoDeusBookGoogleId()) @@ -38,6 +41,6 @@ public class InventoryFacadeTest { //then Book actualBook = database.books.get(1L); - assertEquals(expectedBook, actualBook); + assertNotNull(actualBook); } } diff --git a/src/test/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/InventoryDatabaseAdapterITCase.java b/src/test/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/InventoryDatabaseAdapterITCase.java new file mode 100644 index 0000000..17ff4c0 --- /dev/null +++ b/src/test/java/io/wkrzywiec/hexagonal/library/inventory/infrastructure/InventoryDatabaseAdapterITCase.java @@ -0,0 +1,47 @@ +package io.wkrzywiec.hexagonal.library.inventory.infrastructure; + +import io.wkrzywiec.hexagonal.library.TestData; +import io.wkrzywiec.hexagonal.library.inventory.model.Book; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.jdbc.core.JdbcTemplate; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@DataJpaTest +public class InventoryDatabaseAdapterITCase { + + @Autowired + private BookRepository bookRepository; + + @Autowired + private JdbcTemplate jdbcTemplate; + + private InventoryDatabaseAdapter database; + + @BeforeEach + public void init() { + database = new InventoryDatabaseAdapter(bookRepository); + } + + @Test + @DisplayName("Save new book in database") + public void givenBook_whenSaveIt_thenBookIsSaved() { + //given + Book homoDeusBook = TestData.homoDeusBook(); + + //when + Book savedBook = database.save(homoDeusBook); + + //then + Long savedBookId = jdbcTemplate.queryForObject( + "SELECT id FROM book WHERE id = ?", + Long.class, + savedBook.getIdAsLong()); + + assertEquals(savedBook.getIdAsLong(), savedBookId); + } +}