From d34246a87e1fb22cd4eb9451da1b086df1b4d23a Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Sun, 22 Dec 2024 15:29:00 +0000 Subject: [PATCH] feat : use SQLStatementValidator and ValidationGroups --- .../sequence/config/{ => db}/JpaConfig.java | 2 +- .../LazyConnectionDataSourceProxyConfig.java | 2 +- ...xedNumberFormattedSequenceIdGenerator.java | 2 +- .../{ => db}/StringPrefixedSequence.java | 2 +- .../custom/sequence/entities/Customer.java | 2 +- .../custom/sequence/entities/Order.java | 2 +- .../sequence/mapper/CustomerMapper.java | 6 + .../model/request/CustomerRequest.java | 4 +- .../sequence/model/request/OrderRequest.java | 8 +- .../model/request/ValidationGroups.java | 8 + .../sequence/services/CustomerService.java | 7 +- .../web/controllers/CustomerController.java | 17 +- .../web/controllers/OrderController.java | 9 +- ...ot.autoconfigure.AutoConfiguration.imports | 2 +- .../custom/sequence/SchemaValidationTest.java | 2 +- .../common/AbstractIntegrationTest.java | 4 +- .../services/CustomerServiceTest.java | 13 +- .../web/controllers/CustomerControllerIT.java | 300 +++++++++++++----- .../controllers/CustomerControllerTest.java | 42 ++- .../web/controllers/OrderControllerIT.java | 222 ++++++++----- .../web/controllers/OrderControllerTest.java | 4 +- 21 files changed, 460 insertions(+), 200 deletions(-) rename jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/{ => db}/JpaConfig.java (89%) rename jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/{ => db}/LazyConnectionDataSourceProxyConfig.java (97%) rename jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/{ => db}/StringPrefixedNumberFormattedSequenceIdGenerator.java (97%) rename jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/{ => db}/StringPrefixedSequence.java (91%) create mode 100644 jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/model/request/ValidationGroups.java diff --git a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/JpaConfig.java b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/db/JpaConfig.java similarity index 89% rename from jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/JpaConfig.java rename to jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/db/JpaConfig.java index b742fc25f..cc059ab2d 100644 --- a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/JpaConfig.java +++ b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/db/JpaConfig.java @@ -1,4 +1,4 @@ -package com.example.custom.sequence.config; +package com.example.custom.sequence.config.db; import io.hypersistence.utils.spring.repository.BaseJpaRepositoryImpl; import org.springframework.context.annotation.Configuration; diff --git a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/LazyConnectionDataSourceProxyConfig.java b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/db/LazyConnectionDataSourceProxyConfig.java similarity index 97% rename from jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/LazyConnectionDataSourceProxyConfig.java rename to jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/db/LazyConnectionDataSourceProxyConfig.java index 3c9ee6167..3ff4a184f 100644 --- a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/LazyConnectionDataSourceProxyConfig.java +++ b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/db/LazyConnectionDataSourceProxyConfig.java @@ -1,4 +1,4 @@ -package com.example.custom.sequence.config; +package com.example.custom.sequence.config.db; import io.hypersistence.utils.logging.InlineQueryLogEntryCreator; import javax.sql.DataSource; diff --git a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/StringPrefixedNumberFormattedSequenceIdGenerator.java b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/db/StringPrefixedNumberFormattedSequenceIdGenerator.java similarity index 97% rename from jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/StringPrefixedNumberFormattedSequenceIdGenerator.java rename to jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/db/StringPrefixedNumberFormattedSequenceIdGenerator.java index 12dcf44a5..26bc79a94 100644 --- a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/StringPrefixedNumberFormattedSequenceIdGenerator.java +++ b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/db/StringPrefixedNumberFormattedSequenceIdGenerator.java @@ -1,4 +1,4 @@ -package com.example.custom.sequence.config; +package com.example.custom.sequence.config.db; import java.io.Serializable; import java.lang.reflect.Member; diff --git a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/StringPrefixedSequence.java b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/db/StringPrefixedSequence.java similarity index 91% rename from jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/StringPrefixedSequence.java rename to jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/db/StringPrefixedSequence.java index c9546dd6d..6edbd35ff 100644 --- a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/StringPrefixedSequence.java +++ b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/config/db/StringPrefixedSequence.java @@ -1,4 +1,4 @@ -package com.example.custom.sequence.config; +package com.example.custom.sequence.config.db; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; diff --git a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/entities/Customer.java b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/entities/Customer.java index e43c647e6..d3d8b6197 100644 --- a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/entities/Customer.java +++ b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/entities/Customer.java @@ -1,6 +1,6 @@ package com.example.custom.sequence.entities; -import com.example.custom.sequence.config.StringPrefixedSequence; +import com.example.custom.sequence.config.db.StringPrefixedSequence; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/entities/Order.java b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/entities/Order.java index efb8638e1..f93c77bea 100644 --- a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/entities/Order.java +++ b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/entities/Order.java @@ -1,6 +1,6 @@ package com.example.custom.sequence.entities; -import com.example.custom.sequence.config.StringPrefixedSequence; +import com.example.custom.sequence.config.db.StringPrefixedSequence; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; diff --git a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/mapper/CustomerMapper.java b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/mapper/CustomerMapper.java index 351af323a..277c76f6e 100644 --- a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/mapper/CustomerMapper.java +++ b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/mapper/CustomerMapper.java @@ -30,6 +30,9 @@ public CustomerResponse mapToResponse(Customer saved) { public Customer mapToEntity(CustomerRequest customerRequest) { Customer customer = new Customer(customerRequest.text()); + if (customerRequest.orders() == null) { + return customer; + } customerRequest .orders() .forEach(orderRequest -> customer.addOrder(orderMapper.mapToEntity(orderRequest))); @@ -38,6 +41,9 @@ public Customer mapToEntity(CustomerRequest customerRequest) { public void updateCustomerFromRequest(CustomerRequest customerRequest, Customer foundCustomer) { foundCustomer.setText(customerRequest.text()); + if (customerRequest.orders() == null) { + return; + } List removedOrders = new ArrayList<>(foundCustomer.getOrders()); List ordersFromRequest = customerRequest.orders().stream() diff --git a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/model/request/CustomerRequest.java b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/model/request/CustomerRequest.java index 1ff7719d6..71358bc50 100644 --- a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/model/request/CustomerRequest.java +++ b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/model/request/CustomerRequest.java @@ -1,7 +1,9 @@ package com.example.custom.sequence.model.request; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import java.util.List; public record CustomerRequest( - @NotBlank(message = "Text cannot be empty") String text, List orders) {} + @NotBlank(message = "Text cannot be empty") String text, + @Valid List orders) {} diff --git a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/model/request/OrderRequest.java b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/model/request/OrderRequest.java index 88b07373e..699154ae9 100644 --- a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/model/request/OrderRequest.java +++ b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/model/request/OrderRequest.java @@ -1,8 +1,10 @@ package com.example.custom.sequence.model.request; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotEmpty; public record OrderRequest( - @NotEmpty(message = "Text cannot be empty") String text, - @NotBlank(message = "CustomerId cannot be blank") String customerId) {} + @NotBlank(message = "Text cannot be empty") String text, + @NotBlank( + message = "CustomerId cannot be blank", + groups = ValidationGroups.GroupCheck.class) + String customerId) {} diff --git a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/model/request/ValidationGroups.java b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/model/request/ValidationGroups.java new file mode 100644 index 000000000..2c05a8412 --- /dev/null +++ b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/model/request/ValidationGroups.java @@ -0,0 +1,8 @@ +package com.example.custom.sequence.model.request; + +public interface ValidationGroups { + + interface SkipGroupCheck {} + + interface GroupCheck {} +} diff --git a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/services/CustomerService.java b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/services/CustomerService.java index 14e9de331..33565163c 100644 --- a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/services/CustomerService.java +++ b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/services/CustomerService.java @@ -77,8 +77,11 @@ public Optional updateCustomerById( } @Transactional - public void deleteCustomerById(String id) { - customerRepository.deleteById(id); + public Optional deleteCustomerById(String id) { + Optional optionalCustomer = findCustomerById(id); + optionalCustomer.ifPresent( + customerResponse -> customerRepository.deleteById(customerResponse.id())); + return optionalCustomer; } public Optional findById(String customerId) { diff --git a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/web/controllers/CustomerController.java b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/web/controllers/CustomerController.java index b9e075e96..5350d6125 100644 --- a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/web/controllers/CustomerController.java +++ b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/web/controllers/CustomerController.java @@ -2,11 +2,13 @@ import com.example.custom.sequence.entities.Customer; import com.example.custom.sequence.model.request.CustomerRequest; +import com.example.custom.sequence.model.request.ValidationGroups; import com.example.custom.sequence.model.response.CustomerResponse; import com.example.custom.sequence.model.response.PagedResult; import com.example.custom.sequence.services.CustomerService; import com.example.custom.sequence.utils.AppConstants; import com.example.custom.sequence.web.api.CustomerAPI; +import jakarta.validation.groups.Default; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -60,13 +62,16 @@ public ResponseEntity getCustomerById(@PathVariable String id) @ResponseStatus(HttpStatus.CREATED) @Override public CustomerResponse createCustomer( - @RequestBody @Validated CustomerRequest customerRequest) { + @RequestBody @Validated(value = {Default.class, ValidationGroups.SkipGroupCheck.class}) + CustomerRequest customerRequest) { return customerService.saveCustomer(customerRequest); } @PutMapping("/{id}") public ResponseEntity updateCustomer( - @PathVariable String id, @RequestBody CustomerRequest customerRequest) { + @PathVariable String id, + @RequestBody @Validated(value = {Default.class, ValidationGroups.GroupCheck.class}) + CustomerRequest customerRequest) { return customerService .updateCustomerById(id, customerRequest) .map(ResponseEntity::ok) @@ -76,12 +81,8 @@ public ResponseEntity updateCustomer( @DeleteMapping("/{id}") public ResponseEntity deleteCustomer(@PathVariable String id) { return customerService - .findCustomerById(id) - .map( - customer -> { - customerService.deleteCustomerById(id); - return ResponseEntity.ok(customer); - }) + .deleteCustomerById(id) + .map(ResponseEntity::ok) .orElseGet(() -> ResponseEntity.notFound().build()); } } diff --git a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/web/controllers/OrderController.java b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/web/controllers/OrderController.java index 9b7a3fcb9..71ea85c63 100644 --- a/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/web/controllers/OrderController.java +++ b/jpa/boot-data-customsequence/src/main/java/com/example/custom/sequence/web/controllers/OrderController.java @@ -1,10 +1,12 @@ package com.example.custom.sequence.web.controllers; import com.example.custom.sequence.model.request.OrderRequest; +import com.example.custom.sequence.model.request.ValidationGroups; import com.example.custom.sequence.model.response.OrderResponse; import com.example.custom.sequence.model.response.PagedResult; import com.example.custom.sequence.services.OrderService; import com.example.custom.sequence.utils.AppConstants; +import jakarta.validation.groups.Default; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -53,7 +55,8 @@ public ResponseEntity getOrderById(@PathVariable String id) { @PostMapping public ResponseEntity createOrder( - @RequestBody @Validated OrderRequest orderRequest) { + @RequestBody @Validated(value = {Default.class, ValidationGroups.GroupCheck.class}) + OrderRequest orderRequest) { return orderService .saveOrder(orderRequest) .map(order -> ResponseEntity.status(HttpStatus.CREATED).body(order)) @@ -62,7 +65,9 @@ public ResponseEntity createOrder( @PutMapping("/{id}") public ResponseEntity updateOrder( - @PathVariable String id, @RequestBody OrderRequest orderRequest) { + @PathVariable String id, + @RequestBody @Validated(value = {Default.class, ValidationGroups.GroupCheck.class}) + OrderRequest orderRequest) { return orderService .updateOrderById(id, orderRequest) .map(ResponseEntity::ok) diff --git a/jpa/boot-data-customsequence/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/jpa/boot-data-customsequence/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 74fc78390..3a2e7c66a 100644 --- a/jpa/boot-data-customsequence/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/jpa/boot-data-customsequence/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1 +1 @@ -com.example.custom.sequence.config.LazyConnectionDataSourceProxyConfig +com.example.custom.sequence.config.db.LazyConnectionDataSourceProxyConfig diff --git a/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/SchemaValidationTest.java b/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/SchemaValidationTest.java index e645aee45..a6fa8af9b 100644 --- a/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/SchemaValidationTest.java +++ b/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/SchemaValidationTest.java @@ -3,7 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import com.example.custom.sequence.common.ContainersConfig; -import com.example.custom.sequence.config.JpaConfig; +import com.example.custom.sequence.config.db.JpaConfig; import com.zaxxer.hikari.HikariDataSource; import javax.sql.DataSource; import org.junit.jupiter.api.Test; diff --git a/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/common/AbstractIntegrationTest.java b/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/common/AbstractIntegrationTest.java index ea775141b..24d640844 100644 --- a/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/common/AbstractIntegrationTest.java +++ b/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/common/AbstractIntegrationTest.java @@ -9,7 +9,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.assertj.MockMvcTester; /** * Base class for integration tests providing test infrastructure including: - Configured DataSource @@ -23,7 +23,7 @@ @AutoConfigureMockMvc public abstract class AbstractIntegrationTest { - @Autowired protected MockMvc mockMvc; + @Autowired protected MockMvcTester mockMvcTester; @Autowired protected ObjectMapper objectMapper; diff --git a/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/services/CustomerServiceTest.java b/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/services/CustomerServiceTest.java index 571c5be6b..5c2b74721 100644 --- a/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/services/CustomerServiceTest.java +++ b/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/services/CustomerServiceTest.java @@ -9,8 +9,8 @@ import com.example.custom.sequence.entities.Customer; import com.example.custom.sequence.mapper.CustomerMapper; import com.example.custom.sequence.model.request.CustomerRequest; -import com.example.custom.sequence.model.response.CustomerResponse; -import com.example.custom.sequence.model.response.PagedResult; +import com.example.custom.sequence.model.request.OrderRequest; +import com.example.custom.sequence.model.response.*; import com.example.custom.sequence.repositories.CustomerRepository; import java.util.List; import java.util.Optional; @@ -88,6 +88,8 @@ void saveCustomer() { void deleteCustomerById() { // given willDoNothing().given(customerRepository).deleteById("CUS_1"); + given(customerRepository.findById("CUS_1")).willReturn(Optional.of(getCustomer())); + given(customerMapper.mapToResponse(getCustomer())).willReturn(getCustomerResponse()); // when customerService.deleteCustomerById("CUS_1"); // then @@ -102,10 +104,13 @@ private Customer getCustomer() { } private CustomerRequest getCustomerRequest() { - return new CustomerRequest("junitTest", List.of()); + return new CustomerRequest("junitTest", List.of(new OrderRequest("ORD_1", "junitTest"))); } private CustomerResponse getCustomerResponse() { - return new CustomerResponse("CUS_1", "junitTest", List.of()); + return new CustomerResponse( + "CUS_1", + "junitTest", + List.of(new OrderResponseWithOutCustomer("ORD_1", "junitTest"))); } } diff --git a/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/CustomerControllerIT.java b/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/CustomerControllerIT.java index 5a4851292..8699070e3 100644 --- a/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/CustomerControllerIT.java +++ b/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/CustomerControllerIT.java @@ -1,26 +1,26 @@ package com.example.custom.sequence.web.controllers; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.Matchers.hasLength; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.startsWith; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.assertj.core.api.Assertions.assertThat; import com.example.custom.sequence.common.AbstractIntegrationTest; import com.example.custom.sequence.entities.Customer; +import com.example.custom.sequence.model.request.CustomerRequest; +import com.example.custom.sequence.model.request.OrderRequest; +import com.example.custom.sequence.model.response.CustomerResponse; +import com.example.custom.sequence.model.response.PagedResult; import com.example.custom.sequence.repositories.CustomerRepository; +import com.fasterxml.jackson.core.JsonProcessingException; +import io.hypersistence.utils.jdbc.validator.SQLStatementCountValidator; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Objects; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ProblemDetail; class CustomerControllerIT extends AbstractIntegrationTest { @@ -37,92 +37,248 @@ void setUp() { customerList.add(new Customer("Second Customer")); customerList.add(new Customer("Third Customer")); customerList = customerRepository.persistAll(customerList); + + SQLStatementCountValidator.reset(); } @Test - void shouldFetchAllCustomers() throws Exception { - this.mockMvc - .perform(get("/api/customers")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.data.size()", is(customerList.size()))) - .andExpect(jsonPath("$.totalElements", is(3))) - .andExpect(jsonPath("$.pageNumber", is(1))) - .andExpect(jsonPath("$.totalPages", is(1))) - .andExpect(jsonPath("$.isFirst", is(true))) - .andExpect(jsonPath("$.isLast", is(true))) - .andExpect(jsonPath("$.hasNext", is(false))) - .andExpect(jsonPath("$.hasPrevious", is(false))); + void shouldFetchAllCustomers() { + + this.mockMvcTester + .get() + .uri("/api/customers") + .assertThat() + .hasStatusOk() + .hasContentType(MediaType.APPLICATION_JSON) + .bodyJson() + .convertTo(PagedResult.class) + .satisfies( + pagedResult -> { + assertThat(pagedResult.data()).hasSize(3); + assertThat(pagedResult.totalElements()).isEqualTo(3); + assertThat(pagedResult.pageNumber()).isEqualTo(1); + assertThat(pagedResult.totalPages()).isEqualTo(1); + assertThat(pagedResult.isFirst()).isTrue(); + assertThat(pagedResult.isLast()).isTrue(); + assertThat(pagedResult.hasNext()).isFalse(); + assertThat(pagedResult.hasPrevious()).isFalse(); + }); + + SQLStatementCountValidator.assertSelectCount(2); + SQLStatementCountValidator.assertTotalCount(2); } @Test - void shouldFindCustomerById() throws Exception { + void shouldFindCustomerById() { Customer customer = customerList.getFirst(); String customerId = customer.getId(); - this.mockMvc - .perform(get("/api/customers/{id}", customerId)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.id", is(customer.getId()))) - .andExpect(jsonPath("$.text", is(customer.getText()))); + this.mockMvcTester + .get() + .uri("/api/customers/{id}", customerId) + .assertThat() + .hasStatusOk() + .hasContentType(MediaType.APPLICATION_JSON) + .bodyJson() + .convertTo(CustomerResponse.class) + .satisfies( + customerResponse -> { + assertThat(customerResponse.id()).isEqualTo(customer.getId()); + assertThat(customerResponse.text()).isEqualTo(customer.getText()); + assertThat(customerResponse.orderResponses()).isEmpty(); + }); + + SQLStatementCountValidator.assertSelectCount(1); + SQLStatementCountValidator.assertTotalCount(1); } @Test void shouldCreateNewCustomer() throws Exception { - Customer customer = new Customer("New Customer"); - this.mockMvc - .perform( - post("/api/customers") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(customer))) - .andExpect(status().isCreated()) - .andExpect(jsonPath("$.id", startsWith("CUS"))) - .andExpect(jsonPath("$.id", hasLength(8))) - .andExpect(jsonPath("$.text", is(customer.getText()))); + CustomerRequest customerRequest = new CustomerRequest("New Customer", null); + + this.mockMvcTester + .post() + .uri("/api/customers") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(customerRequest)) + .assertThat() + .hasStatus(HttpStatus.CREATED) + .hasContentType(MediaType.APPLICATION_JSON) + .bodyJson() + .convertTo(CustomerResponse.class) + .satisfies( + customerResponse -> { + assertThat(customerResponse.id()).startsWith("CUS").hasSize(8); + assertThat(customerResponse.text()).isEqualTo(customerRequest.text()); + assertThat(customerResponse.orderResponses()).isEmpty(); + }); + + SQLStatementCountValidator.assertSelectCount(0); + SQLStatementCountValidator.assertInsertCount(1); + SQLStatementCountValidator.assertTotalCount(1); + + assertThat(customerRepository.count()).isEqualTo(4); } @Test - void shouldReturn400WhenCreateNewCustomerWithoutText() throws Exception { - Customer customer = new Customer(null); - - this.mockMvc - .perform( - post("/api/customers") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(customer))) - .andExpect(status().isBadRequest()) - .andExpect(header().string("Content-Type", is("application/problem+json"))) - .andExpect(jsonPath("$.type", is("about:blank"))) - .andExpect(jsonPath("$.title", is("Constraint Violation"))) - .andExpect(jsonPath("$.status", is(400))) - .andExpect(jsonPath("$.detail", is("Invalid request content."))) - .andExpect(jsonPath("$.instance", is("/api/customers"))) - .andExpect(jsonPath("$.violations", hasSize(1))) - .andExpect(jsonPath("$.violations[0].field", is("text"))) - .andExpect(jsonPath("$.violations[0].message", is("Text cannot be empty"))) - .andReturn(); + void shouldReturn400WhenCreateNewCustomerWithoutText() throws JsonProcessingException { + CustomerRequest customerRequest = + new CustomerRequest(null, List.of(new OrderRequest("First Order", null))); + + this.mockMvcTester + .post() + .uri("/api/customers") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(customerRequest)) + .assertThat() + .hasStatus(HttpStatus.BAD_REQUEST) + .hasContentType(MediaType.APPLICATION_PROBLEM_JSON) + .bodyJson() + .convertTo(ProblemDetail.class) + .satisfies( + errorResponse -> { + assertThat(errorResponse.getType().toString()).isEqualTo("about:blank"); + assertThat(errorResponse.getTitle()).isEqualTo("Constraint Violation"); + assertThat(errorResponse.getStatus()).isEqualTo(400); + assertThat(errorResponse.getDetail()) + .isEqualTo("Invalid request content."); + assertThat( + Objects.requireNonNull(errorResponse.getInstance()) + .toString()) + .isEqualTo("/api/customers"); + assertThat(errorResponse.getProperties()).hasSize(1); + Object violations = errorResponse.getProperties().get("violations"); + assertThat(violations).isNotNull(); + assertThat(violations).isInstanceOf(List.class); + assertThat((List) violations).hasSize(1); + assertThat(((List) violations).getFirst()) + .isInstanceOf(LinkedHashMap.class); + LinkedHashMap violation = + (LinkedHashMap) ((List) violations).getFirst(); + assertThat(violation.get("field")).isEqualTo("text"); + assertThat(violation.get("message")).isEqualTo("Text cannot be empty"); + }); + + SQLStatementCountValidator.assertTotalCount(0); + + assertThat(customerRepository.count()).isEqualTo(3); } @Test void shouldUpdateCustomer() throws Exception { Customer customer = customerList.getFirst(); - customer.setText("Updated Customer"); - - this.mockMvc - .perform( - put("/api/customers/{id}", customer.getId()) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(customer))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.text", is(customer.getText()))); + CustomerRequest customerRequest = new CustomerRequest("Updated Customer", null); + + this.mockMvcTester + .put() + .uri("/api/customers/{id}", customer.getId()) + .content(objectMapper.writeValueAsString(customerRequest)) + .contentType(MediaType.APPLICATION_JSON) + .assertThat() + .hasStatusOk() + .hasContentType(MediaType.APPLICATION_JSON) + .bodyJson() + .convertTo(CustomerResponse.class) + .satisfies( + customerResponse -> { + assertThat(customerResponse.text()).isEqualTo("Updated Customer"); + assertThat(customerResponse.orderResponses()).isEmpty(); + }); + + // select for customer + SQLStatementCountValidator.assertSelectCount(1); + // update for customer table + SQLStatementCountValidator.assertUpdateCount(1); + SQLStatementCountValidator.assertInsertCount(0); + SQLStatementCountValidator.assertDeleteCount(0); + + List orders = new ArrayList<>(); + orders.add(new OrderRequest("First Order", customer.getId())); + orders.add(new OrderRequest("Second Order", customer.getId())); + + customerRequest = new CustomerRequest("Updated Customer1", orders); + + SQLStatementCountValidator.reset(); + + this.mockMvcTester + .put() + .uri("/api/customers/{id}", customer.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(customerRequest)) + .assertThat() + .hasContentType(MediaType.APPLICATION_JSON) + .hasStatusOk() + .bodyJson() + .convertTo(CustomerResponse.class) + .satisfies( + customerResponse -> { + assertThat(customerResponse.text()).isEqualTo("Updated Customer1"); + assertThat(customerResponse.orderResponses()).isNotEmpty().hasSize(2); + }); + // select for customer and 2 for orders sequence + SQLStatementCountValidator.assertSelectCount(3); + // update for customer table + SQLStatementCountValidator.assertUpdateCount(1); + // bulk insert for orders + SQLStatementCountValidator.assertInsertCount(1); + SQLStatementCountValidator.assertDeleteCount(0); + + orders = new ArrayList<>(); + orders.add(new OrderRequest("Third Order", customer.getId())); + orders.add(new OrderRequest("Second Order", customer.getId())); + + customerRequest = new CustomerRequest("Updated Customer1", orders); + + SQLStatementCountValidator.reset(); + + this.mockMvcTester + .put() + .uri("/api/customers/{id}", customer.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(customerRequest)) + .assertThat() + .hasContentType(MediaType.APPLICATION_JSON) + .hasStatusOk() + .bodyJson() + .convertTo(CustomerResponse.class) + .satisfies( + customerResponse -> { + assertThat(customerResponse.text()).isEqualTo("Updated Customer1"); + assertThat(customerResponse.orderResponses()).isNotEmpty().hasSize(2); + }); + // select for customer + SQLStatementCountValidator.assertSelectCount(1); + // update for customer table + SQLStatementCountValidator.assertUpdateCount(0); + // bulk insert for orders + SQLStatementCountValidator.assertInsertCount(1); + // delete for first order + SQLStatementCountValidator.assertDeleteCount(1); } @Test - void shouldDeleteCustomer() throws Exception { + void shouldDeleteCustomer() { Customer customer = customerList.getFirst(); - this.mockMvc - .perform(delete("/api/customers/{id}", customer.getId())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.text", is(customer.getText()))); + this.mockMvcTester + .delete() + .uri("/api/customers/{id}", customer.getId()) + .assertThat() + .hasStatusOk() + .hasContentType(MediaType.APPLICATION_JSON) + .bodyJson() + .convertTo(CustomerResponse.class) + .satisfies( + customerResponse -> + assertThat(customerResponse.text()).isEqualTo(customer.getText())); + + // select for customer + SQLStatementCountValidator.assertSelectCount(1); + SQLStatementCountValidator.assertUpdateCount(0); + SQLStatementCountValidator.assertInsertCount(0); + // delete for customer + SQLStatementCountValidator.assertDeleteCount(1); + + assertThat(customerRepository.findById(customer.getId())).isEmpty(); } } diff --git a/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/CustomerControllerTest.java b/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/CustomerControllerTest.java index 1547a1bbb..30defa286 100644 --- a/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/CustomerControllerTest.java +++ b/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/CustomerControllerTest.java @@ -6,7 +6,6 @@ import static org.hamcrest.Matchers.hasSize; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.doNothing; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -17,6 +16,7 @@ import com.example.custom.sequence.entities.Customer; import com.example.custom.sequence.model.request.CustomerRequest; +import com.example.custom.sequence.model.request.OrderRequest; import com.example.custom.sequence.model.response.CustomerResponse; import com.example.custom.sequence.model.response.PagedResult; import com.example.custom.sequence.services.CustomerService; @@ -116,7 +116,8 @@ void shouldCreateNewCustomer() throws Exception { @Test void shouldReturn400WhenCreateNewCustomerWithoutText() throws Exception { - CustomerRequest customerRequest = new CustomerRequest(null, new ArrayList<>()); + CustomerRequest customerRequest = + new CustomerRequest(null, List.of(new OrderRequest("ORD_1", null))); this.mockMvc .perform( @@ -161,7 +162,8 @@ void shouldUpdateCustomer() throws Exception { String customerId = "CUS_1"; CustomerResponse customerResponse = new CustomerResponse(customerId, "Updated text", List.of()); - CustomerRequest customerRequest = new CustomerRequest("Updated text", List.of()); + CustomerRequest customerRequest = + new CustomerRequest("Updated text", List.of(new OrderRequest("ORD_1", customerId))); given(customerService.updateCustomerById(customerId, customerRequest)) .willReturn(Optional.of(customerResponse)); @@ -174,6 +176,29 @@ void shouldUpdateCustomer() throws Exception { .andExpect(jsonPath("$.text", is(customerResponse.text()))); } + @Test + void shouldReturn400WhenUpdateCustomerWithEmpty() throws Exception { + String customerId = "CUS_1"; + CustomerRequest customerRequest = + new CustomerRequest("Updated text", List.of(new OrderRequest("ORD_1", null))); + + this.mockMvc + .perform( + put("/api/customers/{id}", customerId) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(customerRequest))) + .andExpect(status().isBadRequest()) + .andExpect(header().string("Content-Type", is("application/problem+json"))) + .andExpect(jsonPath("$.type", is("about:blank"))) + .andExpect(jsonPath("$.title", is("Constraint Violation"))) + .andExpect(jsonPath("$.status", is(400))) + .andExpect(jsonPath("$.detail", is("Invalid request content."))) + .andExpect(jsonPath("$.instance", is("/api/customers/CUS_1"))) + .andExpect(jsonPath("$.violations", hasSize(1))) + .andExpect(jsonPath("$.violations[0].field", is("orders[0].customerId"))) + .andExpect(jsonPath("$.violations[0].message", is("CustomerId cannot be blank"))); + } + @Test void shouldReturn404WhenUpdatingNonExistingCustomer() throws Exception { String customerId = "CUS_1"; @@ -191,20 +216,21 @@ void shouldReturn404WhenUpdatingNonExistingCustomer() throws Exception { @Test void shouldDeleteCustomer() throws Exception { String customerId = "CUS_1"; - CustomerResponse customer = new CustomerResponse(customerId, "Some text", List.of()); - given(customerService.findCustomerById(customerId)).willReturn(Optional.of(customer)); - doNothing().when(customerService).deleteCustomerById(customerId); + CustomerResponse customerResponse = + new CustomerResponse(customerId, "Some text", List.of()); + given(customerService.deleteCustomerById(customerId)) + .willReturn(Optional.of(customerResponse)); this.mockMvc .perform(delete("/api/customers/{id}", customerId)) .andExpect(status().isOk()) - .andExpect(jsonPath("$.text", is(customer.text()))); + .andExpect(jsonPath("$.text", is(customerResponse.text()))); } @Test void shouldReturn404WhenDeletingNonExistingCustomer() throws Exception { String customerId = "CUS_1"; - given(customerService.findCustomerById(customerId)).willReturn(Optional.empty()); + given(customerService.deleteCustomerById(customerId)).willReturn(Optional.empty()); this.mockMvc .perform(delete("/api/customers/{id}", customerId)) diff --git a/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/OrderControllerIT.java b/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/OrderControllerIT.java index 108d0a7f0..886e8f357 100644 --- a/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/OrderControllerIT.java +++ b/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/OrderControllerIT.java @@ -1,29 +1,24 @@ package com.example.custom.sequence.web.controllers; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.Matchers.hasLength; -import static org.hamcrest.Matchers.hasSize; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.assertj.core.api.Assertions.assertThat; import com.example.custom.sequence.common.AbstractIntegrationTest; import com.example.custom.sequence.entities.Customer; import com.example.custom.sequence.entities.Order; import com.example.custom.sequence.model.request.OrderRequest; +import com.example.custom.sequence.model.response.OrderResponse; +import com.example.custom.sequence.model.response.PagedResult; import com.example.custom.sequence.repositories.CustomerRepository; import com.example.custom.sequence.repositories.OrderRepository; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Objects; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ProblemDetail; class OrderControllerIT extends AbstractIntegrationTest { @@ -55,107 +50,158 @@ private List createTestOrders(Customer customer) { } @Test - void shouldFetchAllOrders() throws Exception { - this.mockMvc - .perform(get("/api/orders")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.data.size()", is(orderList.size()))) - .andExpect(jsonPath("$.totalElements", is(3))) - .andExpect(jsonPath("$.pageNumber", is(1))) - .andExpect(jsonPath("$.totalPages", is(1))) - .andExpect(jsonPath("$.isFirst", is(true))) - .andExpect(jsonPath("$.isLast", is(true))) - .andExpect(jsonPath("$.hasNext", is(false))) - .andExpect(jsonPath("$.hasPrevious", is(false))); + void shouldFetchAllOrders() { + + this.mockMvcTester + .get() + .uri("/api/orders") + .assertThat() + .hasStatusOk() + .hasContentType(MediaType.APPLICATION_JSON) + .bodyJson() + .convertTo(PagedResult.class) + .satisfies( + pagedResult -> { + assertThat(pagedResult.data()).hasSize(3); + assertThat(pagedResult.totalElements()).isEqualTo(3); + assertThat(pagedResult.pageNumber()).isEqualTo(1); + assertThat(pagedResult.totalPages()).isEqualTo(1); + assertThat(pagedResult.isFirst()).isTrue(); + assertThat(pagedResult.isLast()).isTrue(); + assertThat(pagedResult.hasNext()).isFalse(); + assertThat(pagedResult.hasPrevious()).isFalse(); + }); } @Test - void shouldFindOrderById() throws Exception { + void shouldFindOrderById() { Order order = orderList.getFirst(); String orderId = order.getId(); - this.mockMvc - .perform(get("/api/orders/{id}", orderId)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.id", is(order.getId()), String.class)) - .andExpect(jsonPath("$.text", is(order.getText()))); + this.mockMvcTester + .get() + .uri("/api/orders/{id}", orderId) + .assertThat() + .hasStatusOk() + .hasContentType(MediaType.APPLICATION_JSON) + .bodyJson() + .convertTo(OrderResponse.class) + .satisfies( + orderResponse -> { + assertThat(orderResponse.id()).isEqualTo(orderId); + assertThat(orderResponse.text()).isEqualTo(order.getText()); + }); } @Test void shouldCreateNewOrder() throws Exception { - OrderRequest order = new OrderRequest("New Order", customer.getId()); - this.mockMvc - .perform( - post("/api/orders") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(order))) - .andExpect(status().isCreated()) - .andExpect(jsonPath("$.id", notNullValue(), String.class)) - .andExpect(jsonPath("$.id", hasLength(9))) - .andExpect(jsonPath("$.text", is(order.text()))); - } - - @Test - void shouldReturn400WhenUpdatingOrderWithInvalidCustomerId() throws Exception { - OrderRequest orderRequest = new OrderRequest("Updated Order", "INVALID_ID"); - Order order = orderList.getFirst(); - - this.mockMvc - .perform( - put("/api/orders/{id}", order.getId()) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(orderRequest))) - .andExpect(status().isNotFound()); + OrderRequest orderRequest = new OrderRequest("New Order", customer.getId()); + + this.mockMvcTester + .post() + .uri("/api/orders") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(orderRequest)) + .assertThat() + .hasStatus(HttpStatus.CREATED) + .hasContentType(MediaType.APPLICATION_JSON) + .bodyJson() + .convertTo(OrderResponse.class) + .satisfies( + orderResponse -> { + assertThat(orderResponse.id()).isNotNull().hasSize(9); + assertThat(orderResponse.text()).isEqualTo(orderRequest.text()); + }); } @Test void shouldReturn400WhenCreateNewOrderWithoutText() throws Exception { - OrderRequest order = new OrderRequest(null, "CUS_1"); - - this.mockMvc - .perform( - post("/api/orders") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(order))) - .andExpect(status().isBadRequest()) - .andExpect( - header().string( - HttpHeaders.CONTENT_TYPE, - is(MediaType.APPLICATION_PROBLEM_JSON_VALUE))) - .andExpect(jsonPath("$.type", is("about:blank"))) - .andExpect(jsonPath("$.title", is("Constraint Violation"))) - .andExpect(jsonPath("$.status", is(400))) - .andExpect(jsonPath("$.detail", is("Invalid request content."))) - .andExpect(jsonPath("$.instance", is("/api/orders"))) - .andExpect(jsonPath("$.violations", hasSize(1))) - .andExpect(jsonPath("$.violations[0].field", is("text"))) - .andExpect(jsonPath("$.violations[0].message", is("Text cannot be empty"))) - .andReturn(); + OrderRequest orderRequest = new OrderRequest(null, "CUS_1"); + + this.mockMvcTester + .post() + .uri("/api/orders") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(orderRequest)) + .assertThat() + .hasStatus(HttpStatus.BAD_REQUEST) + .hasContentType(MediaType.APPLICATION_PROBLEM_JSON) + .bodyJson() + .convertTo(ProblemDetail.class) + .satisfies( + problem -> { + assertThat(problem.getType().toString()).isEqualTo("about:blank"); + assertThat(problem.getTitle()).isEqualTo("Constraint Violation"); + assertThat(problem.getStatus()).isEqualTo(400); + assertThat(problem.getDetail()).isEqualTo("Invalid request content."); + assertThat(Objects.requireNonNull(problem.getInstance()).toString()) + .isEqualTo("/api/orders"); + assertThat(problem.getProperties()).hasSize(1); + Object violations = problem.getProperties().get("violations"); + assertThat(violations).isNotNull(); + assertThat(violations).isInstanceOf(List.class); + assertThat((List) violations).hasSize(1); + assertThat(((List) violations).getFirst()) + .isInstanceOf(LinkedHashMap.class); + LinkedHashMap violation = + (LinkedHashMap) ((List) violations).getFirst(); + assertThat(violation.get("field")).isEqualTo("text"); + assertThat(violation.get("message")).isEqualTo("Text cannot be empty"); + }); } @Test void shouldUpdateOrder() throws Exception { OrderRequest orderRequest = new OrderRequest("Updated Order", customer.getId()); + Order order = orderList.getFirst(); + + this.mockMvcTester + .put() + .uri("/api/orders/{id}", order.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(orderRequest)) + .assertThat() + .hasStatus(HttpStatus.OK) + .hasContentType(MediaType.APPLICATION_JSON) + .bodyJson() + .convertTo(OrderResponse.class) + .satisfies( + orderResponse -> { + assertThat(orderResponse.id()).isEqualTo(order.getId()); + assertThat(orderResponse.text()).isEqualTo(orderRequest.text()); + }); + } + @Test + void shouldReturn400WhenUpdatingOrderWithInvalidCustomerId() throws Exception { + OrderRequest orderRequest = new OrderRequest("Updated Order", "INVALID_ID"); Order order = orderList.getFirst(); - this.mockMvc - .perform( - put("/api/orders/{id}", order.getId()) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(orderRequest))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.id", is(order.getId()), String.class)) - .andExpect(jsonPath("$.text", is(orderRequest.text()))); + + this.mockMvcTester + .put() + .uri("/api/orders/{id}", order.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(orderRequest)) + .assertThat() + .hasStatus(HttpStatus.NOT_FOUND); } @Test - void shouldDeleteOrder() throws Exception { + void shouldDeleteOrder() { Order order = orderList.getFirst(); - this.mockMvc - .perform(delete("/api/orders/{id}", order.getId())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.id", is(order.getId()), String.class)) - .andExpect(jsonPath("$.text", is(order.getText()))); + this.mockMvcTester + .delete() + .uri("/api/orders/{id}", order.getId()) + .assertThat() + .hasStatusOk() + .hasContentType(MediaType.APPLICATION_JSON) + .bodyJson() + .convertTo(OrderResponse.class) + .satisfies( + orderResponse -> { + assertThat(orderResponse.id()).isEqualTo(order.getId()); + assertThat(orderResponse.text()).isEqualTo(order.getText()); + }); } } diff --git a/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/OrderControllerTest.java b/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/OrderControllerTest.java index c18dc617c..b276e0bbd 100644 --- a/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/OrderControllerTest.java +++ b/jpa/boot-data-customsequence/src/test/java/com/example/custom/sequence/web/controllers/OrderControllerTest.java @@ -181,13 +181,13 @@ void shouldUpdateOrder() throws Exception { void shouldReturn404WhenUpdatingNonExistingOrder() throws Exception { String orderId = "1"; given(orderService.findOrderById(orderId)).willReturn(Optional.empty()); - Order order = new Order(orderId, "Updated text", customer); + OrderRequest orderRequest = new OrderRequest("Updated text", customer.getId()); this.mockMvc .perform( put("/api/orders/{id}", orderId) .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(order))) + .content(objectMapper.writeValueAsString(orderRequest))) .andExpect(status().isNotFound()); }