From e7e86b98acff1ebe7a42c61abca0a028c56b8ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Pavlica?= Date: Wed, 20 Apr 2022 20:54:30 +0200 Subject: [PATCH] Add base mappers, add Order and OrderItem mappers, add base tests, add OrderService tests --- .../fi/pa165/winery/dto/order/OrderDto.java | 4 +- .../pa165/winery/dto/order/OrderItemDto.java | 2 +- .../pa165/winery/dto/wine/WineBottleDto.java | 5 + .../services/order/OrderItemService.java | 7 - .../fi/pa165/winery/WineryApplication.java | 14 -- .../persistence/entities/WineBottle.java | 12 -- .../pa165/winery/WineryApplicationTests.java | 13 -- .../persistence/entity/OrderItemTests.java | 5 - winery/service/pom.xml | 42 ++++++ .../services/PersistenceServiceImpl.java | 33 ++--- .../winery/services/mappers/EntityMapper.java | 8 ++ .../services/mappers/EntityMapperImpl.java | 38 +++++ .../mappers/order/OrderEntityMapper.java | 26 ++++ .../mappers/order/OrderItemEntityMapper.java | 37 +++++ .../mappers/wine/WineBottleEntityMapper.java | 13 ++ .../services/order/OrderItemServiceImpl.java | 25 +--- .../services/order/OrderServiceImpl.java | 66 ++++++--- .../services/wine/WineBottleServiceImpl.java | 5 +- .../winery/services/ServiceTestConfig.java | 59 ++++++++ .../PersistenceServiceTestBase.java | 95 ++++++++++++ .../persistence/order/OrderServiceTests.java | 135 ++++++++++++++++++ .../winery/webapp/WebappApplication.java | 2 + .../webapp/controllers/HomeController.java | 9 ++ 23 files changed, 548 insertions(+), 107 deletions(-) delete mode 100644 winery/dao/src/main/java/cz/muni/fi/pa165/winery/WineryApplication.java delete mode 100644 winery/dao/src/test/java/cz/muni/fi/pa165/winery/WineryApplicationTests.java create mode 100644 winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/EntityMapper.java create mode 100644 winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/EntityMapperImpl.java create mode 100644 winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/order/OrderEntityMapper.java create mode 100644 winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/order/OrderItemEntityMapper.java create mode 100644 winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/wine/WineBottleEntityMapper.java create mode 100644 winery/service/src/test/java/cz/muni/fi/pa165/winery/services/ServiceTestConfig.java create mode 100644 winery/service/src/test/java/cz/muni/fi/pa165/winery/services/persistence/PersistenceServiceTestBase.java create mode 100644 winery/service/src/test/java/cz/muni/fi/pa165/winery/services/persistence/order/OrderServiceTests.java diff --git a/winery/api/src/main/java/cz/muni/fi/pa165/winery/dto/order/OrderDto.java b/winery/api/src/main/java/cz/muni/fi/pa165/winery/dto/order/OrderDto.java index 7c6b98a..2c4c9b1 100644 --- a/winery/api/src/main/java/cz/muni/fi/pa165/winery/dto/order/OrderDto.java +++ b/winery/api/src/main/java/cz/muni/fi/pa165/winery/dto/order/OrderDto.java @@ -8,7 +8,9 @@ import lombok.Setter; import javax.validation.constraints.NotNull; import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; @Getter @Setter @@ -35,5 +37,5 @@ public class OrderDto extends PersistentDtoBase { /** * The order items */ - private List items = new ArrayList<>(); + private Set items = new HashSet<>(); } diff --git a/winery/api/src/main/java/cz/muni/fi/pa165/winery/dto/order/OrderItemDto.java b/winery/api/src/main/java/cz/muni/fi/pa165/winery/dto/order/OrderItemDto.java index 683806a..cb63f7c 100644 --- a/winery/api/src/main/java/cz/muni/fi/pa165/winery/dto/order/OrderItemDto.java +++ b/winery/api/src/main/java/cz/muni/fi/pa165/winery/dto/order/OrderItemDto.java @@ -30,5 +30,5 @@ public class OrderItemDto extends PersistentDtoBase { /** * The ordered wine bottle. Can be null if not explicitly loaded. */ - private WineBottleDto bottle; + private WineBottleDto wineBottle; } diff --git a/winery/api/src/main/java/cz/muni/fi/pa165/winery/dto/wine/WineBottleDto.java b/winery/api/src/main/java/cz/muni/fi/pa165/winery/dto/wine/WineBottleDto.java index c4e0924..220ed3c 100644 --- a/winery/api/src/main/java/cz/muni/fi/pa165/winery/dto/wine/WineBottleDto.java +++ b/winery/api/src/main/java/cz/muni/fi/pa165/winery/dto/wine/WineBottleDto.java @@ -1,7 +1,12 @@ package cz.muni.fi.pa165.winery.dto.wine; import cz.muni.fi.pa165.winery.dto.PersistentDtoBase; +import lombok.Getter; +import lombok.Setter; // TODO +@Getter +@Setter public class WineBottleDto extends PersistentDtoBase { + private String name; } diff --git a/winery/api/src/main/java/cz/muni/fi/pa165/winery/services/order/OrderItemService.java b/winery/api/src/main/java/cz/muni/fi/pa165/winery/services/order/OrderItemService.java index 3498ca6..78ea30b 100644 --- a/winery/api/src/main/java/cz/muni/fi/pa165/winery/services/order/OrderItemService.java +++ b/winery/api/src/main/java/cz/muni/fi/pa165/winery/services/order/OrderItemService.java @@ -5,11 +5,4 @@ import cz.muni.fi.pa165.winery.services.PersistenceService; public interface OrderItemService extends PersistenceService { - /*** - * Returns a record with the given ID from the database. Can optionally load the order item's bottle data. - * @param id Primary key. - * @param loadBottleData Whether to load the bottle data. - * @return A record with the given primary key or null if the record is not found. - */ - OrderItemDto get(long id, boolean loadBottleData); } diff --git a/winery/dao/src/main/java/cz/muni/fi/pa165/winery/WineryApplication.java b/winery/dao/src/main/java/cz/muni/fi/pa165/winery/WineryApplication.java deleted file mode 100644 index b0e9242..0000000 --- a/winery/dao/src/main/java/cz/muni/fi/pa165/winery/WineryApplication.java +++ /dev/null @@ -1,14 +0,0 @@ -package cz.muni.fi.pa165.winery; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.ComponentScan; - -@SpringBootApplication -public class WineryApplication { - - public static void main(String[] args) { - SpringApplication.run(WineryApplication.class, args); - } - -} diff --git a/winery/dao/src/main/java/cz/muni/fi/pa165/winery/persistence/entities/WineBottle.java b/winery/dao/src/main/java/cz/muni/fi/pa165/winery/persistence/entities/WineBottle.java index d64c864..e160868 100644 --- a/winery/dao/src/main/java/cz/muni/fi/pa165/winery/persistence/entities/WineBottle.java +++ b/winery/dao/src/main/java/cz/muni/fi/pa165/winery/persistence/entities/WineBottle.java @@ -70,16 +70,4 @@ public class WineBottle extends EntityBase{ @ManyToOne @NotNull private Grape grape; - - /** - * attribute of type Set contains items in orders, where item is this wine bottle - */ - @OneToMany(mappedBy = "bottle") - private Set items = new HashSet<>(); - - /** - * attribute of type Set contains reviews - */ - @OneToMany(mappedBy = "wineBottle") - private Set reviews = new HashSet<>(); } diff --git a/winery/dao/src/test/java/cz/muni/fi/pa165/winery/WineryApplicationTests.java b/winery/dao/src/test/java/cz/muni/fi/pa165/winery/WineryApplicationTests.java deleted file mode 100644 index 8f27124..0000000 --- a/winery/dao/src/test/java/cz/muni/fi/pa165/winery/WineryApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package cz.muni.fi.pa165.winery; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class WineryApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/winery/dao/src/test/java/cz/muni/fi/pa165/winery/persistence/entity/OrderItemTests.java b/winery/dao/src/test/java/cz/muni/fi/pa165/winery/persistence/entity/OrderItemTests.java index 0cdee97..f60f05a 100644 --- a/winery/dao/src/test/java/cz/muni/fi/pa165/winery/persistence/entity/OrderItemTests.java +++ b/winery/dao/src/test/java/cz/muni/fi/pa165/winery/persistence/entity/OrderItemTests.java @@ -109,9 +109,6 @@ public class OrderItemTests { var retrievedOrder = em.find(Order.class, order.getId()); Assertions.assertEquals(1, retrievedOrder.getItems().size()); Assertions.assertEquals(retrievedOrderItem.getId(), retrievedOrder.getItems().iterator().next().getId()); - var retrievedBottle = em.find(WineBottle.class, bottle.getId()); - Assertions.assertEquals(1, retrievedBottle.getItems().size()); - Assertions.assertEquals(retrievedOrderItem.getId(), retrievedBottle.getItems().iterator().next().getId()); // equals working correctly var sameItem = testOrderItem; @@ -133,8 +130,6 @@ public class OrderItemTests { retrievedOrder = em2.find(Order.class, order.getId()); Assertions.assertEquals(2, retrievedOrder.getItems().size()); - retrievedBottle = em2.find(WineBottle.class, bottle.getId()); - Assertions.assertEquals(2, retrievedBottle.getItems().size()); em2.getTransaction().commit(); em2.close(); diff --git a/winery/service/pom.xml b/winery/service/pom.xml index 04182f7..875165d 100644 --- a/winery/service/pom.xml +++ b/winery/service/pom.xml @@ -13,6 +13,18 @@ cz.muni.fi.pa165.winery service 0.0.1-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + service service @@ -70,6 +82,36 @@ 3.22.0 test + + org.mockito + mockito-core + 4.5.0 + test + + + org.mockito + mockito-junit-jupiter + 4.5.0 + test + + + net.bytebuddy + byte-buddy + 1.12.9 + compile + + + net.bytebuddy + byte-buddy-agent + 1.12.9 + compile + + + org.objenesis + objenesis + 3.2 + runtime + \ No newline at end of file diff --git a/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/PersistenceServiceImpl.java b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/PersistenceServiceImpl.java index e42d93a..65eecec 100644 --- a/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/PersistenceServiceImpl.java +++ b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/PersistenceServiceImpl.java @@ -3,28 +3,36 @@ package cz.muni.fi.pa165.winery.services; import cz.muni.fi.pa165.winery.dto.PersistentDtoBase; import cz.muni.fi.pa165.winery.persistence.dao.DaoBase; import cz.muni.fi.pa165.winery.persistence.entities.EntityBase; +import cz.muni.fi.pa165.winery.services.mappers.EntityMapper; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import org.springframework.ui.ModelMap; +import javax.transaction.Transactional; import java.util.List; import java.util.stream.Collectors; -public class PersistenceServiceImpl implements PersistenceService { +@Component +public abstract class PersistenceServiceImpl implements PersistenceService { private final Class dtoClass; private final Class entityClass; private final DaoBase dao; + private final EntityMapper mapper; - public PersistenceServiceImpl(Class dtoClass, Class entityClass, DaoBase dao) { + public PersistenceServiceImpl(Class dtoClass, Class entityClass, DaoBase dao, EntityMapper mapper) { this.dtoClass = dtoClass; this.entityClass = entityClass; this.dao = dao; + this.mapper = mapper; } @Override + @Transactional public void create(DTO record) { if (record == null) { throw new IllegalArgumentException("The supplied record is null."); @@ -34,18 +42,21 @@ public class PersistenceServiceImpl getAll() { var entities = dao.getAll(); return entities.stream().map(this::mapToDto).collect(Collectors.toList()); } @Override + @Transactional public void update(DTO record) { if (record == null) { throw new IllegalArgumentException("The supplied record is null."); @@ -55,6 +66,7 @@ public class PersistenceServiceImpl { + DTO mapToDto(ENTITY entity); + ENTITY mapToEntity(DTO record); +} diff --git a/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/EntityMapperImpl.java b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/EntityMapperImpl.java new file mode 100644 index 0000000..862e1b9 --- /dev/null +++ b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/EntityMapperImpl.java @@ -0,0 +1,38 @@ +package cz.muni.fi.pa165.winery.services.mappers; + +import cz.muni.fi.pa165.winery.persistence.entities.EntityBase; +import org.modelmapper.ModelMapper; + +public abstract class EntityMapperImpl implements EntityMapper{ + + private final Class dtoClass; + private final Class entityClass; + + public EntityMapperImpl(Class dtoClass, Class entityClass) { + + this.dtoClass = dtoClass; + this.entityClass = entityClass; + } + + @Override + public DTO mapToDto(ENTITY entity) { + var mapper = getModelMapper(); + return mapper.map(entity, dtoClass); + } + + @Override + public ENTITY mapToEntity(DTO record) { + var mapper= getModelMapper(); + return mapper.map(record, entityClass); + } + + protected void configureModelMapper(ModelMapper mapper) { + + } + + private ModelMapper getModelMapper() { + var mapper = new ModelMapper(); + configureModelMapper(mapper); + return mapper; + } +} diff --git a/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/order/OrderEntityMapper.java b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/order/OrderEntityMapper.java new file mode 100644 index 0000000..30a83ee --- /dev/null +++ b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/order/OrderEntityMapper.java @@ -0,0 +1,26 @@ +package cz.muni.fi.pa165.winery.services.mappers.order; + +import cz.muni.fi.pa165.winery.dto.order.OrderDto; +import cz.muni.fi.pa165.winery.persistence.entities.Order; +import cz.muni.fi.pa165.winery.services.mappers.EntityMapper; +import cz.muni.fi.pa165.winery.services.mappers.EntityMapperImpl; +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Service; + +import javax.print.attribute.standard.Destination; + +@Service +public class OrderEntityMapper extends EntityMapperImpl { + public OrderEntityMapper() { + super(OrderDto.class, Order.class); + } + + @Override + protected void configureModelMapper(ModelMapper mapper) { + super.configureModelMapper(mapper); + mapper.createTypeMap(Order.class, OrderDto.class) + .addMappings(m -> m.skip(OrderDto::setItems)); + mapper.createTypeMap(OrderDto.class, Order.class) + .addMappings(m -> m.skip(Order::setItems)); + } +} diff --git a/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/order/OrderItemEntityMapper.java b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/order/OrderItemEntityMapper.java new file mode 100644 index 0000000..4860b8c --- /dev/null +++ b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/order/OrderItemEntityMapper.java @@ -0,0 +1,37 @@ +package cz.muni.fi.pa165.winery.services.mappers.order; + +import cz.muni.fi.pa165.winery.dto.order.OrderItemDto; +import cz.muni.fi.pa165.winery.persistence.entities.OrderItem; +import cz.muni.fi.pa165.winery.persistence.entities.WineBottle; +import cz.muni.fi.pa165.winery.services.mappers.EntityMapperImpl; +import org.modelmapper.ModelMapper; +import org.modelmapper.convention.MatchingStrategies; +import org.springframework.stereotype.Service; + +@Service +public class OrderItemEntityMapper extends EntityMapperImpl { + public OrderItemEntityMapper() { + super(OrderItemDto.class, OrderItem.class); + } + + @Override + protected void configureModelMapper(ModelMapper mapper) { + super.configureModelMapper(mapper); + mapper.getConfiguration().setAmbiguityIgnored(true); + mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); + mapper.createTypeMap(OrderItemDto.class, OrderItem.class) + .addMappings(m -> m.skip(OrderItem::setBottle)); + mapper.createTypeMap(OrderItem.class, OrderItemDto.class) + .addMappings(m -> m.skip(OrderItemDto::setWineBottle)) + .addMappings(m -> m.map(src -> src.getBottle().getId(), OrderItemDto::setBottleId)); + } + + @Override + public OrderItem mapToEntity(OrderItemDto record) { + var entity = super.mapToEntity(record); + var bottle = new WineBottle(); + bottle.setId(record.getBottleId()); + entity.setBottle(bottle); + return entity; + } +} diff --git a/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/wine/WineBottleEntityMapper.java b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/wine/WineBottleEntityMapper.java new file mode 100644 index 0000000..ae72942 --- /dev/null +++ b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/mappers/wine/WineBottleEntityMapper.java @@ -0,0 +1,13 @@ +package cz.muni.fi.pa165.winery.services.mappers.wine; + +import cz.muni.fi.pa165.winery.dto.wine.WineBottleDto; +import cz.muni.fi.pa165.winery.persistence.entities.WineBottle; +import cz.muni.fi.pa165.winery.services.mappers.EntityMapperImpl; +import org.springframework.stereotype.Service; + +@Service +public class WineBottleEntityMapper extends EntityMapperImpl { + public WineBottleEntityMapper() { + super(WineBottleDto.class, WineBottle.class); + } +} diff --git a/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/order/OrderItemServiceImpl.java b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/order/OrderItemServiceImpl.java index 0b5d08c..aff185f 100644 --- a/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/order/OrderItemServiceImpl.java +++ b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/order/OrderItemServiceImpl.java @@ -1,31 +1,18 @@ package cz.muni.fi.pa165.winery.services.order; import cz.muni.fi.pa165.winery.dto.order.OrderItemDto; -import cz.muni.fi.pa165.winery.persistence.dao.order.OrderDao; -import cz.muni.fi.pa165.winery.persistence.entities.Order; +import cz.muni.fi.pa165.winery.persistence.dao.order.OrderItemDao; +import cz.muni.fi.pa165.winery.persistence.entities.OrderItem; import cz.muni.fi.pa165.winery.services.PersistenceServiceImpl; -import cz.muni.fi.pa165.winery.services.wine.WineBottleService; +import cz.muni.fi.pa165.winery.services.mappers.EntityMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service -public class OrderItemServiceImpl extends PersistenceServiceImpl implements OrderItemService { - - private final WineBottleService wineBottleService; +public class OrderItemServiceImpl extends PersistenceServiceImpl implements OrderItemService { @Autowired - public OrderItemServiceImpl(OrderDao orderDao, WineBottleService wineBottleService) { - super(OrderItemDto.class, Order.class, orderDao); - this.wineBottleService = wineBottleService; - } - - @Override - public OrderItemDto get(long id, boolean loadBottleData) { - var record = get(id); - if (!loadBottleData) { - return record; - } - record.setBottle(wineBottleService.get(record.getBottleId())); - return record; + public OrderItemServiceImpl(OrderItemDao orderItemDao, EntityMapper mapper) { + super(OrderItemDto.class, OrderItem.class, orderItemDao, mapper); } } diff --git a/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/order/OrderServiceImpl.java b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/order/OrderServiceImpl.java index 44e7787..a31d9dc 100644 --- a/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/order/OrderServiceImpl.java +++ b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/order/OrderServiceImpl.java @@ -1,41 +1,73 @@ package cz.muni.fi.pa165.winery.services.order; import cz.muni.fi.pa165.winery.dto.order.OrderDto; -import cz.muni.fi.pa165.winery.persistence.dao.DaoBase; +import cz.muni.fi.pa165.winery.dto.order.OrderItemDto; +import cz.muni.fi.pa165.winery.dto.wine.WineBottleDto; import cz.muni.fi.pa165.winery.persistence.dao.order.OrderDao; import cz.muni.fi.pa165.winery.persistence.entities.Order; +import cz.muni.fi.pa165.winery.persistence.entities.OrderItem; +import cz.muni.fi.pa165.winery.persistence.entities.WineBottle; import cz.muni.fi.pa165.winery.services.PersistenceServiceImpl; -import org.modelmapper.ModelMapper; +import cz.muni.fi.pa165.winery.services.mappers.EntityMapper; import org.springframework.stereotype.Service; -import java.util.List; +import java.util.stream.Collectors; @Service public class OrderServiceImpl extends PersistenceServiceImpl implements OrderService { - private final OrderItemService orderItemService; - public OrderServiceImpl(OrderDao orderDao, OrderItemService orderItemService) { - super(OrderDto.class, Order.class, orderDao); - this.orderItemService = orderItemService; + private final OrderDao orderDao; + private final EntityMapper orderItemMapper; + private final EntityMapper wineBottleMapper; + + public OrderServiceImpl( + OrderDao orderDao, + EntityMapper orderMapper, + EntityMapper orderItemMapper, + EntityMapper wineBottleMapper) { + super(OrderDto.class, Order.class, orderDao, orderMapper); + this.orderDao = orderDao; + this.orderItemMapper = orderItemMapper; + this.wineBottleMapper = wineBottleMapper; } @Override public OrderDto get(long id, boolean loadBottleData) { - var record = get(id); - - // TODO - orderItemService.get(id, loadBottleData); - if (!loadBottleData) { - return record; + return get(id); } - //record.setBottle(wineBottleService.get(record.getBottleId())); - return record; + + var entity = orderDao.get(id); + if (entity == null) { + return null; + } + + return mapToDtoWithBottles(entity); } @Override - protected void configureModelMapper(ModelMapper mapper) { - super.configureModelMapper(mapper); + protected OrderDto mapToDto(Order order) { + var dto = super.mapToDto(order); + dto.setItems(order.getItems().stream() + .map(x -> orderItemMapper.mapToDto(x)).collect(Collectors.toSet())); + return dto; + } + @Override + protected Order mapToEntity(OrderDto record) { + var entity = super.mapToEntity(record); + entity.setItems(record.getItems().stream() + .map(x -> orderItemMapper.mapToEntity(x)).collect(Collectors.toSet())); + return entity; + } + + protected OrderDto mapToDtoWithBottles(Order order) { + var dto = mapToDto(order); + var itemsLookup = dto.getItems().stream().collect(Collectors.toMap(x -> x.getId(), x -> x)); + for (var itemEntity : order.getItems()) { + var item = itemsLookup.get(itemEntity.getId()); + item.setWineBottle(wineBottleMapper.mapToDto(itemEntity.getBottle())); + } + return dto; } } diff --git a/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/wine/WineBottleServiceImpl.java b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/wine/WineBottleServiceImpl.java index 080d0ec..e4f215b 100644 --- a/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/wine/WineBottleServiceImpl.java +++ b/winery/service/src/main/java/cz/muni/fi/pa165/winery/services/wine/WineBottleServiceImpl.java @@ -4,11 +4,12 @@ import cz.muni.fi.pa165.winery.dto.wine.WineBottleDto; import cz.muni.fi.pa165.winery.persistence.dao.DaoBase; import cz.muni.fi.pa165.winery.persistence.entities.WineBottle; import cz.muni.fi.pa165.winery.services.PersistenceServiceImpl; +import cz.muni.fi.pa165.winery.services.mappers.EntityMapper; import org.springframework.stereotype.Service; @Service public class WineBottleServiceImpl extends PersistenceServiceImpl implements WineBottleService { - public WineBottleServiceImpl(DaoBase wineBottleDao) { - super(WineBottleDto.class, WineBottle.class, wineBottleDao); + public WineBottleServiceImpl(DaoBase wineBottleDao, EntityMapper mapper) { + super(WineBottleDto.class, WineBottle.class, wineBottleDao, mapper); } } diff --git a/winery/service/src/test/java/cz/muni/fi/pa165/winery/services/ServiceTestConfig.java b/winery/service/src/test/java/cz/muni/fi/pa165/winery/services/ServiceTestConfig.java new file mode 100644 index 0000000..f833372 --- /dev/null +++ b/winery/service/src/test/java/cz/muni/fi/pa165/winery/services/ServiceTestConfig.java @@ -0,0 +1,59 @@ +/*** + * @author Ondřej Pavlica + */ + +package cz.muni.fi.pa165.winery.services; + +import org.hibernate.jpa.HibernatePersistenceProvider; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.sql.DataSource; + +@Configuration +@EnableJpaRepositories +@EnableTransactionManagement +@ComponentScan({"cz.muni.fi.pa165.winery.*"}) +@EntityScan(basePackages = {"cz.muni.fi.pa165.winery.persistence.*"}) +public class ServiceTestConfig { + + @Bean + public DataSource dataSource() { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName("org.apache.derby.jdbc.EmbeddedDriver\t"); + dataSource.setUrl("jdbc:derby:test;create=true"); + + return dataSource; + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) { + LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean(); + entityManagerFactory.setDataSource(dataSource); + entityManagerFactory.setPackagesToScan("cz.muni.fi.pa165.winery.persistence.*"); + entityManagerFactory.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + entityManagerFactory.setPersistenceProviderClass(HibernatePersistenceProvider.class); + return entityManagerFactory; + } + + @Bean + public JpaTransactionManager transactionManager() { + return new JpaTransactionManager(); + } + + @Bean + public TransactionTemplate transactionTemplate() { + var template = new TransactionTemplate(transactionManager()); + template.afterPropertiesSet(); + return template; + } +} diff --git a/winery/service/src/test/java/cz/muni/fi/pa165/winery/services/persistence/PersistenceServiceTestBase.java b/winery/service/src/test/java/cz/muni/fi/pa165/winery/services/persistence/PersistenceServiceTestBase.java new file mode 100644 index 0000000..b14926c --- /dev/null +++ b/winery/service/src/test/java/cz/muni/fi/pa165/winery/services/persistence/PersistenceServiceTestBase.java @@ -0,0 +1,95 @@ +package cz.muni.fi.pa165.winery.services.persistence; + +import cz.muni.fi.pa165.winery.dto.PersistentDtoBase; +import cz.muni.fi.pa165.winery.persistence.dao.DaoBase; +import cz.muni.fi.pa165.winery.persistence.entities.EntityBase; +import cz.muni.fi.pa165.winery.services.PersistenceService; +import cz.muni.fi.pa165.winery.services.PersistenceServiceImpl; +import cz.muni.fi.pa165.winery.services.ServiceTestConfig; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.verification.VerificationMode; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + + +@ExtendWith(MockitoExtension.class) +public abstract class PersistenceServiceTestBase { + + @Captor + ArgumentCaptor entityArgumentCaptor; + + @Test + public void testCreate() { + getPersistenceService().create(getTestDto()); + Mockito.verify(getMockedDao(), Mockito.times(1)).create(entityArgumentCaptor.capture()); + var entity = entityArgumentCaptor.getValue(); + assertThat(equals(getTestDto(), entity)).isTrue(); + } + + @Test + public void testGet() { + var dao = getMockedDao(); + Mockito.when(dao.get(1)).thenReturn(getTestEntity()); + var dto = getPersistenceService().get(1); + assertThat(equals(dto, getTestEntity())).isTrue(); + Mockito.verify(dao, Mockito.times(1)).get(1); + } + + @Test + public void testGetAll() { + var dao = getMockedDao(); + var entityList = List.of(getTestEntity()); + Mockito.when(dao.getAll()).thenReturn(entityList); + var dtos = getPersistenceService().getAll(); + assertThat(dtos).hasSize(1); + assertThat(equals(dtos.stream().findFirst().get(), getTestEntity())).isTrue(); + Mockito.verify(dao, Mockito.times(1)).getAll(); + } + + @Test + public void testUpdate() { + var dto = getTestDto(); + getPersistenceService().update(dto); + Mockito.verify(getMockedDao(), Mockito.times(1)).update(entityArgumentCaptor.capture()); + var entity = entityArgumentCaptor.getValue(); + assertThat(equals(dto, entity)).isTrue(); + } + + @Test + public void testDelete() { + var dto = getTestDto(); + getPersistenceService().delete(dto); + Mockito.verify(getMockedDao(), Mockito.times(1)).delete(entityArgumentCaptor.capture()); + var entity = entityArgumentCaptor.getValue(); + assertThat(equals(dto, entity)).isTrue(); + } + + @Test + public void testUpsert() { + var dto = getTestDto(); + getPersistenceService().upsert(dto); + Mockito.verify(getMockedDao(), Mockito.times(1)).upsert(entityArgumentCaptor.capture()); + var entity = entityArgumentCaptor.getValue(); + assertThat(equals(dto, entity)).isTrue(); + } + + protected abstract DTO getTestDto(); + protected abstract ENTITY getTestEntity(); + protected abstract boolean equals(DTO dto, ENTITY entity); + + protected abstract PersistenceServiceImpl getPersistenceService(); + protected abstract DaoBase getMockedDao(); +} diff --git a/winery/service/src/test/java/cz/muni/fi/pa165/winery/services/persistence/order/OrderServiceTests.java b/winery/service/src/test/java/cz/muni/fi/pa165/winery/services/persistence/order/OrderServiceTests.java new file mode 100644 index 0000000..63489b8 --- /dev/null +++ b/winery/service/src/test/java/cz/muni/fi/pa165/winery/services/persistence/order/OrderServiceTests.java @@ -0,0 +1,135 @@ +package cz.muni.fi.pa165.winery.services.persistence.order; + +import cz.muni.fi.pa165.winery.dto.order.OrderDto; +import cz.muni.fi.pa165.winery.dto.order.OrderItemDto; +import cz.muni.fi.pa165.winery.dto.wine.WineBottleDto; +import cz.muni.fi.pa165.winery.persistence.dao.DaoBase; +import cz.muni.fi.pa165.winery.persistence.dao.order.OrderDao; +import cz.muni.fi.pa165.winery.persistence.entities.*; +import cz.muni.fi.pa165.winery.services.PersistenceServiceImpl; +import cz.muni.fi.pa165.winery.services.mappers.order.OrderEntityMapper; +import cz.muni.fi.pa165.winery.services.mappers.order.OrderItemEntityMapper; +import cz.muni.fi.pa165.winery.services.mappers.wine.WineBottleEntityMapper; +import cz.muni.fi.pa165.winery.services.order.OrderServiceImpl; +import cz.muni.fi.pa165.winery.services.persistence.PersistenceServiceTestBase; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(MockitoExtension.class) +public class OrderServiceTests extends PersistenceServiceTestBase { + + @Mock + OrderDao orderDao; + + @Spy + OrderEntityMapper orderEntityMapper; + @Spy + OrderItemEntityMapper orderItemEntityMapper; + @Spy + WineBottleEntityMapper wineBottleEntityMapper; + + OrderServiceImpl orderService; + + @Test + void testGetWithBottle() { + getPersistenceService(); // Force lazy load + var dao = getMockedDao(); + Mockito.when(dao.get(1)).thenReturn(getTestEntity()); + var dto = orderService.get(1, true); + assertThat(equals(dto, getTestEntity())).isTrue(); + Mockito.verify(dao, Mockito.times(1)).get(1); + assertThat(dto.getItems().stream().findFirst().get().getWineBottle().getName()) + .isEqualTo(getTestEntity().getItems().stream().findFirst().get().getBottle().getName()); + } + + @Override + protected OrderDto getTestDto() { + var dto = new OrderDto(); + dto.setId(1); + dto.setUserId(2); + dto.setDateTime(LocalDateTime.of(2022, 1, 1, 0, 0)); + dto.setShipped(true); + + var item = new OrderItemDto(); + item.setId(10); + item.setBottleId(11); + item.setQuantity(new BigDecimal(2)); + + var bottle = new WineBottleDto(); + bottle.setId(11); + bottle.setName("Bottle"); + + item.setWineBottle(bottle); + dto.setItems(Set.of(item)); + + return dto; + } + + @Override + protected Order getTestEntity() { + var order = new Order(); + order.setId(1); + var user = new User(); + user.setId(2); + order.setUser(user); + order.setDateTime(LocalDateTime.of(2022, 1, 1, 0, 0)); + order.setShipped(true); + + var item = new OrderItem(); + item.setId(10); + item.setQuantity(new BigDecimal(2)); + + var bottle = new WineBottle(); + bottle.setId(11); + bottle.setName("Bottle"); + + item.setBottle(bottle); + order.setItems(Set.of(item)); + + return order; + } + + @Override + protected boolean equals(OrderDto orderDto, Order order) { + var orderEquals = orderDto.getId() == order.getId() && + orderDto.getDateTime().equals(order.getDateTime()) && + orderDto.getUserId() == order.getUser().getId() && + orderDto.isShipped() == order.isShipped(); + + var itemDto = orderDto.getItems().stream().findFirst().get(); + var item = order.getItems().stream().findFirst().get(); + + var itemEquals = itemDto.getId() == item.getId() && + itemDto.getBottleId() == item.getBottle().getId() && + itemDto.getQuantity().equals(item.getQuantity()); + + // Bottle is intentionally omitted, it is not always loaded. + // We check the equality separately in the given test. + + return orderEquals && itemEquals; + } + + @Override + protected PersistenceServiceImpl getPersistenceService() { + if (orderService != null) { + return orderService; + } + orderService = new OrderServiceImpl(orderDao, orderEntityMapper, orderItemEntityMapper, wineBottleEntityMapper); + return orderService; + } + + @Override + protected DaoBase getMockedDao() { + return orderDao; + } +} diff --git a/winery/webapp/src/main/java/cz/muni/fi/pa165/winery/webapp/WebappApplication.java b/winery/webapp/src/main/java/cz/muni/fi/pa165/winery/webapp/WebappApplication.java index b991c17..e843756 100644 --- a/winery/webapp/src/main/java/cz/muni/fi/pa165/winery/webapp/WebappApplication.java +++ b/winery/webapp/src/main/java/cz/muni/fi/pa165/winery/webapp/WebappApplication.java @@ -2,8 +2,10 @@ package cz.muni.fi.pa165.winery.webapp; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; @SpringBootApplication +@ComponentScan("cz.muni.fi.pa165.winery.*") public class WebappApplication { public static void main(String[] args) { diff --git a/winery/webapp/src/main/java/cz/muni/fi/pa165/winery/webapp/controllers/HomeController.java b/winery/webapp/src/main/java/cz/muni/fi/pa165/winery/webapp/controllers/HomeController.java index 5e4b83e..07b7405 100644 --- a/winery/webapp/src/main/java/cz/muni/fi/pa165/winery/webapp/controllers/HomeController.java +++ b/winery/webapp/src/main/java/cz/muni/fi/pa165/winery/webapp/controllers/HomeController.java @@ -1,5 +1,7 @@ package cz.muni.fi.pa165.winery.webapp.controllers; +import cz.muni.fi.pa165.winery.services.order.OrderService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; @@ -8,6 +10,13 @@ import org.springframework.web.bind.annotation.RestController; @RestController public class HomeController { + private final OrderService orderService; + + public HomeController(OrderService orderService) { + + this.orderService = orderService; + } + @GetMapping("/") public String index() { return "Home page"; -- GitLab