diff --git a/customer-api-provider/pom.xml b/customer-api-provider/pom.xml index 8a725ee..198ea72 100644 --- a/customer-api-provider/pom.xml +++ b/customer-api-provider/pom.xml @@ -28,6 +28,13 @@ pom import + + org.assertj + assertj-bom + 3.26.0 + pom + import + @@ -56,6 +63,16 @@ rest-assured test + + org.assertj + assertj-core + test + + + io.quarkus + quarkus-junit5-mockito + test + io.quarkus quarkus-hibernate-validator diff --git a/customer-api-provider/src/main/java/de/schulung/sample/quarkus/Customer.java b/customer-api-provider/src/main/java/de/schulung/sample/quarkus/Customer.java index ff7165f..ba0a508 100644 --- a/customer-api-provider/src/main/java/de/schulung/sample/quarkus/Customer.java +++ b/customer-api-provider/src/main/java/de/schulung/sample/quarkus/Customer.java @@ -2,6 +2,7 @@ import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbTransient; +import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; @@ -22,10 +23,12 @@ public class Customer { @Setter(onMethod_ = @JsonbTransient) private UUID uuid; @Size(min = 3, max = 100) + @NotNull private String name; @JsonbProperty("birth_date") // TODO -> use snake_case globally? private LocalDate birthdate; @Pattern(regexp = "active|locked|disabled") + @NotNull private String state; } diff --git a/customer-api-provider/src/main/java/de/schulung/sample/quarkus/CustomersResource.java b/customer-api-provider/src/main/java/de/schulung/sample/quarkus/CustomersResource.java index 604f3dd..4f2eab4 100644 --- a/customer-api-provider/src/main/java/de/schulung/sample/quarkus/CustomersResource.java +++ b/customer-api-provider/src/main/java/de/schulung/sample/quarkus/CustomersResource.java @@ -5,38 +5,30 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriBuilder; +import lombok.RequiredArgsConstructor; -import java.time.LocalDate; -import java.time.Month; -import java.util.*; +import java.util.Collection; +import java.util.UUID; @Path("/customers") +@RequiredArgsConstructor public class CustomersResource { - private final Map customers = new HashMap<>(); - - { - var customer = new Customer( - UUID.randomUUID(), - "Tom", - LocalDate.of(2000, Month.DECEMBER, 6), - "active" - ); - customers.put(customer.getUuid(), customer); - } + private final CustomersService service; @GET @Produces(MediaType.APPLICATION_JSON) public Collection getCustomers() { - return customers.values(); + return service + .getAll() + .toList(); } @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response createCustomer(@Valid Customer customer) { - customer.setUuid(UUID.randomUUID()); - customers.put(customer.getUuid(), customer); + service.createCustomer(customer); final var location = UriBuilder.fromResource(CustomersResource.class) .path(CustomersResource.class, "findCustomerById") .build(customer.getUuid().toString()); @@ -58,14 +50,14 @@ public Response createCustomer(@Valid Customer customer) { @GET @Path("/{uuid}") public Customer findCustomerById(@PathParam("uuid") UUID uuid) { - return Optional.ofNullable(customers.get(uuid)) - .orElseThrow(NotFoundException::new); + return service.getByUuid(uuid) + .orElseThrow(NotFoundException::new); } @DELETE @Path("/{uuid}") public Response deleteCustomerById(@PathParam("uuid") UUID uuid) { - if (customers.remove(uuid) == null) { + if (!service.delete(uuid)) { throw new NotFoundException(); } return Response diff --git a/customer-api-provider/src/main/java/de/schulung/sample/quarkus/CustomersService.java b/customer-api-provider/src/main/java/de/schulung/sample/quarkus/CustomersService.java new file mode 100644 index 0000000..d833329 --- /dev/null +++ b/customer-api-provider/src/main/java/de/schulung/sample/quarkus/CustomersService.java @@ -0,0 +1,63 @@ +package de.schulung.sample.quarkus; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; + +import java.time.LocalDate; +import java.time.Month; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Stream; + +@ApplicationScoped +public class CustomersService { + + private final Map customers = new HashMap<>(); + + { // TODO replace this? + var customer = new Customer( + UUID.randomUUID(), + "Tom", + LocalDate.of(2000, Month.DECEMBER, 6), + "active" + ); + customers.put(customer.getUuid(), customer); + } + + public Stream getAll() { + return this.customers + .values() + .stream(); + } + + public Stream getByState( + @NotNull + @Pattern(regexp = "active|locked|disabled") + String state + ) { + return this.getAll() + .filter(c -> c.getState().equals(state)); + } + + public void createCustomer(@Valid Customer customer) { + customer.setUuid(UUID.randomUUID()); + customers.put(customer.getUuid(), customer); + } + + public Optional getByUuid(@NotNull UUID uuid) { + return Optional.ofNullable(customers.get(uuid)); + } + + public boolean exists(@NotNull UUID uuid) { + return customers.containsKey(uuid); + } + + public boolean delete(@NotNull UUID uuid) { + return customers.remove(uuid) != null; + } + +} diff --git a/customer-api-provider/src/test/java/de/schulung/sample/quarkus/CustomerApiMockedServiceTests.java b/customer-api-provider/src/test/java/de/schulung/sample/quarkus/CustomerApiMockedServiceTests.java new file mode 100644 index 0000000..777728a --- /dev/null +++ b/customer-api-provider/src/test/java/de/schulung/sample/quarkus/CustomerApiMockedServiceTests.java @@ -0,0 +1,61 @@ +package de.schulung.sample.quarkus; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.Test; + +import java.util.Optional; +import java.util.UUID; + +import static io.restassured.RestAssured.given; +import static org.mockito.Mockito.*; + +@QuarkusTest +public class CustomerApiMockedServiceTests { + + @InjectMock + CustomersService service; + + @Test + void shouldReturn404WhenCustomerNotExists() { + + var uuid = UUID.randomUUID(); + when(service.getByUuid(uuid)) + .thenReturn(Optional.empty()); + + given() + .when() + .accept(ContentType.JSON) + .get("/customers/{uuid}", uuid) + .then() + .statusCode(404); + + verify(service).getByUuid(uuid); + + } + + // TODO: wenn invalider Kunde angelegt werden soll, dann 400 + kein Service-Aufruf + + @Test + void shouldNotInvokeServiceWhenInvalidCustomerIsCreated() { + given() + .when() + .contentType(ContentType.JSON) + .body(""" + { + "name": "T", + "birth_date": "2000-10-04", + "state": "active" + } + """) + .accept(ContentType.JSON) + .post("/customers") + .then() + .statusCode(400); + + verifyNoInteractions(service); + + } + +} diff --git a/customer-api-provider/src/test/java/de/schulung/sample/quarkus/CustomersServiceIntegrationTests.java b/customer-api-provider/src/test/java/de/schulung/sample/quarkus/CustomersServiceIntegrationTests.java new file mode 100644 index 0000000..64b001d --- /dev/null +++ b/customer-api-provider/src/test/java/de/schulung/sample/quarkus/CustomersServiceIntegrationTests.java @@ -0,0 +1,36 @@ +package de.schulung.sample.quarkus; + +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@QuarkusTest +public class CustomersServiceIntegrationTests { + + @Inject + CustomersService service; + + // TODO Testfall: Customer anlegen mit zu kurzem Namen -> Validierungsfehler + + @Test + void shouldNotCreateInvalidCustomer() { + var customer = new Customer(); + assertThatThrownBy(() -> service.createCustomer(customer)) + .isNotNull(); + } + + @Test + void shouldNotCreateNullCustomer() { + assertThatThrownBy(() -> service.createCustomer(null)) + .isNotNull(); + } + + @Test + void shouldNotInvokeSearchWithNullUuid() { + assertThatThrownBy(() -> service.getByUuid(null)) + .isNotNull(); + } + +} diff --git a/customer-api-provider/src/test/java/de/schulung/sample/quarkus/CustomersServiceTests.java b/customer-api-provider/src/test/java/de/schulung/sample/quarkus/CustomersServiceTests.java new file mode 100644 index 0000000..4dcab18 --- /dev/null +++ b/customer-api-provider/src/test/java/de/schulung/sample/quarkus/CustomersServiceTests.java @@ -0,0 +1,41 @@ +package de.schulung.sample.quarkus; + +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; +import java.time.Month; + +import static org.assertj.core.api.Assertions.assertThat; + +class CustomersServiceTests { + + private final CustomersService service = new CustomersService(); + + @Test + void shouldAddUuidToNewCustomer() { + var customer = new Customer(); + customer.setName("Tom"); + customer.setBirthdate(LocalDate.of(2000, Month.FEBRUARY, 2)); + customer.setState("active"); + + service.createCustomer(customer); + + assertThat(customer.getUuid()) + .isNotNull(); + } + + @Test + void shouldFindNewCustomer() { + var customer = new Customer(); + customer.setName("Tom"); + customer.setBirthdate(LocalDate.of(2000, Month.FEBRUARY, 2)); + customer.setState("active"); + service.createCustomer(customer); + var uuid = customer.getUuid(); + + var result = service.getByUuid(uuid); + + assertThat(result).isNotEmpty(); + } + +}