Commit ec405098 authored by Vladimir Trubachoff's avatar Vladimir Trubachoff

Added error validation messages, with tests

- Added validation messages
- Change date from String to LocalDate
- Added tests with bad request validation
parent 7c7f55d1
...@@ -2,12 +2,14 @@ package com.example.testj.domain; ...@@ -2,12 +2,14 @@ package com.example.testj.domain;
import jakarta.persistence.*; import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -27,10 +29,10 @@ public class Order { ...@@ -27,10 +29,10 @@ public class Order {
@Column(name = "client") @Column(name = "client")
private String client; private String client;
@NotBlank @NotNull
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
@Column(name = "date") @Column(name = "date")
private String date; private LocalDate date;
@NotBlank @NotBlank
@Column(name = "address") @Column(name = "address")
......
...@@ -22,19 +22,19 @@ public class OrderLine { ...@@ -22,19 +22,19 @@ public class OrderLine {
@Column(name = "id") @Column(name = "id")
private Long id; private Long id;
@NotNull
@JsonIgnore @JsonIgnore
@ManyToOne @ManyToOne
@JoinColumn(name = "order_id") @JoinColumn(name = "order_id")
@NotNull
private Order order; private Order order;
@NotNull
@ManyToOne @ManyToOne
@JoinColumn(name = "goods_id") @JoinColumn(name = "goods_id")
@NotNull
private Goods goods; private Goods goods;
@Column(name = "count")
@NumberFormat @NumberFormat
@Min(1) @Min(1)
@Column(name = "count")
private int count; private int count;
} }
package com.example.testj.exception;
import lombok.Getter;
import lombok.Setter;
import org.springframework.http.HttpStatus;
import java.util.*;
@Getter
@Setter
public class ApiError {
private final Date timestamp;
private HttpStatus status;
private String message;
private List<Map<String, String>> errors;
public ApiError() {
timestamp = new Date();
}
public ApiError(HttpStatus status, String message, List<Map<String, String>> errors) {
super();
timestamp = new Date();
this.status = status;
this.message = message;
this.errors = errors;
}
public ApiError(HttpStatus status, String message, String error) {
super();
timestamp = new Date();
this.status = status;
this.message = message;
errors = new ArrayList<>((Collection) Map.of("message", error));
}
}
\ No newline at end of file
package com.example.testj.exception;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@ControllerAdvice
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {
@ExceptionHandler({ DateTimeParseException.class })
public ResponseEntity<Object> handleDateTimeParseException(Exception ex, WebRequest request) {
List<Map<String, String>> errors = List.of(Map.of("field", "date", "message", ex.getMessage()));
ApiError body = new ApiError(HttpStatus.BAD_REQUEST, "Date parse error", errors);
return new ResponseEntity<>(body, new HttpHeaders(), HttpStatus.BAD_REQUEST);
}
// 500
@ExceptionHandler({ Exception.class })
public ResponseEntity<Object> handleAll(Exception ex, WebRequest request) {
final ApiError apiError = new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, ex.getLocalizedMessage(), "error occurred");
return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
}
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
//Get all fields errors
List<Map<String, String>> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(err -> Map.of("field", err.getField(), "message", err.getDefaultMessage()))
.collect(Collectors.toList());
ApiError body = new ApiError(HttpStatus.BAD_REQUEST, ex.getBody().getDetail(), errors);
return handleExceptionInternal(ex, body, headers, status, request);
}
protected ResponseEntity<Object> handleHttpMessageNotReadable(
HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
ApiError body = new ApiError(HttpStatus.BAD_REQUEST, ex.getMessage(), new ArrayList<>());
return handleExceptionInternal(ex, body, headers, status, request);
}
}
package com.example.testj.service.dto; package com.example.testj.service.dto;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.*; import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDate;
import java.util.List; import java.util.List;
/** /**
...@@ -19,8 +25,9 @@ public class OrderDto implements Serializable { ...@@ -19,8 +25,9 @@ public class OrderDto implements Serializable {
@NotBlank @NotBlank
private String client; private String client;
@NotBlank @NotNull
private String date; @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate date;
@NotBlank @NotBlank
private String address; private String address;
......
This diff is collapsed.
...@@ -7,6 +7,7 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; ...@@ -7,6 +7,7 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
...@@ -27,7 +28,7 @@ public class OrderRepositoryTest { ...@@ -27,7 +28,7 @@ public class OrderRepositoryTest {
Order order = new Order(); Order order = new Order();
order.setClient("Client 1"); order.setClient("Client 1");
order.setAddress("Address 1"); order.setAddress("Address 1");
order.setDate("2000-01-01"); order.setDate(LocalDate.of(2000, 1, 1));
// when // when
orderRepository.save(order); orderRepository.save(order);
// then // then
...@@ -61,7 +62,7 @@ public class OrderRepositoryTest { ...@@ -61,7 +62,7 @@ public class OrderRepositoryTest {
Order order = entityManager.find(Order.class, 1L); Order order = entityManager.find(Order.class, 1L);
order.setAddress("Address 2"); order.setAddress("Address 2");
order.setClient("Client 2"); order.setClient("Client 2");
order.setDate("2000-01-01"); order.setDate(LocalDate.of(2002, 2, 2));
// when // when
Order saved = orderRepository.save(order); Order saved = orderRepository.save(order);
// then // then
......
...@@ -18,6 +18,7 @@ import org.mockito.Mock; ...@@ -18,6 +18,7 @@ import org.mockito.Mock;
import org.mockito.Spy; import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.time.LocalDate;
import java.util.Optional; import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
...@@ -52,7 +53,7 @@ public class OrderLineServiceTest { ...@@ -52,7 +53,7 @@ public class OrderLineServiceTest {
@BeforeAll @BeforeAll
static void init() { static void init() {
orderDto = new OrderDto(1L, "client 1", "20-20-2020", "address 1", null); orderDto = new OrderDto(1L, "client 1", LocalDate.of(2020, 1, 20), "address 1", null);
goodsDto = new GoodsDto(1L, "product 1", 10.99); goodsDto = new GoodsDto(1L, "product 1", 10.99);
orderLine = new OrderLine( orderLine = new OrderLine(
1L, 1L,
......
...@@ -19,6 +19,7 @@ import org.springframework.data.domain.PageImpl; ...@@ -19,6 +19,7 @@ import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PagedModel; import org.springframework.data.web.PagedModel;
import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
...@@ -46,13 +47,13 @@ public class OrderServiceTest { ...@@ -46,13 +47,13 @@ public class OrderServiceTest {
order.setId(1L); order.setId(1L);
order.setClient("client 1"); order.setClient("client 1");
order.setAddress("address 1"); order.setAddress("address 1");
order.setDate("20-20-2020"); order.setDate(LocalDate.of(2020, 1, 1));
Order order1 = new Order(); Order order1 = new Order();
order1.setId(2L); order1.setId(2L);
order1.setClient("client 2"); order1.setClient("client 2");
order1.setAddress("address 2"); order1.setAddress("address 2");
order1.setDate("10-10-2021"); order1.setDate(LocalDate.of(2020, 2, 20));
ordersList = Stream.of( ordersList = Stream.of(
order, order,
...@@ -63,7 +64,7 @@ public class OrderServiceTest { ...@@ -63,7 +64,7 @@ public class OrderServiceTest {
@Test @Test
void testCreateOrUpdateOrder() { void testCreateOrUpdateOrder() {
// given // given
Order order = new Order(null, "client new", "01-01-2021", "address new", null); Order order = new Order(null, "client new", LocalDate.of(2021, 1, 1), "address new", null);
Order result = new Order( Order result = new Order(
ordersList.getLast().getId() + 1, ordersList.getLast().getId() + 1,
...@@ -84,7 +85,7 @@ public class OrderServiceTest { ...@@ -84,7 +85,7 @@ public class OrderServiceTest {
@Disabled @Disabled
void testSaveOrderWithEmptyClient() { void testSaveOrderWithEmptyClient() {
// given // given
Order order = new Order(null, "", "01-01-2020", "address new", null); Order order = new Order(null, "", LocalDate.of(2020, 1, 1), "address new", null);
// when // when
assertThrows(ConstraintViolationException.class, () -> orderService.createOrUpdateOrder(order)); assertThrows(ConstraintViolationException.class, () -> orderService.createOrUpdateOrder(order));
// then // then
...@@ -95,7 +96,7 @@ public class OrderServiceTest { ...@@ -95,7 +96,7 @@ public class OrderServiceTest {
@Disabled @Disabled
void testSaveOrderWithNullName() { void testSaveOrderWithNullName() {
// given // given
Order order = new Order(null, null, "01-01-2020", "address", null); Order order = new Order(null, null, LocalDate.of(2020, 1, 1), "address", null);
// when // when
assertThrows(ConstraintViolationException.class, () -> orderService.createOrUpdateOrder(order)); assertThrows(ConstraintViolationException.class, () -> orderService.createOrUpdateOrder(order));
// then // then
...@@ -133,7 +134,7 @@ public class OrderServiceTest { ...@@ -133,7 +134,7 @@ public class OrderServiceTest {
Order order = ordersList.getFirst(); Order order = ordersList.getFirst();
order.setClient("client new"); order.setClient("client new");
order.setAddress("address new"); order.setAddress("address new");
order.setDate("02-02-2020"); order.setDate(LocalDate.of(2020, 2, 2));
// when // when
when(orderRepository.save(order)).thenReturn(order); when(orderRepository.save(order)).thenReturn(order);
OrderDto updated = orderService.createOrUpdateOrder(order); OrderDto updated = orderService.createOrUpdateOrder(order);
......
...@@ -5,6 +5,7 @@ import com.example.testj.service.GoodsService; ...@@ -5,6 +5,7 @@ import com.example.testj.service.GoodsService;
import com.example.testj.service.dto.GoodsDto; import com.example.testj.service.dto.GoodsDto;
import com.example.testj.service.mapper.GoodsMapper; import com.example.testj.service.mapper.GoodsMapper;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.hamcrest.Matchers;
import org.json.JSONObject; import org.json.JSONObject;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
...@@ -109,7 +110,9 @@ public class GoodsControllerTest { ...@@ -109,7 +110,9 @@ public class GoodsControllerTest {
JSONObject bodyJson = new JSONObject("{'name': null, 'price': 0}"); JSONObject bodyJson = new JSONObject("{'name': null, 'price': 0}");
mvc.perform(post("/api/goods").contentType(MediaType.APPLICATION_JSON).content(bodyJson.toString())) mvc.perform(post("/api/goods").contentType(MediaType.APPLICATION_JSON).content(bodyJson.toString()))
.andExpect(status().isBadRequest()); .andExpect(status().isBadRequest())
.andExpect(jsonPath("$.errors[*].field").value(
Matchers.containsInAnyOrder("name", "price")));
} }
@Test @Test
......
...@@ -4,6 +4,8 @@ import com.example.testj.service.OrderService; ...@@ -4,6 +4,8 @@ import com.example.testj.service.OrderService;
import com.example.testj.service.dto.OrderDto; import com.example.testj.service.dto.OrderDto;
import com.example.testj.service.mapper.OrderMapper; import com.example.testj.service.mapper.OrderMapper;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.hamcrest.Matchers;
import org.json.JSONObject;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
...@@ -18,6 +20,7 @@ import org.springframework.data.web.PagedModel; ...@@ -18,6 +20,7 @@ import org.springframework.data.web.PagedModel;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
...@@ -34,6 +37,9 @@ public class OrderControllerTest { ...@@ -34,6 +37,9 @@ public class OrderControllerTest {
@Autowired @Autowired
private MockMvc mvc; private MockMvc mvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean @MockBean
private OrderService orderService; private OrderService orderService;
...@@ -46,10 +52,10 @@ public class OrderControllerTest { ...@@ -46,10 +52,10 @@ public class OrderControllerTest {
static void init() { static void init() {
ordersList = Stream.of( ordersList = Stream.of(
new OrderDto( new OrderDto(
1L, "client 1", "20-20-2020", "address 1", null 1L, "client 1", LocalDate.of(2020, 1, 20), "address 1", null
), ),
new OrderDto( new OrderDto(
2L, "client 2", "20-20-2020", "address 2", null 2L, "client 2", LocalDate.of(2020, 1, 21), "address 2", null
) )
).toList(); ).toList();
} }
...@@ -83,9 +89,9 @@ public class OrderControllerTest { ...@@ -83,9 +89,9 @@ public class OrderControllerTest {
@DisplayName("POST /api/orders [201 Created]") @DisplayName("POST /api/orders [201 Created]")
void testCreateOrder() throws Exception { void testCreateOrder() throws Exception {
OrderDto orderDto = new OrderDto( OrderDto orderDto = new OrderDto(
null, "client new", "21-21-2021", "address new", null null, "client new", LocalDate.of(2021, 1, 21), "address new", null
); );
String body = new ObjectMapper().writeValueAsString(orderDto); String body = objectMapper.writeValueAsString(orderDto);
Long nextId = (ordersList.getLast().getId() + 1); Long nextId = (ordersList.getLast().getId() + 1);
orderDto.setId(nextId); orderDto.setId(nextId);
...@@ -96,7 +102,18 @@ public class OrderControllerTest { ...@@ -96,7 +102,18 @@ public class OrderControllerTest {
.andExpect(jsonPath("$.id").isNumber()) .andExpect(jsonPath("$.id").isNumber())
.andExpect(jsonPath("$.client").value(orderDto.getClient())) .andExpect(jsonPath("$.client").value(orderDto.getClient()))
.andExpect(jsonPath("$.address").value(orderDto.getAddress())) .andExpect(jsonPath("$.address").value(orderDto.getAddress()))
.andExpect(jsonPath("$.date").value(orderDto.getDate())).andReturn(); .andExpect(jsonPath("$.date").value(orderDto.getDate().toString())).andReturn();
}
@Test
@DisplayName("POST /api/orders [400 Bad Request]")
void testCreateOrderBadRequest() throws Exception {
JSONObject bodyJson = new JSONObject("{'client': null, 'address': '', 'date': ''}");
mvc.perform(post("/api/orders").contentType(MediaType.APPLICATION_JSON).content(bodyJson.toString()))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.errors[*].field").value(
Matchers.containsInAnyOrder("client", "address", "date")));
} }
@Test @Test
...@@ -104,9 +121,9 @@ public class OrderControllerTest { ...@@ -104,9 +121,9 @@ public class OrderControllerTest {
void testUpdateOrder() throws Exception { void testUpdateOrder() throws Exception {
Long id = 1L; Long id = 1L;
OrderDto orderDto = new OrderDto( OrderDto orderDto = new OrderDto(
id, "client new", "21-21-2021", "address new", null id, "client new", LocalDate.of(2021,1,21), "address new", null
); );
String body = new ObjectMapper().writeValueAsString(orderDto); String body = objectMapper.writeValueAsString(orderDto);
when(orderService.getOrder(id)).thenReturn(ordersList.getFirst()); when(orderService.getOrder(id)).thenReturn(ordersList.getFirst());
when(orderService.createOrUpdateOrder(orderMapper.toEntity(orderDto))).thenReturn(orderDto); when(orderService.createOrUpdateOrder(orderMapper.toEntity(orderDto))).thenReturn(orderDto);
...@@ -117,7 +134,7 @@ public class OrderControllerTest { ...@@ -117,7 +134,7 @@ public class OrderControllerTest {
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("$.client").value(orderDto.getClient())) .andExpect(jsonPath("$.client").value(orderDto.getClient()))
.andExpect(jsonPath("$.address").value(orderDto.getAddress())) .andExpect(jsonPath("$.address").value(orderDto.getAddress()))
.andExpect(jsonPath("$.date").value(orderDto.getDate())); .andExpect(jsonPath("$.date").value(orderDto.getDate().toString()));
} }
@Test @Test
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment