Compare commits
20 Commits
borrow
...
spock-test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6dc153309b | ||
|
|
3218fb056e | ||
|
|
8306b5158a | ||
|
|
967a349b4c | ||
|
|
42b08c8995 | ||
|
|
58a9a0f95b | ||
|
|
9b0c37ffd7 | ||
|
|
53939113f2 | ||
|
|
a9fd37ec5a | ||
|
|
18d1038a18 | ||
|
|
f071d557ff | ||
|
|
b7db201e7b | ||
|
|
36316de3f7 | ||
|
|
428d281e38 | ||
|
|
62960ef444 | ||
|
|
19ce80db76 | ||
|
|
8a1e7ae20e | ||
|
|
d2b7d08072 | ||
|
|
2e5858d8a9 | ||
|
|
5e221dd427 |
@@ -8,4 +8,4 @@ RUN mvn -B -f pom.xml clean package -DskipTests
|
||||
FROM openjdk:11-jdk-slim
|
||||
COPY --from=build /workspace/target/*.jar app.jar
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["java","-jar","app.jar"]
|
||||
ENTRYPOINT ["java","-jar","app.jar", "--spring.profiles.active=prod"]
|
||||
17
README.md
17
README.md
@@ -19,14 +19,23 @@ In the terminal run the following command:
|
||||
$ docker-compose up
|
||||
```
|
||||
|
||||
#### Using Maven
|
||||
#### Using Maven (with H2 or local Postgres database)
|
||||
|
||||
First make sure that you adjust the configuration file - `src/main/resources/application.yml` with connection details to your database.
|
||||
First compile an application:
|
||||
|
||||
Then, in the terminal run the following command:
|
||||
```console
|
||||
$ mvn clean package
|
||||
$ mvn spring-boot:run
|
||||
```
|
||||
|
||||
Then, you have two options either run it with H2 database or with local Postgres database. For first approach just run:
|
||||
|
||||
```console
|
||||
$ mvn spring-boot:run
|
||||
```
|
||||
|
||||
For a second option, check in the configuration file - `src/main/resources/application.yml` for profile *local-postgres* if connection details are correct and if so, run the command:
|
||||
```console
|
||||
$ mvn spring-boot:run -P local-postgres
|
||||
```
|
||||
|
||||
#### Inside IntelliJ (with H2 or Postgres database)
|
||||
|
||||
82
pom.xml
82
pom.xml
@@ -33,6 +33,10 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
@@ -87,27 +91,48 @@
|
||||
<version>1.2.32</version>
|
||||
</dependency>
|
||||
|
||||
<!-- TEST dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-core</artifactId>
|
||||
<version>1.3-groovy-2.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-spring</artifactId>
|
||||
<version>1.3-groovy-2.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.athaydes</groupId>
|
||||
<artifactId>spock-reports</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.tngtech.archunit</groupId>
|
||||
<artifactId>archunit-junit5</artifactId>
|
||||
<version>0.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -140,8 +165,25 @@
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<reportsDirectory>${surefire.and.failsafe.report.dir}</reportsDirectory>
|
||||
<includes>
|
||||
<include>**/*Spec.java</include>
|
||||
<include>**/*Test.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.gmavenplus</groupId>
|
||||
<artifactId>gmavenplus-plugin</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>addTestSources</goal>
|
||||
<goal>compileTests</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
@@ -186,6 +228,33 @@
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>default</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<properties>
|
||||
<active-profiles>default</active-profiles>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>prod</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
</activation>
|
||||
<properties>
|
||||
<active-profiles>prod</active-profiles>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>local-postgres</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
</activation>
|
||||
<properties>
|
||||
<active-profiles>local-postgres</active-profiles>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>component-test</id>
|
||||
<build>
|
||||
@@ -232,6 +301,7 @@
|
||||
<sonar.sources>.</sonar.sources>
|
||||
<sonar.inclusions>src/main/java/**,src/main/resources/**</sonar.inclusions>
|
||||
<sonar.exclusions>${code.coverage.exclusions}</sonar.exclusions>
|
||||
<sonat.tests>src/test/groovy,src/test/java</sonat.tests>
|
||||
<sonar.projectKey>wkrzywiec_library-hexagonal</sonar.projectKey>
|
||||
<sonar.organization>wkrzywiec</sonar.organization>
|
||||
<sonar.coverage.jacoco.xmlReportPaths>target/site/jacoco/jacoco.xml,target/site/jacoco-it/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.wkrzywiec.hexagonal.library.DatabaseHelper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
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;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public abstract class BaseComponentTest {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
protected String baseURL;
|
||||
|
||||
@Autowired
|
||||
protected JdbcTemplate jdbcTemplate;
|
||||
protected DatabaseHelper databaseHelper;
|
||||
|
||||
@BeforeEach
|
||||
public void init() {
|
||||
this.baseURL = "http://localhost:" + port;
|
||||
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
|
||||
databaseHelper = new DatabaseHelper(jdbcTemplate);
|
||||
}
|
||||
}
|
||||
@@ -1,83 +1,44 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.wkrzywiec.hexagonal.library.BookTestData;
|
||||
import io.wkrzywiec.hexagonal.library.UserTestData;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BookReservationCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.BaseComponentTest;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.application.model.BookStatus;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.application.model.ChangeBookStatusRequest;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowBookCommand;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
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 org.springframework.test.context.jdbc.Sql;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public class BorrowBookComponentTest {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
private String baseURL;
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@BeforeEach
|
||||
public void init() {
|
||||
this.baseURL = "http://localhost:" + port;
|
||||
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
|
||||
}
|
||||
public class BorrowBookComponentTest extends BaseComponentTest {
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
@DisplayName("Borrow reserved book")
|
||||
@Sql({"/book-and-user.sql", "/available-book.sql"})
|
||||
@Sql({"/book-and-user.sql", "/reserved-book.sql"})
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void givenBookIsReserved_thenBorrowIt_thenBookIsBorrowed() {
|
||||
//given
|
||||
Long homoDeusBookId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM book WHERE title = ?",
|
||||
Long.class,
|
||||
BookTestData.homoDeusBookTitle());
|
||||
Long homoDeusBookId = databaseHelper.getHomoDeusBookId();
|
||||
Long activeUserId = databaseHelper.getJohnDoeUserId();
|
||||
|
||||
Long activeUserId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM user WHERE email = ?",
|
||||
Long.class,
|
||||
UserTestData.johnDoeEmail());
|
||||
|
||||
jdbcTemplate.update(
|
||||
"INSERT INTO public.reserved (book_id, user_id) VALUES (?, ?)",
|
||||
homoDeusBookId,
|
||||
activeUserId);
|
||||
|
||||
BorrowBookCommand borrowBookCommand =
|
||||
BorrowBookCommand.builder()
|
||||
.bookId(homoDeusBookId )
|
||||
.userId(activeUserId)
|
||||
.build();
|
||||
ChangeBookStatusRequest borrowRequest =
|
||||
ChangeBookStatusRequest.builder()
|
||||
.userId(activeUserId)
|
||||
.status(BookStatus.BORROWED)
|
||||
.build();
|
||||
|
||||
//when
|
||||
given()
|
||||
.contentType("application/json")
|
||||
.body(borrowBookCommand)
|
||||
.body(borrowRequest)
|
||||
.when()
|
||||
.post( baseURL + "/borrow")
|
||||
.patch( baseURL + "/books/" + homoDeusBookId + "/status")
|
||||
.prettyPeek()
|
||||
.then();
|
||||
|
||||
Long reservationId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM borrowed WHERE book_id = ?",
|
||||
Long.class,
|
||||
homoDeusBookId);
|
||||
|
||||
assertTrue(reservationId > 0);
|
||||
Long borrowId = databaseHelper.getPrimaryKeyOfBorrowedByBookId(homoDeusBookId);
|
||||
assertTrue(borrowId > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.domain.BaseComponentTest;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.application.model.BookStatus;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.application.model.ChangeBookStatusRequest;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.GiveBackBookCommand;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;
|
||||
|
||||
public class GiveBackBookComponentTest extends BaseComponentTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Give back borrowed book")
|
||||
@Sql({"/book-and-user.sql", "/borrowed-book.sql"})
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void givenBookIsBorrowed_thenGiveBackIt_thenItIsAvailable() {
|
||||
//given
|
||||
Long homoDeusBookId = databaseHelper.getHomoDeusBookId();
|
||||
Long activeUserId = databaseHelper.getJohnDoeUserId();
|
||||
|
||||
ChangeBookStatusRequest giveBackRequest =
|
||||
ChangeBookStatusRequest.builder()
|
||||
.userId(activeUserId)
|
||||
.status(BookStatus.AVAILABLE)
|
||||
.build();
|
||||
|
||||
//when
|
||||
given()
|
||||
.contentType("application/json")
|
||||
.body(giveBackRequest)
|
||||
.when()
|
||||
.patch( baseURL + "/books/" + homoDeusBookId + "/status")
|
||||
.prettyPeek()
|
||||
.then();
|
||||
|
||||
Long bookId = databaseHelper.getPrimaryKeyOfAvailableByBookBy(homoDeusBookId);
|
||||
|
||||
assertEquals(homoDeusBookId, bookId);
|
||||
}
|
||||
}
|
||||
@@ -1,79 +1,49 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.wkrzywiec.hexagonal.library.BookTestData;
|
||||
import io.wkrzywiec.hexagonal.library.UserTestData;
|
||||
import io.wkrzywiec.hexagonal.library.domain.BaseComponentTest;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.application.model.BookStatus;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.application.model.ChangeBookStatusRequest;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BookReservationCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.inventory.infrastructure.BookRepository;
|
||||
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 org.springframework.test.context.jdbc.Sql;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public class MakeReservationComponentTest {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
public class MakeReservationComponentTest extends BaseComponentTest {
|
||||
|
||||
@Autowired
|
||||
private BookRepository bookRepository;
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
private String baseURL;
|
||||
|
||||
@BeforeEach
|
||||
public void init() {
|
||||
this.baseURL = "http://localhost:" + port;
|
||||
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Reserve available book")
|
||||
@Sql({"/book-and-user.sql", "/available-book.sql"})
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void givenBookIsAvailable_thenMakeReservation_thenBookIsReserved() {
|
||||
//given
|
||||
Long homoDeusBookId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM book WHERE title = ?",
|
||||
Long.class,
|
||||
BookTestData.homoDeusBookTitle());
|
||||
Long homoDeusBookId = databaseHelper.getHomoDeusBookId();
|
||||
Long activeUserId = databaseHelper.getJohnDoeUserId();
|
||||
|
||||
Long activeUserId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM user WHERE email = ?",
|
||||
Long.class,
|
||||
UserTestData.johnDoeEmail());
|
||||
|
||||
BookReservationCommand reservationCommand =
|
||||
BookReservationCommand.builder()
|
||||
.bookId(homoDeusBookId )
|
||||
.userId(activeUserId)
|
||||
.build();
|
||||
ChangeBookStatusRequest reservationRequest =
|
||||
ChangeBookStatusRequest.builder()
|
||||
.userId(activeUserId)
|
||||
.status(BookStatus.RESERVED)
|
||||
.build();
|
||||
|
||||
//when
|
||||
given()
|
||||
.contentType("application/json")
|
||||
.body(reservationCommand)
|
||||
.body(reservationRequest)
|
||||
.when()
|
||||
.post( baseURL + "/reservations")
|
||||
.patch( baseURL + "/books/" + homoDeusBookId + "/status")
|
||||
.prettyPeek()
|
||||
.then();
|
||||
|
||||
Long reservationId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM reserved WHERE book_id = ?",
|
||||
Long.class,
|
||||
homoDeusBookId);
|
||||
|
||||
Long reservationId = databaseHelper.getPrimaryKeyOfReservationByBookId(homoDeusBookId);
|
||||
assertTrue(reservationId > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.inventory;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.response.ValidatableResponse;
|
||||
import io.wkrzywiec.hexagonal.library.BookTestData;
|
||||
import io.wkrzywiec.hexagonal.library.domain.BaseComponentTest;
|
||||
import io.wkrzywiec.hexagonal.library.domain.inventory.core.model.AddNewBookCommand;
|
||||
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.http.HttpStatus;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
@@ -19,22 +14,7 @@ import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public class AddNewBookComponentTest {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbc;
|
||||
|
||||
private String baseURL;
|
||||
|
||||
@BeforeEach
|
||||
public void init(){
|
||||
this.baseURL = "http://localhost:" + port;
|
||||
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
|
||||
}
|
||||
public class AddNewBookComponentTest extends BaseComponentTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Search for a new book in Google Books")
|
||||
@@ -73,17 +53,10 @@ public class AddNewBookComponentTest {
|
||||
.then();
|
||||
|
||||
//then
|
||||
Long savedBookId = jdbc.queryForObject(
|
||||
"SELECT id FROM book WHERE book_external_id = ?",
|
||||
Long.class,
|
||||
BookTestData.homoDeusBookGoogleId());
|
||||
|
||||
Long savedBookId = databaseHelper.getHomoDeusBookId();
|
||||
assertTrue(savedBookId > 0);
|
||||
|
||||
Long availableBookId = jdbc.queryForObject(
|
||||
"SELECT id FROM available WHERE book_id = ?",
|
||||
Long.class,
|
||||
savedBookId);
|
||||
Long availableBookId = databaseHelper.getPrimaryKeyOfAvailableByBookBy(savedBookId);
|
||||
|
||||
assertTrue(availableBookId > 0);
|
||||
}
|
||||
|
||||
@@ -1,36 +1,16 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.user;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.wkrzywiec.hexagonal.library.domain.BaseComponentTest;
|
||||
import io.wkrzywiec.hexagonal.library.domain.user.core.model.AddUserCommand;
|
||||
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 org.springframework.test.context.jdbc.Sql;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public class AddNewUserComponentTest {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
private String baseURL;
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@BeforeEach
|
||||
public void init(){
|
||||
baseURL = "http://localhost:" + port;
|
||||
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
|
||||
}
|
||||
public class AddNewUserComponentTest extends BaseComponentTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Create new user")
|
||||
@@ -53,11 +33,7 @@ public class AddNewUserComponentTest {
|
||||
.then();
|
||||
|
||||
//then
|
||||
Long savedUserId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM user WHERE email = ?",
|
||||
Long.class,
|
||||
"john.doe@test.com");
|
||||
|
||||
Long savedUserId = databaseHelper.getJohnDoeUserId();
|
||||
assertTrue(savedUserId > 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing.application;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowBookCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.BorrowBook;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
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("/borrow")
|
||||
@RequiredArgsConstructor
|
||||
public class BorrowBookController {
|
||||
|
||||
@Qualifier("BorrowBook")
|
||||
private final BorrowBook borrowBook;
|
||||
|
||||
@PostMapping("")
|
||||
public ResponseEntity<String> borrowBook(@RequestBody BorrowBookCommand borrowBookCommand){
|
||||
borrowBook.handle(borrowBookCommand);
|
||||
return new ResponseEntity<>("Book with an id " + borrowBookCommand.getBookId() + " was borrowed", HttpStatus.CREATED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing.application;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.application.model.ChangeBookStatusRequest;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BookReservationCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowBookCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.GiveBackBookCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.BorrowBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.GiveBackBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.ReserveBook;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PatchMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/books")
|
||||
@RequiredArgsConstructor
|
||||
public class BorrowingDomainController {
|
||||
|
||||
@Qualifier("GiveBackBook")
|
||||
private final GiveBackBook giveBackBook;
|
||||
|
||||
@Qualifier("ReserveBook")
|
||||
private final ReserveBook reserveBook;
|
||||
|
||||
@Qualifier("BorrowBook")
|
||||
private final BorrowBook borrowBook;
|
||||
|
||||
@PatchMapping("/{id}/status")
|
||||
public ResponseEntity<String> borrowBook(@PathVariable("id") Long bookId, @RequestBody ChangeBookStatusRequest request){
|
||||
switch (request.getStatus()){
|
||||
case AVAILABLE:
|
||||
giveBackBook.handle(new GiveBackBookCommand(bookId, request.getUserId()));
|
||||
return new ResponseEntity<>("Book with an id " + bookId + " was returned", HttpStatus.OK);
|
||||
case RESERVED:
|
||||
Long reservationId = reserveBook.handle(new BookReservationCommand(bookId, request.getUserId()));
|
||||
return new ResponseEntity<>("Reservation has been made with an id " + reservationId, HttpStatus.OK);
|
||||
case BORROWED:
|
||||
borrowBook.handle(new BorrowBookCommand(bookId, request.getUserId()));
|
||||
return new ResponseEntity<>("Book with an id " + bookId + " was borrowed", HttpStatus.OK);
|
||||
default:
|
||||
return new ResponseEntity<>("Book can't have status: " + request.getStatus(), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ public class OverdueReservationScheduler {
|
||||
@Qualifier("CancelOverdueReservations")
|
||||
private final CancelOverdueReservations overdueReservations;
|
||||
|
||||
@Scheduled(fixedRate = 10 * 1000)
|
||||
@Scheduled(fixedRate = 60 * 1000)
|
||||
public void checkOverdueReservations(){
|
||||
overdueReservations.cancelOverdueReservations();
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing.application;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BookReservationCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.ReserveBook;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
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 {
|
||||
|
||||
@Qualifier("ReserveBook")
|
||||
private final ReserveBook reserveBook;
|
||||
|
||||
@PostMapping("")
|
||||
public ResponseEntity<String> makeReservation(@RequestBody BookReservationCommand reservationCommand){
|
||||
Long reservationId = reserveBook.handle(reservationCommand);
|
||||
return new ResponseEntity<>("Reservation has been made with an id " + reservationId, HttpStatus.CREATED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing.application.model;
|
||||
|
||||
public enum BookStatus {
|
||||
AVAILABLE, RESERVED, BORROWED
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing.application.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class ChangeBookStatusRequest {
|
||||
|
||||
private BookStatus status;
|
||||
private Long userId;
|
||||
}
|
||||
@@ -6,27 +6,26 @@ import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BookReservatio
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BookReservedEvent;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowBookCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowedBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.DueDate;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.GiveBackBookCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.MakeBookAvailableCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.OverdueReservation;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservationDetails;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservedBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.exception.ActiveUserNotFoundException;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.exception.AvailableBookNotFoundExeption;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.exception.BorrowedBookNotFoundException;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.exception.ReservedBookNotFoundException;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.BorrowBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.CancelOverdueReservations;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.GiveBackBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.MakeBookAvailable;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.ReserveBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingDatabase;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingEventPublisher;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class BorrowingFacade implements MakeBookAvailable, ReserveBook, CancelOverdueReservations, BorrowBook {
|
||||
public class BorrowingFacade implements MakeBookAvailable, ReserveBook, CancelOverdueReservations, BorrowBook, GiveBackBook {
|
||||
|
||||
private final BorrowingDatabase database;
|
||||
private final BorrowingEventPublisher eventPublisher;
|
||||
@@ -38,7 +37,7 @@ public class BorrowingFacade implements MakeBookAvailable, ReserveBook, CancelOv
|
||||
|
||||
@Override
|
||||
public void handle(MakeBookAvailableCommand bookAvailableCommand) {
|
||||
database.setBookAvailable(bookAvailableCommand.getBookId());
|
||||
database.save(new AvailableBook(bookAvailableCommand.getBookId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,10 +58,9 @@ public class BorrowingFacade implements MakeBookAvailable, ReserveBook, CancelOv
|
||||
|
||||
@Override
|
||||
public void cancelOverdueReservations() {
|
||||
DueDate dueDate = new DueDate(Instant.now().plus(3L, ChronoUnit.DAYS));
|
||||
List<OverdueReservation> overdueReservationList = database.findReservationsAfter(dueDate);
|
||||
List<OverdueReservation> overdueReservationList = database.findReservationsForMoreThan(3L);
|
||||
overdueReservationList.forEach(
|
||||
overdue -> database.setBookAvailable(overdue.getBookIdentificationAsLong()));
|
||||
overdueBook -> database.save(new AvailableBook(overdueBook.getBookIdentificationAsLong())));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,4 +75,17 @@ public class BorrowingFacade implements MakeBookAvailable, ReserveBook, CancelOv
|
||||
BorrowedBook borrowedBook = activeUser.borrow(reservedBook);
|
||||
database.save(borrowedBook);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(GiveBackBookCommand command) {
|
||||
BorrowedBook borrowedBook =
|
||||
database.getBorrowedBook(command.getBookId())
|
||||
.orElseThrow(() -> new BorrowedBookNotFoundException(command.getBookId()));
|
||||
ActiveUser activeUser =
|
||||
database.getActiveUser(command.getUserId())
|
||||
.orElseThrow(() -> new ActiveUserNotFoundException(command.getUserId()));
|
||||
|
||||
AvailableBook availableBook = activeUser.giveBack(borrowedBook);
|
||||
database.save(availableBook);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,14 @@ package io.wkrzywiec.hexagonal.library.domain.borrowing.core.model;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.exception.TooManyBooksAssignedToUserException;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
public class ActiveUser {
|
||||
|
||||
private final Long id;
|
||||
@@ -38,6 +42,15 @@ public class ActiveUser {
|
||||
}
|
||||
}
|
||||
|
||||
public AvailableBook giveBack(BorrowedBook borrowedBook) {
|
||||
boolean isBookRemovedFromUserAccount = borrowedBooks.removeIf(book -> book.equals(borrowedBook));
|
||||
if (isBookRemovedFromUserAccount){
|
||||
return new AvailableBook(borrowedBook.getIdAsLong());
|
||||
} else {
|
||||
throw new IllegalArgumentException("User with an id: " + id + " didn't borrow book with an id: " + borrowedBook.getIdAsLong());
|
||||
}
|
||||
}
|
||||
|
||||
public Long getIdAsLong(){
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing.core.model;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
public class AvailableBook implements Book {
|
||||
|
||||
private final Long id;
|
||||
|
||||
@@ -3,8 +3,10 @@ package io.wkrzywiec.hexagonal.library.domain.borrowing.core.model;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Builder
|
||||
public class BookReservationCommand {
|
||||
|
||||
@@ -7,9 +7,10 @@ import java.time.Instant;
|
||||
@EqualsAndHashCode
|
||||
public class BorrowedBook implements Book {
|
||||
|
||||
private final Long bookId;
|
||||
private final Long userId;
|
||||
private final Instant borrowedDate;
|
||||
private Long bookId;
|
||||
private Long userId;
|
||||
@EqualsAndHashCode.Exclude
|
||||
private Instant borrowedDate;
|
||||
|
||||
public BorrowedBook(Long bookId, Long userId) {
|
||||
this.bookId = bookId;
|
||||
@@ -17,6 +18,12 @@ public class BorrowedBook implements Book {
|
||||
this.borrowedDate = Instant.now();
|
||||
}
|
||||
|
||||
public BorrowedBook(Long bookId, Long userId, Instant borrowedDate) {
|
||||
this.bookId = bookId;
|
||||
this.userId = userId;
|
||||
this.borrowedDate = borrowedDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getIdAsLong() {
|
||||
return bookId;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing.core.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Builder
|
||||
public class GiveBackBookCommand {
|
||||
private Long bookId;
|
||||
private Long userId;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing.core.model;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
@@ -17,6 +18,12 @@ public class ReservedBook implements Book {
|
||||
this.reservedDate = Instant.now();
|
||||
}
|
||||
|
||||
public ReservedBook(Long bookId, Long userId, Instant reservedDate) {
|
||||
this.bookId = bookId;
|
||||
this.userId = userId;
|
||||
this.reservedDate = reservedDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getIdAsLong() {
|
||||
return bookId;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.exception;
|
||||
|
||||
public class BorrowedBookNotFoundException extends RuntimeException {
|
||||
public BorrowedBookNotFoundException(Long bookId) {
|
||||
super("There is no borrowed book with an ID: " + bookId,
|
||||
null,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.GiveBackBookCommand;
|
||||
|
||||
public interface GiveBackBook {
|
||||
void handle(GiveBackBookCommand command);
|
||||
}
|
||||
|
||||
@@ -12,11 +12,12 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface BorrowingDatabase {
|
||||
void setBookAvailable(Long bookId);
|
||||
Optional<AvailableBook> getAvailableBook(Long bookId);
|
||||
Optional<ActiveUser> getActiveUser(Long userId);
|
||||
void save(AvailableBook availableBook);
|
||||
ReservationDetails save(ReservedBook reservedBook);
|
||||
void save(BorrowedBook borrowedBook);
|
||||
List<OverdueReservation> findReservationsAfter(DueDate dueDate);
|
||||
Optional<AvailableBook> getAvailableBook(Long bookId);
|
||||
Optional<ActiveUser> getActiveUser(Long userId);
|
||||
List<OverdueReservation> findReservationsForMoreThan(Long days);
|
||||
Optional<ReservedBook> getReservedBook(Long bookId);
|
||||
Optional<BorrowedBook> getBorrowedBook(Long bookId);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservationDet
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservationId;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservedBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingDatabase;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.infrastructure.mapper.BorrowedBookRowMapper;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.infrastructure.mapper.ReservedBookRowMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.BeanPropertyRowMapper;
|
||||
@@ -27,18 +29,18 @@ public class BorrowingDatabaseAdapter implements BorrowingDatabase {
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public void setBookAvailable(Long bookId) {
|
||||
public void save(AvailableBook availableBook) {
|
||||
jdbcTemplate.update(
|
||||
"INSERT INTO available (book_id) VALUES (?)",
|
||||
bookId);
|
||||
availableBook.getIdAsLong());
|
||||
|
||||
jdbcTemplate.update(
|
||||
"DELETE FROM reserved WHERE book_id = ?",
|
||||
bookId);
|
||||
availableBook.getIdAsLong());
|
||||
|
||||
jdbcTemplate.update(
|
||||
"DELETE FROM borrowed WHERE book_id = ?",
|
||||
bookId);
|
||||
availableBook.getIdAsLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,7 +60,7 @@ public class BorrowingDatabaseAdapter implements BorrowingDatabase {
|
||||
public Optional<ActiveUser> getActiveUser(Long userId) {
|
||||
try {
|
||||
jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM public.user as u WHERE u.id = ?",
|
||||
"SELECT id FROM public.library_user as u WHERE u.id = ?",
|
||||
Long.class,
|
||||
userId);
|
||||
} catch (DataAccessException exception) {
|
||||
@@ -76,7 +78,7 @@ public class BorrowingDatabaseAdapter implements BorrowingDatabase {
|
||||
"INSERT INTO reserved (book_id, user_id, reserved_date) VALUES (?, ?, ?)",
|
||||
reservedBook.getIdAsLong(),
|
||||
reservedBook.getAssignedUserIdAsLong(),
|
||||
reservedBook.getReservedDateAsInstant());
|
||||
Timestamp.from(reservedBook.getReservedDateAsInstant()));
|
||||
|
||||
jdbcTemplate.update(
|
||||
"DELETE FROM available WHERE book_id = ?",
|
||||
@@ -95,7 +97,7 @@ public class BorrowingDatabaseAdapter implements BorrowingDatabase {
|
||||
"INSERT INTO borrowed (book_id, user_id, borrowed_date) VALUES (?, ?, ?)",
|
||||
borrowedBook.getIdAsLong(),
|
||||
borrowedBook.getAssignedUserIdAsLong(),
|
||||
borrowedBook.getBorrowedDateAsInstant());
|
||||
Timestamp.from(borrowedBook.getBorrowedDateAsInstant()));
|
||||
|
||||
jdbcTemplate.update(
|
||||
"DELETE FROM reserved WHERE book_id = ?",
|
||||
@@ -108,11 +110,11 @@ public class BorrowingDatabaseAdapter implements BorrowingDatabase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OverdueReservation> findReservationsAfter(DueDate dueDate) {
|
||||
public List<OverdueReservation> findReservationsForMoreThan(Long days) {
|
||||
List<OverdueReservationEntity> entities = jdbcTemplate.query(
|
||||
"SELECT id AS reservationId, book_id AS bookIdentification FROM reserved WHERE reserved_date > ?",
|
||||
"SELECT id AS reservationId, book_id AS bookIdentification FROM reserved WHERE DATEADD(day, ?, reserved_date) > NOW()",
|
||||
new BeanPropertyRowMapper<OverdueReservationEntity>(OverdueReservationEntity.class),
|
||||
Timestamp.from(dueDate.asInstant()));
|
||||
days);
|
||||
return entities.stream()
|
||||
.map(entity -> new OverdueReservation(entity.getReservationId(), entity.getBookIdentification()))
|
||||
.collect(Collectors.toList());
|
||||
@@ -123,8 +125,21 @@ public class BorrowingDatabaseAdapter implements BorrowingDatabase {
|
||||
try {
|
||||
return Optional.ofNullable(
|
||||
jdbcTemplate.queryForObject(
|
||||
"SELECT book_id AS bookId, user_id AS userId, reserved_date AS reservedDate FROM reserved WHERE reserved.book_id = ?",
|
||||
ReservedBook.class,
|
||||
"SELECT book_id, user_id, reserved_date FROM reserved WHERE book_id = ?",
|
||||
new ReservedBookRowMapper(),
|
||||
bookId));
|
||||
} catch (DataAccessException exception) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<BorrowedBook> getBorrowedBook(Long bookId) {
|
||||
try {
|
||||
return Optional.ofNullable(
|
||||
jdbcTemplate.queryForObject(
|
||||
"SELECT book_id, user_id, borrowed_date FROM borrowed WHERE book_id = ?",
|
||||
new BorrowedBookRowMapper(),
|
||||
bookId));
|
||||
} catch (DataAccessException exception) {
|
||||
return Optional.empty();
|
||||
@@ -133,9 +148,9 @@ public class BorrowingDatabaseAdapter implements BorrowingDatabase {
|
||||
|
||||
private List<ReservedBook> getReservedBooksByUser(Long userId) {
|
||||
try {
|
||||
return jdbcTemplate.queryForList(
|
||||
"SELECT book_id FROM reserved WHERE reserved.user_id = ?",
|
||||
ReservedBook.class,
|
||||
return jdbcTemplate.query(
|
||||
"SELECT book_id, user_id, reserved_date FROM reserved WHERE user_id = ?",
|
||||
new ReservedBookRowMapper(),
|
||||
userId
|
||||
);
|
||||
} catch (DataAccessException exception){
|
||||
@@ -144,6 +159,14 @@ public class BorrowingDatabaseAdapter implements BorrowingDatabase {
|
||||
}
|
||||
|
||||
private List<BorrowedBook> getBorrowedBooksByUser(Long userId) {
|
||||
return new ArrayList<>();
|
||||
try {
|
||||
return jdbcTemplate.query(
|
||||
"SELECT book_id, user_id, borrowed_date FROM borrowed WHERE user_id = ?",
|
||||
new BorrowedBookRowMapper(),
|
||||
userId
|
||||
);
|
||||
} catch (DataAccessException exception){
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing.infrastructure.mapper;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowedBook;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
|
||||
public class BorrowedBookRowMapper implements RowMapper<BorrowedBook> {
|
||||
|
||||
@Override
|
||||
public BorrowedBook mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
return new BorrowedBook(
|
||||
rs.getLong("book_id"),
|
||||
rs.getLong("user_id"),
|
||||
rs.getTimestamp("borrowed_date").toInstant()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing.infrastructure.mapper;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservedBook;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class ReservedBookRowMapper implements RowMapper<ReservedBook> {
|
||||
|
||||
@Override
|
||||
public ReservedBook mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
return new ReservedBook(
|
||||
rs.getLong("book_id"),
|
||||
rs.getLong("user_id"),
|
||||
rs.getTimestamp("reserved_date").toInstant()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public class EmailDatabaseAdapter implements EmailDatabase {
|
||||
public Optional<String> getUserEmailAddress(Long userId) {
|
||||
try {
|
||||
return Optional.ofNullable(jdbcTemplate.queryForObject(
|
||||
"SELECT email FROM user WHERE id = ?",
|
||||
"SELECT email FROM library_user WHERE id = ?",
|
||||
String.class,
|
||||
userId));
|
||||
} catch (DataAccessException ex){
|
||||
|
||||
@@ -11,7 +11,7 @@ import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name="user")
|
||||
@Table(name="library_user")
|
||||
@EqualsAndHashCode
|
||||
public class User {
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.wkrzywiec.hexagonal.library.infrastructure;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.BorrowingFacade;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.BorrowBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.CancelOverdueReservations;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.GiveBackBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.MakeBookAvailable;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.ReserveBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingDatabase;
|
||||
@@ -12,7 +13,6 @@ import io.wkrzywiec.hexagonal.library.domain.borrowing.infrastructure.SpringBorr
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
public class BorrowingDomainConfig {
|
||||
@@ -45,6 +45,12 @@ public class BorrowingDomainConfig {
|
||||
return new BorrowingFacade(database, borrowingEventPublisher);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Qualifier("GiveBackBook")
|
||||
public GiveBackBook giveBackBook(BorrowingDatabase database, BorrowingEventPublisher borrowingEventPublisher){
|
||||
return new BorrowingFacade(database, borrowingEventPublisher);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Qualifier("CancelOverdueReservations")
|
||||
public CancelOverdueReservations cancelOverdueReservations(BorrowingDatabase database, BorrowingEventPublisher borrowingEventPublisher){
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
spring:
|
||||
application:
|
||||
name: library
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb
|
||||
driverClassName: org.h2.Driver
|
||||
username: sa
|
||||
password: password
|
||||
jpa.database-platform: org.hibernate.dialect.H2Dialect
|
||||
@@ -1,9 +0,0 @@
|
||||
spring:
|
||||
application:
|
||||
name: library
|
||||
datasource:
|
||||
url: jdbc:postgresql://localhost:5432/library
|
||||
driverClassName: org.postgresql.Driver
|
||||
username: library
|
||||
password: library
|
||||
jpa.database-platform: org.hibernate.dialect.PostgreSQL9Dialect
|
||||
@@ -1,6 +1,39 @@
|
||||
spring:
|
||||
application:
|
||||
name: library
|
||||
profiles:
|
||||
active: @active-profiles@
|
||||
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: "*"
|
||||
|
||||
---
|
||||
spring:
|
||||
profiles: default
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb
|
||||
driverClassName: org.h2.Driver
|
||||
username: sa
|
||||
password: password
|
||||
jpa.database-platform: org.hibernate.dialect.H2Dialect
|
||||
|
||||
---
|
||||
spring:
|
||||
profiles: local-postgres
|
||||
datasource:
|
||||
url: jdbc:postgresql://localhost:5432/library
|
||||
driverClassName: org.postgresql.Driver
|
||||
username: library
|
||||
password: library
|
||||
jpa.database-platform: org.hibernate.dialect.PostgreSQL9Dialect
|
||||
|
||||
---
|
||||
spring:
|
||||
profiles: prod
|
||||
datasource:
|
||||
url: jdbc:postgresql://${POSTGRES_SERVER}:5432/${POSTGRES_DB}
|
||||
driverClassName: org.postgresql.Driver
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CREATE TABLE IF NOT EXISTS public.user (
|
||||
CREATE TABLE IF NOT EXISTS library_user (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
first_name CHARACTER VARYING(255) NOT NULL,
|
||||
last_name CHARACTER VARYING(255) NOT NULL,
|
||||
|
||||
@@ -7,12 +7,12 @@ CREATE TABLE IF NOT EXISTS public.reserved (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
reserved_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
book_id BIGINT NOT NULL REFERENCES public.book,
|
||||
user_id BIGINT NOT NULL REFERENCES public.user
|
||||
user_id BIGINT NOT NULL REFERENCES public.library_user
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.borrowed (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
borrowed_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
book_id BIGINT NOT NULL REFERENCES public.book,
|
||||
user_id BIGINT NOT NULL REFERENCES public.user
|
||||
user_id BIGINT NOT NULL REFERENCES public.library_user
|
||||
);
|
||||
@@ -0,0 +1,42 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.BorrowingFacade
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.AvailableBook
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.MakeBookAvailableCommand
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingEventPublisher
|
||||
import spock.lang.Narrative
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Subject
|
||||
import spock.lang.Title
|
||||
|
||||
@Title("Unit tests for borrowing book logic")
|
||||
@Narrative("""
|
||||
BorrowingFacade class encapsulate a logic of
|
||||
managing books in a library. It contains methods
|
||||
responsible for registering new book, borrowing it,
|
||||
reserving it and taking it back.
|
||||
""")
|
||||
@Subject(BorrowingFacade)
|
||||
class BorrowingFacadeSpec extends Specification {
|
||||
|
||||
private BorrowingFacade facade
|
||||
private InMemoryBorrowingDatabase database
|
||||
private BorrowingEventPublisher eventPublisher
|
||||
|
||||
def setup() {
|
||||
database = new InMemoryBorrowingDatabase()
|
||||
eventPublisher = new BorrowingEventPublisherFake()
|
||||
facade = new BorrowingFacade(database, eventPublisher)
|
||||
}
|
||||
|
||||
def "Make a book available"() {
|
||||
given: "prepare a command"
|
||||
def makeBookAvailableCommand = new MakeBookAvailableCommand(100)
|
||||
|
||||
when: "receive MakeBookAvailableCommand"
|
||||
facade.handle(makeBookAvailableCommand)
|
||||
|
||||
then: "check database to have this book as available"
|
||||
database.availableBooks[100L] == new AvailableBook(100)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package io.wkrzywiec.hexagonal.library;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class DatabaseHelper {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
public Long getHomoDeusBookId(){
|
||||
return jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM book WHERE title = ?",
|
||||
Long.class,
|
||||
BookTestData.homoDeusBookTitle());
|
||||
}
|
||||
|
||||
public Long getJohnDoeUserId(){
|
||||
return jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM library_user WHERE email = ?",
|
||||
Long.class,
|
||||
UserTestData.johnDoeEmail());
|
||||
}
|
||||
|
||||
public Long getPrimaryKeyOfAvailableByBookBy(Long bookId){
|
||||
return jdbcTemplate.queryForObject(
|
||||
"SELECT book_id FROM available WHERE book_id = ?",
|
||||
Long.class,
|
||||
bookId);
|
||||
}
|
||||
|
||||
public Long getPrimaryKeyOfReservationByBookId(Long bookId){
|
||||
return jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM reserved WHERE book_id = ?",
|
||||
Long.class,
|
||||
bookId);
|
||||
}
|
||||
|
||||
public Long getPrimaryKeyOfBorrowedByBookId(Long bookId){
|
||||
return jdbcTemplate.queryForObject(
|
||||
"SELECT book_id FROM borrowed WHERE book_id = ?",
|
||||
Long.class,
|
||||
bookId);
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,11 @@ package io.wkrzywiec.hexagonal.library.domain.borrowing;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ActiveUser;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowBookCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowedBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.GiveBackBookCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservedBook;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BorrowTestData {
|
||||
|
||||
@@ -16,11 +18,31 @@ public class BorrowTestData {
|
||||
.build();
|
||||
}
|
||||
|
||||
public static GiveBackBookCommand anyGiveBookCommand(Long bookId, Long userId){
|
||||
return GiveBackBookCommand.builder()
|
||||
.bookId(bookId)
|
||||
.userId(userId)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static ReservedBook anyReservedBook(Long bookId, Long userId){
|
||||
return new ReservedBook(bookId, userId);
|
||||
}
|
||||
|
||||
public static BorrowedBook anyBorrowedBook(Long bookId, Long userId){
|
||||
return new BorrowedBook(bookId, userId);
|
||||
}
|
||||
|
||||
public static ActiveUser anyActiveUser(Long userId){
|
||||
return new ActiveUser(userId, new ArrayList<ReservedBook>(), new ArrayList<BorrowedBook>());
|
||||
}
|
||||
|
||||
public static ActiveUser anyActiveUserWithReservedBooks(Long userId, List<ReservedBook> reservedBookList){
|
||||
return new ActiveUser(userId, reservedBookList, new ArrayList<BorrowedBook>());
|
||||
}
|
||||
|
||||
public static ActiveUser anyActiveUserWithBorrowedBooks(Long userId, List<BorrowedBook> borrowedBooksList){
|
||||
return new ActiveUser(userId, new ArrayList<ReservedBook>(), borrowedBooksList);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BookReservedEvent;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingEventPublisher;
|
||||
|
||||
public class BorrowingEventPublisherFake implements BorrowingEventPublisher {
|
||||
|
||||
@Override
|
||||
public void publish(BookReservedEvent event) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,19 @@ import io.wkrzywiec.hexagonal.library.domain.borrowing.core.BorrowingFacade;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ActiveUser;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.AvailableBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BookReservationCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BookReservedEvent;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowBookCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowedBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.GiveBackBookCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.MakeBookAvailableCommand;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.OverdueReservation;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservationDetails;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservationId;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservedBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.exception.ActiveUserNotFoundException;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.exception.AvailableBookNotFoundExeption;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.exception.TooManyBooksAssignedToUserException;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingDatabase;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingEventPublisher;
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -18,12 +25,19 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class BorrowingFacadeTest {
|
||||
class BorrowingFacadeTest {
|
||||
|
||||
private BorrowingFacade facade;
|
||||
private InMemoryBorrowingDatabase database;
|
||||
@@ -138,7 +152,7 @@ public class BorrowingFacadeTest {
|
||||
public void givenBookIsReserved_when3daysPass_thenBookIsAvailable(){
|
||||
//given
|
||||
ReservedBook reservedBook = ReservationTestData.anyReservedBook(100L, 100L);
|
||||
changeReservationTimeFor(reservedBook, 4L);
|
||||
changeReservationTimeFor(reservedBook, Instant.now().minus(4, ChronoUnit.DAYS));
|
||||
database.reservedBooks.put(100L, reservedBook);
|
||||
|
||||
//when
|
||||
@@ -153,7 +167,7 @@ public class BorrowingFacadeTest {
|
||||
public void givenBookIsReserved_when2daysPass_thenBookIsStillReserved(){
|
||||
//given
|
||||
ReservedBook reservedBook = ReservationTestData.anyReservedBook(100L, 100L);
|
||||
changeReservationTimeFor(reservedBook, 2L);
|
||||
changeReservationTimeFor(reservedBook, Instant.now().minus(2, ChronoUnit.DAYS));
|
||||
database.reservedBooks.put(100L, reservedBook);
|
||||
|
||||
//when
|
||||
@@ -163,14 +177,6 @@ public class BorrowingFacadeTest {
|
||||
assertEquals(1, database.reservedBooks.size());
|
||||
}
|
||||
|
||||
private void changeReservationTimeFor(ReservedBook reservedBook, Long daysFromNow) {
|
||||
try {
|
||||
FieldUtils.writeField(reservedBook, "reservedDate", Instant.now().plus(daysFromNow, ChronoUnit.DAYS), true);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Successfully borrow a book")
|
||||
public void givenReservedBookAndActiveUser_whenBorrowing_thenBookIsBorrowed(){
|
||||
@@ -188,4 +194,109 @@ public class BorrowingFacadeTest {
|
||||
//then
|
||||
assertEquals(1, activeUser.getBorrowedBookList().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Successful give back a book")
|
||||
public void givenUserWithBorrowedBook_whenBookIsReturned_thenBookIsAvailable(){
|
||||
//given
|
||||
GiveBackBookCommand giveBackBookCommand = BorrowTestData.anyGiveBookCommand(100L, 100L);
|
||||
BorrowedBook borrowedBook = BorrowTestData.anyBorrowedBook(giveBackBookCommand.getBookId(), giveBackBookCommand.getUserId());
|
||||
ActiveUser activeUser = BorrowTestData.anyActiveUserWithBorrowedBooks(giveBackBookCommand.getUserId(), new ArrayList<BorrowedBook>(Arrays.asList(borrowedBook)));
|
||||
|
||||
database.borrowedBooks.put(borrowedBook.getIdAsLong(), borrowedBook);
|
||||
database.activeUsers.put(activeUser.getIdAsLong(), activeUser);
|
||||
|
||||
//when
|
||||
facade.handle(giveBackBookCommand);
|
||||
|
||||
//then
|
||||
assertEquals(0, database.borrowedBooks.size());
|
||||
assertEquals(1, database.availableBooks.size());
|
||||
assertEquals(0, database.activeUsers.get(activeUser.getIdAsLong()).getBorrowedBookList().size());
|
||||
}
|
||||
|
||||
private void changeReservationTimeFor(ReservedBook reservedBook, Instant reservationDate) {
|
||||
try {
|
||||
FieldUtils.writeField(reservedBook, "reservedDate", reservationDate, true);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InMemoryBorrowingDatabase implements BorrowingDatabase {
|
||||
|
||||
ConcurrentHashMap<Long, ActiveUser> activeUsers = new ConcurrentHashMap<>();
|
||||
ConcurrentHashMap<Long, AvailableBook> availableBooks = new ConcurrentHashMap<>();
|
||||
ConcurrentHashMap<Long, ReservedBook> reservedBooks = new ConcurrentHashMap<>();
|
||||
ConcurrentHashMap<Long, BorrowedBook> borrowedBooks = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void save(AvailableBook availableBook) {
|
||||
availableBooks.put(availableBook.getIdAsLong(), availableBook);
|
||||
reservedBooks.remove(availableBook.getIdAsLong());
|
||||
borrowedBooks.remove(availableBook.getIdAsLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<AvailableBook> getAvailableBook(Long bookId) {
|
||||
if (availableBooks.containsKey(bookId)) {
|
||||
return Optional.of(availableBooks.get(bookId));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ActiveUser> getActiveUser(Long userId) {
|
||||
if (activeUsers.containsKey(userId)) {
|
||||
return Optional.of(activeUsers.get(userId));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReservationDetails save(ReservedBook reservedBook) {
|
||||
Long reservationId = new Random().nextLong();
|
||||
availableBooks.remove(reservedBook.getIdAsLong());
|
||||
reservedBooks.put(reservationId, reservedBook);
|
||||
return new ReservationDetails(new ReservationId(reservationId), reservedBook);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(BorrowedBook borrowedBook) {
|
||||
reservedBooks.remove(borrowedBook.getIdAsLong());
|
||||
borrowedBooks.put(borrowedBook.getIdAsLong(), borrowedBook);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OverdueReservation> findReservationsForMoreThan(Long days) {
|
||||
return reservedBooks.values().stream()
|
||||
.filter(reservedBook ->
|
||||
Instant.now().isAfter(
|
||||
reservedBook.getReservedDateAsInstant().plus(days, ChronoUnit.DAYS)))
|
||||
.map(reservedBook ->
|
||||
new OverdueReservation(
|
||||
1L,
|
||||
reservedBook.getIdAsLong()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ReservedBook> getReservedBook(Long bookId) {
|
||||
return Optional.of(reservedBooks.get(bookId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<BorrowedBook> getBorrowedBook(Long bookId) {
|
||||
return Optional.of(borrowedBooks.get(bookId));
|
||||
}
|
||||
}
|
||||
|
||||
class BorrowingEventPublisherFake implements BorrowingEventPublisher {
|
||||
|
||||
@Override
|
||||
public void publish(BookReservedEvent event) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ActiveUser;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.AvailableBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowedBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.DueDate;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.OverdueReservation;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservationDetails;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservationId;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservedBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingDatabase;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class InMemoryBorrowingDatabase implements BorrowingDatabase {
|
||||
|
||||
ConcurrentHashMap<Long, ActiveUser> activeUsers = new ConcurrentHashMap<>();
|
||||
ConcurrentHashMap<Long, AvailableBook> availableBooks = new ConcurrentHashMap<>();
|
||||
ConcurrentHashMap<Long, ReservedBook> reservedBooks = new ConcurrentHashMap<>();
|
||||
ConcurrentHashMap<Long, BorrowedBook> borrowedBooks = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void setBookAvailable(Long bookId) {
|
||||
availableBooks.put(bookId, new AvailableBook(bookId));
|
||||
reservedBooks.remove(bookId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<AvailableBook> getAvailableBook(Long bookId) {
|
||||
if (availableBooks.containsKey(bookId)) {
|
||||
return Optional.of(availableBooks.get(bookId));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ActiveUser> getActiveUser(Long userId) {
|
||||
if (activeUsers.containsKey(userId)) {
|
||||
return Optional.of(activeUsers.get(userId));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReservationDetails save(ReservedBook reservedBook) {
|
||||
Long reservationId = new Random().nextLong();
|
||||
availableBooks.remove(reservedBook.getIdAsLong());
|
||||
reservedBooks.put(reservationId, reservedBook);
|
||||
return new ReservationDetails(new ReservationId(reservationId), reservedBook);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(BorrowedBook borrowedBook) {
|
||||
reservedBooks.remove(borrowedBook.getIdAsLong());
|
||||
borrowedBooks.put(borrowedBook.getIdAsLong(), borrowedBook);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OverdueReservation> findReservationsAfter(DueDate dueDate) {
|
||||
return reservedBooks.values().stream()
|
||||
.filter(reservedBook ->
|
||||
reservedBook.getReservedDateAsInstant()
|
||||
.isAfter(dueDate.asInstant()))
|
||||
.map(reservedBook ->
|
||||
new OverdueReservation(
|
||||
1L,
|
||||
reservedBook.getIdAsLong()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ReservedBook> getReservedBook(Long bookId) {
|
||||
return Optional.of(reservedBooks.get(bookId));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.borrowing.infrastructure;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.BookTestData;
|
||||
import io.wkrzywiec.hexagonal.library.UserTestData;
|
||||
import io.wkrzywiec.hexagonal.library.DatabaseHelper;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ActiveUser;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.AvailableBook;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowedBook;
|
||||
@@ -10,7 +9,6 @@ import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.OverdueReserva
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservationDetails;
|
||||
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservedBook;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -31,12 +29,13 @@ public class BorrowingDatabaseAdapterITCase {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
private DatabaseHelper databaseHelper;
|
||||
private BorrowingDatabaseAdapter database;
|
||||
|
||||
@BeforeEach
|
||||
public void init(){
|
||||
database = new BorrowingDatabaseAdapter(jdbcTemplate);
|
||||
databaseHelper = new DatabaseHelper(jdbcTemplate);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -45,17 +44,14 @@ public class BorrowingDatabaseAdapterITCase {
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void shouldSaveAvailableBook(){
|
||||
//given
|
||||
Long bookId = getHomoDeusBookId();
|
||||
Long bookId = databaseHelper.getHomoDeusBookId();
|
||||
|
||||
//when
|
||||
database.setBookAvailable(bookId);
|
||||
database.save(new AvailableBook(bookId));
|
||||
|
||||
//then
|
||||
Long savedBookId = jdbcTemplate.queryForObject(
|
||||
"SELECT book_id FROM available WHERE book_id = ?",
|
||||
Long.class,
|
||||
bookId);
|
||||
assertEquals(bookId, savedBookId);
|
||||
Long id = databaseHelper.getPrimaryKeyOfAvailableByBookBy(bookId);
|
||||
assertTrue(id > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -64,7 +60,7 @@ public class BorrowingDatabaseAdapterITCase {
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void shouldGetAvailableBook(){
|
||||
//given
|
||||
Long bookId = getHomoDeusBookId();
|
||||
Long bookId = databaseHelper.getHomoDeusBookId();
|
||||
|
||||
//when
|
||||
Optional<AvailableBook> availableBookOptional = database.getAvailableBook(bookId);
|
||||
@@ -80,7 +76,7 @@ public class BorrowingDatabaseAdapterITCase {
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void shouldGetActiveUser() {
|
||||
//given
|
||||
Long activeUserId = getJohnDoeUserId();
|
||||
Long activeUserId = databaseHelper.getJohnDoeUserId();
|
||||
|
||||
//when
|
||||
Optional<ActiveUser> activeUserOptional = database.getActiveUser(activeUserId);
|
||||
@@ -96,9 +92,8 @@ public class BorrowingDatabaseAdapterITCase {
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void shouldSaveReservedBook(){
|
||||
//given
|
||||
Long bookId = getHomoDeusBookId();
|
||||
|
||||
Long activeUserId = getJohnDoeUserId();
|
||||
Long bookId = databaseHelper.getHomoDeusBookId();
|
||||
Long activeUserId = databaseHelper.getJohnDoeUserId();
|
||||
|
||||
ReservedBook reservedBook = new ReservedBook(bookId, activeUserId);
|
||||
|
||||
@@ -112,18 +107,12 @@ public class BorrowingDatabaseAdapterITCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
@DisplayName("Get reserved book by its id")
|
||||
@Sql({"/book-and-user.sql"})
|
||||
@Sql({"/book-and-user.sql", "/reserved-book.sql"})
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void shouldFindReservedBook(){
|
||||
//given
|
||||
Long bookId = getHomoDeusBookId();
|
||||
Long johnDoeUserId = getJohnDoeUserId();
|
||||
jdbcTemplate.update(
|
||||
"INSERT INTO public.reserved (book_id, user_id) VALUES (?, ?)",
|
||||
bookId,
|
||||
johnDoeUserId);
|
||||
Long bookId = databaseHelper.getHomoDeusBookId();
|
||||
|
||||
//when
|
||||
Optional<ReservedBook> reservedBook = database.getReservedBook(bookId);
|
||||
@@ -139,8 +128,8 @@ public class BorrowingDatabaseAdapterITCase {
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void shouldSaveBorrowedBook(){
|
||||
//given
|
||||
Long bookId = getHomoDeusBookId();
|
||||
Long activeUserId = getJohnDoeUserId();
|
||||
Long bookId = databaseHelper.getHomoDeusBookId();
|
||||
Long activeUserId = databaseHelper.getJohnDoeUserId();
|
||||
|
||||
BorrowedBook borrowedBook = new BorrowedBook(bookId, activeUserId);
|
||||
|
||||
@@ -148,10 +137,7 @@ public class BorrowingDatabaseAdapterITCase {
|
||||
database.save(borrowedBook);
|
||||
|
||||
//then
|
||||
Long savedBookId = jdbcTemplate.queryForObject(
|
||||
"SELECT book_id FROM borrowed WHERE book_id = ?",
|
||||
Long.class,
|
||||
bookId);
|
||||
Long savedBookId = databaseHelper.getPrimaryKeyOfBorrowedByBookId(bookId);
|
||||
assertEquals(bookId, savedBookId);
|
||||
}
|
||||
|
||||
@@ -161,33 +147,34 @@ public class BorrowingDatabaseAdapterITCase {
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void shouldFindOverdueReservations(){
|
||||
//given
|
||||
DueDate thirdDayAfterReservation = new DueDate(Instant.now().plus(3, ChronoUnit.DAYS));
|
||||
Long overdueBookId = getHomoDeusBookId();
|
||||
Long johnDoeUserId = getJohnDoeUserId();
|
||||
Long overdueBookId = databaseHelper.getHomoDeusBookId();
|
||||
Long johnDoeUserId = databaseHelper.getJohnDoeUserId();
|
||||
jdbcTemplate.update(
|
||||
"INSERT INTO public.reserved (book_id, user_id, reserved_date) VALUES (?, ?, ?)",
|
||||
overdueBookId,
|
||||
johnDoeUserId,
|
||||
Instant.now().plus(3, ChronoUnit.DAYS));
|
||||
Instant.now().plus(4, ChronoUnit.DAYS));
|
||||
|
||||
//when
|
||||
OverdueReservation overdueReservation = database.findReservationsAfter(thirdDayAfterReservation).get(0);
|
||||
OverdueReservation overdueReservation = database.findReservationsForMoreThan(3L).get(0);
|
||||
|
||||
//then
|
||||
assertEquals(overdueBookId, overdueReservation.getBookIdentificationAsLong());
|
||||
}
|
||||
|
||||
private Long getHomoDeusBookId(){
|
||||
return jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM book WHERE title = ?",
|
||||
Long.class,
|
||||
BookTestData.homoDeusBookTitle());
|
||||
}
|
||||
@Test
|
||||
@DisplayName("Find borrowed book by id")
|
||||
@Sql({"/book-and-user.sql", "/borrowed-book.sql"})
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void shouldFindBorrowedBook(){
|
||||
//given
|
||||
Long bookId = databaseHelper.getHomoDeusBookId();
|
||||
|
||||
private Long getJohnDoeUserId(){
|
||||
return jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM user WHERE email = ?",
|
||||
Long.class,
|
||||
UserTestData.johnDoeEmail());
|
||||
//when
|
||||
Optional<BorrowedBook> borrowedBook = database.getBorrowedBook(bookId);
|
||||
|
||||
//then
|
||||
assertTrue(borrowedBook.isPresent());
|
||||
assertEquals(bookId, borrowedBook.get().getIdAsLong());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.email.infrastructure;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.BookTestData;
|
||||
import io.wkrzywiec.hexagonal.library.DatabaseHelper;
|
||||
import io.wkrzywiec.hexagonal.library.UserTestData;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
@@ -20,12 +21,13 @@ public class EmailDatabaseAdapterITCase {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
private DatabaseHelper databaseHelper;
|
||||
private EmailDatabaseAdapter emailDatabase;
|
||||
|
||||
@BeforeEach
|
||||
public void init(){
|
||||
emailDatabase = new EmailDatabaseAdapter(jdbcTemplate);
|
||||
databaseHelper = new DatabaseHelper(jdbcTemplate);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -34,10 +36,7 @@ public class EmailDatabaseAdapterITCase {
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void givenBookId_whenGetBookTitle_thenGetBookTitle() {
|
||||
//given
|
||||
Long bookId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM book WHERE title = ?",
|
||||
Long.class,
|
||||
BookTestData.homoDeusBookTitle());
|
||||
Long bookId = databaseHelper.getHomoDeusBookId();
|
||||
|
||||
//when
|
||||
Optional<String> bookTitle = emailDatabase.getTitleByBookId(bookId);
|
||||
@@ -52,10 +51,7 @@ public class EmailDatabaseAdapterITCase {
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void givenWrongBookId_whenGetBookTitle_thenGetEmptyResult() {
|
||||
//given
|
||||
Long bookId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM book WHERE title = ?",
|
||||
Long.class,
|
||||
BookTestData.homoDeusBookTitle());
|
||||
Long bookId = databaseHelper.getHomoDeusBookId();
|
||||
|
||||
//when
|
||||
Optional<String> bookTitle = emailDatabase.getTitleByBookId(bookId + 1124);
|
||||
@@ -70,10 +66,7 @@ public class EmailDatabaseAdapterITCase {
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void givenUserId_whenGetEmail_thenGetEmailAddress() {
|
||||
//given
|
||||
Long userId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM user WHERE email = ?",
|
||||
Long.class,
|
||||
UserTestData.johnDoeEmail());
|
||||
Long userId = databaseHelper.getJohnDoeUserId();
|
||||
|
||||
//when
|
||||
Optional<String> emailAddress = emailDatabase.getUserEmailAddress(userId);
|
||||
@@ -88,10 +81,7 @@ public class EmailDatabaseAdapterITCase {
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void givenWrongUserId_whenGetEmail_thenGetEmptyResult() {
|
||||
//given
|
||||
Long userId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM user WHERE email = ?",
|
||||
Long.class,
|
||||
UserTestData.johnDoeEmail());
|
||||
Long userId = databaseHelper.getJohnDoeUserId();
|
||||
|
||||
//when
|
||||
Optional<String> emailAddress = emailDatabase.getUserEmailAddress(userId + 1124);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.wkrzywiec.hexagonal.library.domain.user.infrastructure;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.DatabaseHelper;
|
||||
import io.wkrzywiec.hexagonal.library.UserTestData;
|
||||
import io.wkrzywiec.hexagonal.library.domain.user.core.model.EmailAddress;
|
||||
import io.wkrzywiec.hexagonal.library.domain.user.core.model.User;
|
||||
@@ -20,6 +21,7 @@ public class UserDatabaseAdapterITCase {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
private DatabaseHelper databaseHelper;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
@@ -29,6 +31,7 @@ public class UserDatabaseAdapterITCase {
|
||||
@BeforeEach
|
||||
public void init(){
|
||||
userDatabase = new UserDatabaseAdapter(userRepository);
|
||||
databaseHelper = new DatabaseHelper(jdbcTemplate);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -45,10 +48,7 @@ public class UserDatabaseAdapterITCase {
|
||||
UserIdentifier userIdentifier = userDatabase.save(user);
|
||||
|
||||
//then
|
||||
Long savedUserId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM user WHERE email = ?",
|
||||
Long.class,
|
||||
UserTestData.johnDoeEmail());
|
||||
Long savedUserId = databaseHelper.getJohnDoeUserId();
|
||||
|
||||
assertEquals(userIdentifier.getAsLong(), savedUserId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# Name of the implementation class(es) of report creator(s) to enable (separate multiple entries with commas)
|
||||
# Currently supported classes are:
|
||||
# 1. com.athaydes.spockframework.report.internal.HtmlReportCreator
|
||||
# 2. com.athaydes.spockframework.report.template.TemplateReportCreator
|
||||
com.athaydes.spockframework.report.IReportCreator=com.athaydes.spockframework.report.internal.HtmlReportCreator
|
||||
|
||||
# Set properties of the report creator
|
||||
# For the HtmlReportCreator, the only properties available are
|
||||
# (the location of the css files is relative to the classpath):
|
||||
com.athaydes.spockframework.report.internal.HtmlReportCreator.featureReportCss=spock-feature-report.css
|
||||
com.athaydes.spockframework.report.internal.HtmlReportCreator.summaryReportCss=spock-summary-report.css
|
||||
com.athaydes.spockframework.report.internal.HtmlReportCreator.printThrowableStackTrace=false
|
||||
com.athaydes.spockframework.report.internal.HtmlReportCreator.inlineCss=true
|
||||
com.athaydes.spockframework.report.internal.HtmlReportCreator.enabled=true
|
||||
# options are: "class_name_and_title", "class_name", "title"
|
||||
com.athaydes.spockframework.report.internal.HtmlReportCreator.specSummaryNameOption=class_name_and_title
|
||||
|
||||
# exclude Specs Table of Contents
|
||||
com.athaydes.spockframework.report.internal.HtmlReportCreator.excludeToc=false
|
||||
|
||||
# Output directory (where the spock reports will be created) - relative to working directory
|
||||
com.athaydes.spockframework.report.outputDir=target/spock-reports
|
||||
|
||||
# Output directory where to store the aggregated JSON report (used to support parallel builds)
|
||||
com.athaydes.spockframework.report.aggregatedJsonReportDir=
|
||||
|
||||
# If set to true, hides blocks which do not have any description
|
||||
com.athaydes.spockframework.report.hideEmptyBlocks=false
|
||||
|
||||
# Set the name of the project under test so it can be displayed in the report
|
||||
com.athaydes.spockframework.report.projectName=
|
||||
|
||||
# Set the version of the project under test so it can be displayed in the report
|
||||
com.athaydes.spockframework.report.projectVersion=Unknown
|
||||
|
||||
# Show the source code for each block
|
||||
com.athaydes.spockframework.report.showCodeBlocks=true
|
||||
|
||||
# Set the root location of the Spock test source code (only used if showCodeBlocks is 'true')
|
||||
com.athaydes.spockframework.report.testSourceRoots=src/test/groovy
|
||||
|
||||
# Set properties specific to the TemplateReportCreator
|
||||
com.athaydes.spockframework.report.template.TemplateReportCreator.specTemplateFile=/templateReportCreator/spec-template.md
|
||||
com.athaydes.spockframework.report.template.TemplateReportCreator.reportFileExtension=md
|
||||
com.athaydes.spockframework.report.template.TemplateReportCreator.summaryTemplateFile=/templateReportCreator/summary-template.md
|
||||
com.athaydes.spockframework.report.template.TemplateReportCreator.summaryFileName=summary.md
|
||||
com.athaydes.spockframework.report.template.TemplateReportCreator.enabled=true
|
||||
@@ -1,17 +1,17 @@
|
||||
INSERT INTO public.author (name) VALUES
|
||||
INSERT INTO author (name) VALUES
|
||||
('Yuval Noah Harari')
|
||||
;
|
||||
|
||||
INSERT INTO public.book (book_external_id,isbn_10,isbn_13,title,publisher,published_date,description,page_count,image_link) VALUES
|
||||
INSERT INTO book (book_external_id,isbn_10,isbn_13,title,publisher,published_date,description,page_count,image_link) VALUES
|
||||
('dWYyCwAAQBAJ','1473545374','9781473545373','Homo Deus','Random House','2016-09-08','<p><b>**THE MILLION COPY BESTSELLER**</b><br> <b></b><br><b> <i>Sapiens </i>showed us where we came from. In uncertain times, <i>Homo Deus</i> shows us where we’re going.</b></p><p> Yuval Noah Harari envisions a near future in which we face a new set of challenges. <i>Homo Deus</i> explores the projects, dreams and nightmares that will shape the twenty-first century and beyond – from overcoming death to creating artificial life.</p><p> It asks the fundamental questions: how can we protect this fragile world from our own destructive power? And what does our future hold?<br> <b></b><br><b> ''<i>Homo Deus</i> will shock you. It will entertain you. It will make you think in ways you had not thought before’ Daniel Kahneman, bestselling author of <i>Thinking, Fast and Slow</i></b></p>',528,'http://books.google.com/books/content?id=dWYyCwAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&imgtk=AFLRE73PkLs4TNB-W2uhDvXJkIB4-9G9AJ_L1iYTYLEXa3zi2kahdsN9-_0tL7WRWgujNpjMA5ZuJO7_ykFUlCWAyLzcQVcGkqUS-NOkUkEcJ_ZRrgq48URpcfBrJWQCwSWtHo5pEGkp&source=gbs_api')
|
||||
;
|
||||
|
||||
INSERT INTO public.book_author (book_id, author_id)
|
||||
INSERT INTO book_author (book_id, author_id)
|
||||
SELECT b.id, a.id
|
||||
FROM public.book b, public.author a
|
||||
WHERE b.title = 'Homo deus' AND a.name = 'Yuval Noah Harari'
|
||||
;
|
||||
|
||||
INSERT INTO public.user (first_name, last_name, email) VALUES
|
||||
INSERT INTO library_user (first_name, last_name, email) VALUES
|
||||
('John','Doe','john.doe@test.com')
|
||||
;
|
||||
5
src/test/resources/borrowed-book.sql
Normal file
5
src/test/resources/borrowed-book.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
INSERT INTO public.borrowed (book_id, user_id)
|
||||
VALUES (
|
||||
(SELECT id FROM book WHERE title = 'Homo Deus'),
|
||||
(SELECT id FROM library_user WHERE email = 'john.doe@test.com')
|
||||
);
|
||||
@@ -2,6 +2,6 @@ DELETE FROM public.borrowed;
|
||||
DELETE FROM public.reserved;
|
||||
DELETE FROM public.available;
|
||||
DELETE FROM public.book_author;
|
||||
DELETE FROM public.user;
|
||||
DELETE FROM public.library_user;
|
||||
DELETE FROM public.book;
|
||||
DELETE FROM public.author;
|
||||
5
src/test/resources/reserved-book.sql
Normal file
5
src/test/resources/reserved-book.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
INSERT INTO public.reserved (book_id, user_id)
|
||||
VALUES (
|
||||
(SELECT id FROM book WHERE title = 'Homo Deus'),
|
||||
(SELECT id FROM library_user WHERE email = 'john.doe@test.com')
|
||||
);
|
||||
Reference in New Issue
Block a user