Initial API backbone
This commit is contained in:
18
src/main/java/com/rviewer/skeletons/domain/model/Cart.java
Normal file
18
src/main/java/com/rviewer/skeletons/domain/model/Cart.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.rviewer.skeletons.domain.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@With
|
||||
public class Cart {
|
||||
private UUID id;
|
||||
private List<Item> items;
|
||||
}
|
||||
27
src/main/java/com/rviewer/skeletons/domain/model/Item.java
Normal file
27
src/main/java/com/rviewer/skeletons/domain/model/Item.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.rviewer.skeletons.domain.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.With;
|
||||
import org.springframework.format.annotation.NumberFormat;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@With
|
||||
public class Item {
|
||||
@NotNull
|
||||
private UUID id;
|
||||
@NotNull
|
||||
private String name;
|
||||
@NotNull
|
||||
@NumberFormat
|
||||
private Integer quantity;
|
||||
@NotNull
|
||||
@NumberFormat
|
||||
private Float price;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.rviewer.skeletons.domain.ports.primary;
|
||||
|
||||
import com.rviewer.skeletons.domain.model.Cart;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface CartServicePort {
|
||||
Optional<Cart> get(String id);
|
||||
void save(Cart cart);
|
||||
void delete(String id);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.rviewer.skeletons.domain.ports.secondary;
|
||||
|
||||
import com.rviewer.skeletons.domain.model.Cart;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface DatabasePort {
|
||||
|
||||
Optional<Cart> get(String id);
|
||||
void save(Cart cart);
|
||||
void delete(String id);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.rviewer.skeletons.domain.responses;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public class PongResponse {
|
||||
|
||||
private String message;
|
||||
private int number;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.rviewer.skeletons.domain.service;
|
||||
|
||||
import com.rviewer.skeletons.domain.model.Cart;
|
||||
import com.rviewer.skeletons.domain.ports.primary.CartServicePort;
|
||||
import com.rviewer.skeletons.domain.ports.secondary.DatabasePort;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
@Transactional
|
||||
public class CartService implements CartServicePort {
|
||||
private final DatabasePort databasePort;
|
||||
|
||||
public CartService(DatabasePort databasePort) {
|
||||
this.databasePort = databasePort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Cart> get(String id) {
|
||||
return databasePort.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Cart cart) {
|
||||
databasePort.save(cart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
databasePort.delete(id);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.rviewer.skeletons.domain.services;
|
||||
|
||||
import com.rviewer.skeletons.domain.responses.PongResponse;
|
||||
import com.rviewer.skeletons.domain.services.persistence.DatabaseConnector;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class PongService {
|
||||
|
||||
@Autowired
|
||||
DatabaseConnector postgresConnector;
|
||||
|
||||
public PongResponse getPong() {
|
||||
return new PongResponse("pong", postgresConnector.getConnectionStatus());
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.rviewer.skeletons.domain.services.persistence;
|
||||
|
||||
public interface DatabaseConnector {
|
||||
|
||||
public int getConnectionStatus();
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.rviewer.skeletons.infrastructure.controllers;
|
||||
|
||||
import com.rviewer.skeletons.domain.services.PongService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/")
|
||||
public class PingController {
|
||||
|
||||
@Autowired
|
||||
private PongService pongService;
|
||||
|
||||
@GetMapping("/ping")
|
||||
public ResponseEntity getPing() {
|
||||
return ResponseEntity.ok(pongService.getPong());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.rviewer.skeletons.infrastructure.inbound.api.adapter;
|
||||
|
||||
import com.rviewer.skeletons.domain.model.Cart;
|
||||
import com.rviewer.skeletons.domain.model.Item;
|
||||
import com.rviewer.skeletons.domain.service.CartService;
|
||||
import com.rviewer.skeletons.infrastructure.inbound.api.exception.CartAlreadyExistsException;
|
||||
import com.rviewer.skeletons.infrastructure.inbound.api.exception.CartNotFoundException;
|
||||
import com.rviewer.skeletons.infrastructure.inbound.api.request.CreateCartReq;
|
||||
import com.rviewer.skeletons.infrastructure.inbound.api.request.UpdateCartReq;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PatchMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
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;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/carts")
|
||||
public class CartController {
|
||||
|
||||
private final CartService cartService;
|
||||
|
||||
public CartController(CartService cartService) {
|
||||
this.cartService = cartService;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<Cart> getCart(@PathVariable String id) {
|
||||
return ResponseEntity.status(HttpStatus.OK).body(cartService.get(id).orElseThrow(CartNotFoundException::new));
|
||||
}
|
||||
|
||||
@PostMapping("/{id}")
|
||||
public ResponseEntity<HttpStatus> saveCart(@PathVariable String id, @Valid @RequestBody CreateCartReq createCartReq) {
|
||||
if (cartService.get(id).isPresent()) {
|
||||
throw new CartAlreadyExistsException();
|
||||
} else {
|
||||
cartService.save(new Cart(UUID.fromString(id), createCartReq.getItems()
|
||||
.stream()
|
||||
.map(item -> new Item(item.getId(), item.getName(), item.getQuantity(), item.getPrice()))
|
||||
.toList()));
|
||||
return ResponseEntity.status(HttpStatus.CREATED).build();
|
||||
}
|
||||
}
|
||||
|
||||
@PatchMapping("/{id}")
|
||||
public ResponseEntity<HttpStatus> updateCart(@PathVariable String id, @Valid @RequestBody UpdateCartReq updateCartReq) {
|
||||
Cart cart = cartService.get(id).orElseThrow(CartNotFoundException::new);
|
||||
cart.setItems(updateCartReq.getItems());
|
||||
cartService.save(cart);
|
||||
return ResponseEntity.status(HttpStatus.OK).build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<HttpStatus> deleteCart(@PathVariable String id) {
|
||||
if (cartService.get(id).isEmpty()) {
|
||||
throw new CartNotFoundException();
|
||||
} else {
|
||||
cartService.delete(id);
|
||||
return ResponseEntity.status(HttpStatus.OK).build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.rviewer.skeletons.infrastructure.inbound.api.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@ResponseStatus(HttpStatus.CONFLICT)
|
||||
public class CartAlreadyExistsException extends RuntimeException {
|
||||
private static final String CART_ALREADY_EXISTS = "Invalid identifier.";
|
||||
|
||||
public CartAlreadyExistsException() {
|
||||
super(CART_ALREADY_EXISTS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.rviewer.skeletons.infrastructure.inbound.api.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public class CartNotFoundException extends RuntimeException {
|
||||
private static final String CART_NOT_FOUND = "Cart not found for the given ID";
|
||||
|
||||
public CartNotFoundException() {
|
||||
super(CART_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.rviewer.skeletons.infrastructure.inbound.api.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
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.bind.annotation.ResponseStatus;
|
||||
|
||||
@ControllerAdvice
|
||||
public class ControllerExceptionHandler {
|
||||
|
||||
public static final String INVALID_BODY = "Invalid body provided, check the payload.";
|
||||
|
||||
@ExceptionHandler(CartNotFoundException.class)
|
||||
public ResponseEntity<Object> handleCartNotFound(CartNotFoundException e) {
|
||||
return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@ExceptionHandler(CartAlreadyExistsException.class)
|
||||
public ResponseEntity<Object> handleCartAlreadyExists(CartAlreadyExistsException e) {
|
||||
return new ResponseEntity<>(e.getMessage(), HttpStatus.CONFLICT);
|
||||
}
|
||||
|
||||
@ExceptionHandler({MethodArgumentNotValidException.class, HttpMessageNotReadableException.class})
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public ResponseEntity<Object> handleInvalidBody() {
|
||||
return new ResponseEntity<>(INVALID_BODY, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.rviewer.skeletons.infrastructure.inbound.api.request;
|
||||
|
||||
import com.rviewer.skeletons.domain.model.Item;
|
||||
import lombok.Getter;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
public class CreateCartReq {
|
||||
@NotNull
|
||||
private List<Item> items;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.rviewer.skeletons.infrastructure.inbound.api.request;
|
||||
|
||||
import com.rviewer.skeletons.domain.model.Item;
|
||||
import lombok.Getter;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
public class UpdateCartReq {
|
||||
@NotNull
|
||||
private List<Item> items;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.rviewer.skeletons.infrastructure.outbound.database.adapter;
|
||||
|
||||
import com.rviewer.skeletons.domain.model.Cart;
|
||||
import com.rviewer.skeletons.domain.ports.secondary.DatabasePort;
|
||||
import com.rviewer.skeletons.infrastructure.outbound.database.repository.PostgresCartRepository;
|
||||
import com.rviewer.skeletons.infrastructure.outbound.database.dto.PostgresCart;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Component
|
||||
public class PostgresAdapter implements DatabasePort {
|
||||
|
||||
private final PostgresCartRepository postgresCartRepository;
|
||||
|
||||
public PostgresAdapter(PostgresCartRepository postgresCartRepository) {
|
||||
this.postgresCartRepository = postgresCartRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Cart> get(String id) {
|
||||
return postgresCartRepository.findById(UUID.fromString(id)).map(PostgresCart::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Cart cart) {
|
||||
postgresCartRepository.save(PostgresCart.fromDomain(cart));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
postgresCartRepository.deleteById(UUID.fromString(id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.rviewer.skeletons.infrastructure.outbound.database.dto;
|
||||
|
||||
import com.rviewer.skeletons.domain.model.Cart;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.With;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@With
|
||||
public class PostgresCart {
|
||||
@Id
|
||||
private UUID id;
|
||||
@OneToMany(cascade= CascadeType.ALL)
|
||||
private List<PostgresItem> postgresItems;
|
||||
|
||||
public static PostgresCart fromDomain(Cart cart) {
|
||||
return new PostgresCart()
|
||||
.withId(cart.getId())
|
||||
.withPostgresItems(cart.getItems()
|
||||
.stream()
|
||||
.map(PostgresItem::fromDomain)
|
||||
.toList());
|
||||
}
|
||||
|
||||
public Cart toDomain() {
|
||||
return new Cart()
|
||||
.withId(id)
|
||||
.withItems(postgresItems
|
||||
.stream()
|
||||
.map(PostgresItem::toDomain)
|
||||
.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.rviewer.skeletons.infrastructure.outbound.database.dto;
|
||||
|
||||
import com.rviewer.skeletons.domain.model.Item;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.With;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@With
|
||||
public class PostgresItem {
|
||||
@Id
|
||||
private UUID id;
|
||||
private String name;
|
||||
private Integer quantity;
|
||||
private Float price;
|
||||
|
||||
public static PostgresItem fromDomain(Item item) {
|
||||
return new PostgresItem()
|
||||
.withId(item.getId())
|
||||
.withName(item.getName())
|
||||
.withQuantity(item.getQuantity())
|
||||
.withPrice(item.getPrice());
|
||||
}
|
||||
|
||||
public Item toDomain() {
|
||||
return new Item()
|
||||
.withId(id)
|
||||
.withName(name)
|
||||
.withQuantity(quantity)
|
||||
.withPrice(price);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.rviewer.skeletons.infrastructure.outbound.database.repository;
|
||||
|
||||
import com.rviewer.skeletons.infrastructure.outbound.database.dto.PostgresCart;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface PostgresCartRepository extends CrudRepository<PostgresCart, Long> {
|
||||
Optional<PostgresCart> findById(UUID id);
|
||||
void deleteById(UUID id);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.rviewer.skeletons.infrastructure.persistence;
|
||||
|
||||
import com.rviewer.skeletons.domain.services.persistence.DatabaseConnector;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class PostgresConnector implements DatabaseConnector {
|
||||
|
||||
@Autowired
|
||||
JdbcTemplate jdbcTemplate;
|
||||
|
||||
public int getConnectionStatus() {
|
||||
return jdbcTemplate.queryForObject("SELECT 1+1", Integer.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
server.port=8080
|
||||
application.title=Java skeleton
|
||||
application.title=Cartfidential
|
||||
application.version=1.0.0
|
||||
|
||||
spring.datasource.initialization-mode=always
|
||||
spring.datasource.username = rv_user
|
||||
spring.datasource.password = rv_password
|
||||
spring.datasource.driverClassName = org.postgresql.Driver
|
||||
spring.datasource.url = jdbc:postgresql://postgres-skeleton-db:5432/postgres_rv_database
|
||||
spring.jpa.hibernate.ddl-auto = update
|
||||
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
|
||||
spring.datasource.url = jdbc:postgresql://localhost:5432/postgres_rv_database
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.rviewer.skeletons.infrastructure.controllers;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
public class PingControllerTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void getPing_success() throws Exception {
|
||||
mockMvc
|
||||
.perform(get("/ping"))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPing_returnsPong() throws Exception {
|
||||
mockMvc
|
||||
.perform(get("/ping"))
|
||||
.andExpect(content().string(containsString("pong")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
package com.rviewer.skeletons.infrastructure.inbound.api.adapter;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.rviewer.skeletons.domain.model.Cart;
|
||||
import com.rviewer.skeletons.domain.model.Item;
|
||||
import com.rviewer.skeletons.domain.service.CartService;
|
||||
import com.rviewer.skeletons.infrastructure.inbound.api.exception.CartAlreadyExistsException;
|
||||
import com.rviewer.skeletons.infrastructure.inbound.api.exception.CartNotFoundException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.rviewer.skeletons.infrastructure.inbound.api.exception.ControllerExceptionHandler.INVALID_BODY;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
class CartControllerTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
@MockBean
|
||||
private CartService cartService;
|
||||
|
||||
@Test
|
||||
void should_return_cart() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
Item item = new Item(UUID.randomUUID(), "item", 1, 100F);
|
||||
Cart expectedCart = new Cart(id, List.of(item));
|
||||
|
||||
when(cartService.get(id.toString())).thenReturn(Optional.of(expectedCart));
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/carts/" + id))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id").value(id.toString()))
|
||||
.andExpect(jsonPath("$.items[0].id").value(item.getId().toString()))
|
||||
.andExpect(jsonPath("$.items[0].name").value(item.getName()))
|
||||
.andExpect(jsonPath("$.items[0].quantity").value(item.getQuantity()))
|
||||
.andExpect(jsonPath("$.items[0].price").value(item.getPrice()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_return_non_existing_cart() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
|
||||
when(cartService.get(id.toString())).thenReturn(Optional.empty());
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/carts/" + id))
|
||||
.andExpect(status().isNotFound())
|
||||
.andExpect(result -> assertTrue(result.getResolvedException() instanceof CartNotFoundException));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_save_new_cart() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
Item item = new Item(UUID.randomUUID(), "item", 1, 100F);
|
||||
Cart newCart = new Cart(id, List.of(item));
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/carts/" + id)
|
||||
.content(asJsonString(newCart))
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isCreated());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_save_old_cart() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
Item item = new Item(UUID.randomUUID(), "item", 1, 100F);
|
||||
Cart oldCart = new Cart(id, List.of(item));
|
||||
|
||||
when(cartService.get(id.toString())).thenReturn(Optional.of(oldCart));
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/carts/" + id)
|
||||
.content(asJsonString(oldCart))
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isConflict())
|
||||
.andExpect(result -> assertTrue(result.getResolvedException() instanceof CartAlreadyExistsException))
|
||||
;
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_save_null_cart() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
Cart nullCart = new Cart(id, null);
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/carts/" + id)
|
||||
.content(asJsonString(nullCart))
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(result -> assertTrue(result.getResolvedException() instanceof MethodArgumentNotValidException))
|
||||
.andExpect(result -> assertEquals(INVALID_BODY, result.getResponse().getContentAsString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_save_bad_cart() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
Item item = new Item(UUID.randomUUID(), "item", 1, 100F);
|
||||
Cart badCart = new Cart(id, List.of(item));
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/carts/" + id)
|
||||
.content(asJsonString(badCart).replace("100.0", ""))
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(result -> assertTrue(result.getResolvedException() instanceof HttpMessageNotReadableException))
|
||||
.andExpect(result -> assertEquals(INVALID_BODY, result.getResponse().getContentAsString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_update_old_cart() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
Item item = new Item(UUID.randomUUID(), "item", 1, 100F);
|
||||
Cart oldCart = new Cart(id, List.of(item));
|
||||
|
||||
when(cartService.get(id.toString())).thenReturn(Optional.of(oldCart));
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.patch("/carts/" + id)
|
||||
.content(asJsonString(oldCart).replace("100.0", "1000.0"))
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(result -> assertEquals(1000F, cartService.get(id.toString()).orElseThrow().getItems().get(0).getPrice()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_update_non_existing_cart() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
Item item = new Item(UUID.randomUUID(), "item", 1, 100F);
|
||||
Cart nonExistingCart = new Cart(id, List.of(item));
|
||||
|
||||
when(cartService.get(id.toString())).thenReturn(Optional.empty());
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.patch("/carts/" + id)
|
||||
.content(asJsonString(nonExistingCart))
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isNotFound())
|
||||
.andExpect(result -> assertTrue(result.getResolvedException() instanceof CartNotFoundException));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_update_null_cart() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
Cart nullCart = new Cart(id, null);
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.patch("/carts/" + id)
|
||||
.content(asJsonString(nullCart))
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(result -> assertTrue(result.getResolvedException() instanceof MethodArgumentNotValidException))
|
||||
.andExpect(result -> assertEquals(INVALID_BODY, result.getResponse().getContentAsString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_update_bad_cart() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
Item item = new Item(UUID.randomUUID(), "item", 1, 100F);
|
||||
Cart badCart = new Cart(id, List.of(item));
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.patch("/carts/" + id)
|
||||
.content(asJsonString(badCart).replace("100.0", ""))
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(result -> assertTrue(result.getResolvedException() instanceof HttpMessageNotReadableException))
|
||||
.andExpect(result -> assertEquals(INVALID_BODY, result.getResponse().getContentAsString()))
|
||||
;
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_delete_old_cart() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
Item item = new Item(UUID.randomUUID(), "item", 1, 100F);
|
||||
Cart oldCart = new Cart(id, List.of(item));
|
||||
|
||||
when(cartService.get(id.toString())).thenReturn(Optional.of(oldCart));
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.delete("/carts/" + id)
|
||||
.content(asJsonString(oldCart))
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_delete_non_existing_cart() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
Item item = new Item(UUID.randomUUID(), "item", 1, 100F);
|
||||
Cart nonExistingCart = new Cart(id, List.of(item));
|
||||
|
||||
when(cartService.get(id.toString())).thenReturn(Optional.empty());
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.delete("/carts/" + id)
|
||||
.content(asJsonString(nonExistingCart))
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isNotFound())
|
||||
.andExpect(result -> assertTrue(result.getResolvedException() instanceof CartNotFoundException));
|
||||
}
|
||||
|
||||
private static String asJsonString(final Object obj) {
|
||||
try {
|
||||
return new ObjectMapper().writeValueAsString(obj);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.rviewer.skeletons.infrastructure.outbound.adapter;
|
||||
|
||||
import com.rviewer.skeletons.domain.model.Cart;
|
||||
import com.rviewer.skeletons.domain.model.Item;
|
||||
import com.rviewer.skeletons.infrastructure.outbound.database.adapter.PostgresAdapter;
|
||||
import com.rviewer.skeletons.infrastructure.outbound.database.dto.PostgresCart;
|
||||
import com.rviewer.skeletons.infrastructure.outbound.database.repository.PostgresCartRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@SpringBootTest
|
||||
class PostgresAdapterTest {
|
||||
|
||||
@Autowired
|
||||
private PostgresAdapter postgresAdapter;
|
||||
@Mock
|
||||
private PostgresCartRepository postgresCartRepository;
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
postgresAdapter = new PostgresAdapter(postgresCartRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_get_cart() {
|
||||
UUID id = UUID.randomUUID();
|
||||
Item item = new Item(UUID.randomUUID(), "item", 1, 100F);
|
||||
Cart expectedCart = new Cart(id, List.of(item));
|
||||
|
||||
when(postgresCartRepository.findById(id)).thenReturn(Optional.ofNullable(PostgresCart.fromDomain(expectedCart)));
|
||||
|
||||
Optional<Cart> result = postgresAdapter.get(id.toString());
|
||||
assertThat(Optional.of(result)).hasValue(Optional.of(expectedCart));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_save_cart() {
|
||||
UUID id = UUID.randomUUID();
|
||||
Item item = new Item(UUID.randomUUID(), "item", 1, 100F);
|
||||
Cart newCart = new Cart(id, List.of(item));
|
||||
|
||||
postgresAdapter.save(newCart);
|
||||
|
||||
ArgumentCaptor<PostgresCart> captor = ArgumentCaptor.forClass(PostgresCart.class);
|
||||
verify(postgresCartRepository).save(captor.capture());
|
||||
assertEquals(captor.getValue().toDomain(), newCart);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_delete_cart() {
|
||||
UUID id = UUID.randomUUID();
|
||||
Item item = new Item(UUID.randomUUID(), "item", 1, 100F);
|
||||
Cart cartToDelete = new Cart(id, List.of(item));
|
||||
|
||||
when(postgresCartRepository.findById(id)).thenReturn(Optional.ofNullable(PostgresCart.fromDomain(cartToDelete)));
|
||||
|
||||
postgresAdapter.delete(cartToDelete.getId().toString());
|
||||
|
||||
verify(postgresCartRepository, times(1)).deleteById(cartToDelete.getId());
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.rviewer.skeletons.infrastructure.persistence;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@SpringBootTest
|
||||
public class PostgresConnectorTest {
|
||||
|
||||
@Mock
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
@Autowired
|
||||
private PostgresConnector postgresConnector;
|
||||
|
||||
@Test
|
||||
public void getConnectionStatus_shouldReturn2() {
|
||||
when(jdbcTemplate.queryForObject("SELECT 1+1", Integer.class)).thenReturn(2);
|
||||
assertEquals(2, postgresConnector.getConnectionStatus());
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
server.port=8080
|
||||
application.title=Java skeleton
|
||||
application.title=Cartfidential
|
||||
application.version=1.0.0
|
||||
|
||||
spring.datasource.driver-class-name=org.h2.Driver
|
||||
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
|
||||
spring.datasource.url=jdbc:h2:mem:testdb
|
||||
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
|
||||
spring.datasource.username=user
|
||||
spring.datasource.password=password
|
||||
spring.datasource.password=password
|
||||
|
||||
Reference in New Issue
Block a user