add addresses & more tests
This commit is contained in:
25
README.md
25
README.md
@@ -18,10 +18,11 @@ To run this application you need to first run a
|
||||
mvn clean install
|
||||
```
|
||||
|
||||
in the root directory of this project. This will download all required dependecies and bundle your project properly.
|
||||
in the root directory of this project. This will download all required dependecies and bundle your
|
||||
project properly.
|
||||
|
||||
The database for this application is a mongo db. You either need to have one running on your system, or you can
|
||||
alternatively start the MongoDB in docker with the included docker-compose file.
|
||||
The database for this application is a mongo db. You either need to have one running on your system,
|
||||
or you can alternatively start the MongoDB in docker with the included docker-compose file.
|
||||
|
||||
```cmd
|
||||
docker-compose -f mongodb-docker-compose up
|
||||
@@ -37,11 +38,13 @@ hexagonal-example\config\src\main\java\de\strasser\peter\hexagonal\HexagonalAppl
|
||||
|
||||
### What does this application do
|
||||
|
||||
This app can register customers, add addresses to these customers and retrieve a list of all customers.
|
||||
This app can register customers, add addresses to these customers and retrieve a list of all
|
||||
customers.
|
||||
|
||||
The supported usecases in the business layer can be inspected in the applicationmodule. Under
|
||||
application/src/main/java/de/strasser/peter/hexagonal/application/customer/port all the supported operations are clearly
|
||||
visible, which is a major selling point to structure your application in this way.
|
||||
application/src/main/java/de/strasser/peter/hexagonal/application/customer/port all the supported
|
||||
operations are clearly visible, which is a major selling point to structure your application in this
|
||||
way.
|
||||
|
||||

|
||||
|
||||
@@ -49,3 +52,13 @@ visible, which is a major selling point to structure your application in this wa
|
||||
|
||||

|
||||
|
||||
### Advantages of separated models on each layer in this example
|
||||
|
||||
- The customer response can easily exclude the (hashed) password without much effort. This seperates
|
||||
the concern in what way to display the data to the client.
|
||||
|
||||
|
||||
- The customer can be persisted different from the domain models structure. The layout in this
|
||||
example has no benefit, but assume structuring your data in this way would give you a much needed
|
||||
performance boost. This way the concern on how to handle data persistence is independent from the
|
||||
business layer and can be handled by the persistence module.
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package de.strasser.peter.hexagonal.addressvalidation;
|
||||
|
||||
import de.strasser.peter.hexagonal.application.customer.port.out.commands.ValidateAddressCommand;
|
||||
|
||||
|
||||
class AddressDoesNotExistExc extends IllegalArgumentException {
|
||||
AddressDoesNotExistExc(ValidateAddressCommand validateAddressCommand) {
|
||||
super(String.format("Address %s does not exist", validateAddressCommand.toString()));
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,11 @@ import org.springframework.stereotype.Component;
|
||||
class AddressValidator implements AddressValidatorAdapter {
|
||||
|
||||
@Override
|
||||
public Address validate(ValidateAddressCommand validateAddressCommand) {
|
||||
public Address validate(ValidateAddressCommand validateAddressCommand) throws InvalidAddressExc {
|
||||
// This could be some call to a 3rd party to validate this address.
|
||||
if (validateAddressCommand.getStreet().equalsIgnoreCase("parkring")) {
|
||||
log.info("Address is made up.");
|
||||
throw new AddressDoesNotExistExc(validateAddressCommand);
|
||||
throw new InvalidAddressExc(validateAddressCommand);
|
||||
}
|
||||
|
||||
return new Address(
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package de.strasser.peter.hexagonal.addressvalidation;
|
||||
|
||||
import de.strasser.peter.hexagonal.application.customer.exception.BusinessException;
|
||||
import de.strasser.peter.hexagonal.application.customer.port.out.commands.ValidateAddressCommand;
|
||||
|
||||
public class InvalidAddressExc extends BusinessException {
|
||||
public InvalidAddressExc(ValidateAddressCommand validateAddressCommand) {
|
||||
super(
|
||||
String.format(
|
||||
"Address <%s, %d, %d, %s> does not exist",
|
||||
validateAddressCommand.getStreet(),
|
||||
validateAddressCommand.getHouseNumber(),
|
||||
validateAddressCommand.getZipCode(),
|
||||
validateAddressCommand.getCountry()));
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import de.strasser.peter.hexagonal.application.customer.domain.Customer;
|
||||
import de.strasser.peter.hexagonal.application.customer.port.in.QueryAllCustomersCRUD;
|
||||
import de.strasser.peter.hexagonal.application.customer.port.out.LoadCustomerAdapter;
|
||||
import de.strasser.peter.hexagonal.application.customer.port.out.SaveCustomerAdapter;
|
||||
import de.strasser.peter.hexagonal.persistence.errors.CustomerDoesNotExistExc;
|
||||
import de.strasser.peter.hexagonal.persistence.mapper.CustomerMapper;
|
||||
import de.strasser.peter.hexagonal.persistence.model.CustomerEntity;
|
||||
import de.strasser.peter.hexagonal.persistence.repository.CustomerRepository;
|
||||
@@ -12,37 +13,31 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class CustomerDao implements SaveCustomerAdapter, LoadCustomerAdapter, QueryAllCustomersCRUD {
|
||||
private final CustomerRepository customerRepository;
|
||||
private final CustomerMapper customerMapper;
|
||||
public class CustomerDao
|
||||
implements SaveCustomerAdapter, LoadCustomerAdapter, QueryAllCustomersCRUD {
|
||||
private final CustomerRepository customerRepository;
|
||||
private final CustomerMapper customerMapper;
|
||||
|
||||
@Override
|
||||
public void upsert(Customer customer) {
|
||||
log.info("saving customer");
|
||||
customerRepository.save(customerMapper.toDbEntity(customer));
|
||||
}
|
||||
@Override
|
||||
public void upsert(Customer customer) {
|
||||
customerRepository.save(customerMapper.toDbEntity(customer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Customer findById(BigInteger id) {
|
||||
Optional<CustomerEntity> byId = customerRepository.findById(id);
|
||||
return Customer.createCustomer(id,
|
||||
"max",
|
||||
"mustermann",
|
||||
LocalDate.of(1980, 1, 1),
|
||||
new HashMap<>(),
|
||||
true);
|
||||
}
|
||||
@Override
|
||||
public Customer findById(BigInteger id) {
|
||||
final CustomerEntity customerEntity =
|
||||
customerRepository.findById(id).orElseThrow(() -> new CustomerDoesNotExistExc(id));
|
||||
|
||||
@Override
|
||||
public List<Customer> getAll() {
|
||||
return customerMapper.toDomain(customerRepository.findAll());
|
||||
}
|
||||
return customerMapper.toDomain(customerEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Customer> getAll() {
|
||||
return customerMapper.toDomain(customerRepository.findAll());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package de.strasser.peter.hexagonal.persistence.errors;
|
||||
|
||||
import de.strasser.peter.hexagonal.application.customer.exception.BusinessException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class CustomerDoesNotExistExc extends BusinessException {
|
||||
public CustomerDoesNotExistExc(BigInteger id) {
|
||||
super("Customer with id " + id.toString() + " does not exist!");
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,10 @@ package de.strasser.peter.hexagonal.web;
|
||||
|
||||
import de.strasser.peter.hexagonal.application.customer.port.in.AddAddressUseCase;
|
||||
import de.strasser.peter.hexagonal.application.customer.port.in.commands.AddAddressCommand;
|
||||
import de.strasser.peter.hexagonal.web.mapper.AddAddressWebMapper;
|
||||
import de.strasser.peter.hexagonal.web.dto.request.AddAddressRequest;
|
||||
import de.strasser.peter.hexagonal.web.mapper.AddAddressWebMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
@@ -16,12 +13,14 @@ import java.util.List;
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class AddAddressController {
|
||||
private final AddAddressUseCase addAddressUseCase;
|
||||
private final AddAddressWebMapper addAddressMapper;
|
||||
private final AddAddressUseCase addAddressUseCase;
|
||||
private final AddAddressWebMapper addAddressMapper;
|
||||
|
||||
@PostMapping("/v1/address")
|
||||
public void addAddress(@RequestParam BigInteger customerId, @RequestBody AddAddressRequest addAddressRequest) {
|
||||
final List<AddAddressCommand> addAddressCmds = List.of(addAddressMapper.toCmd(addAddressRequest));
|
||||
addAddressUseCase.addAddresses(customerId, addAddressCmds);
|
||||
}
|
||||
@PostMapping("/v1/customer/address")
|
||||
public void addAddress(
|
||||
@RequestParam BigInteger customerId, @RequestBody AddAddressRequest addAddressRequest) {
|
||||
final List<AddAddressCommand> addAddressCmds =
|
||||
List.of(addAddressMapper.toCmd(addAddressRequest));
|
||||
addAddressUseCase.addAddresses(customerId, addAddressCmds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package de.strasser.peter.hexagonal.web.dto.response;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AddressResponse {
|
||||
private String street;
|
||||
private Integer houseNumber;
|
||||
private Integer zipCode;
|
||||
private String country;
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package de.strasser.peter.hexagonal.web.dto.response;
|
||||
|
||||
import de.strasser.peter.hexagonal.application.customer.domain.Address;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@@ -13,7 +15,7 @@ import java.time.LocalDate;
|
||||
public class CustomerResponse {
|
||||
private BigInteger id;
|
||||
private String name;
|
||||
private String hashedPassword;
|
||||
private LocalDate birthday;
|
||||
private Map<Address.AddressType, AddressResponse> addresses;
|
||||
private int age;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package de.strasser.peter.hexagonal.web;
|
||||
|
||||
import de.strasser.peter.hexagonal.application.customer.port.in.AddAddressUseCase;
|
||||
import de.strasser.peter.hexagonal.application.customer.port.in.commands.AddAddressCommand;
|
||||
import de.strasser.peter.hexagonal.common.validators.TestUtils;
|
||||
import de.strasser.peter.hexagonal.web.mapper.AddAddressWebMapperImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@Slf4j
|
||||
@WebMvcTest(controllers = AddAddressController.class)
|
||||
@Import(AddAddressWebMapperImpl.class)
|
||||
class AddAddressControllerTest {
|
||||
@Autowired private MockMvc mockMvc;
|
||||
|
||||
@MockBean private AddAddressUseCase addAddressUseCase;
|
||||
|
||||
@Test
|
||||
public void should_AddAddress() throws Exception {
|
||||
final String body = TestUtils.readStringFromResource("valid_add_address.json");
|
||||
final int customerId = 1231231;
|
||||
|
||||
mockMvc
|
||||
.perform(
|
||||
post("/v1/customer/address?customerId=" + customerId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(body))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn();
|
||||
|
||||
var addAddressCmd = new AddAddressCommand("default", "street", 59, 85748, "Germany");
|
||||
|
||||
then(addAddressUseCase)
|
||||
.should()
|
||||
.addAddresses(eq(BigInteger.valueOf(customerId)), eq(List.of(addAddressCmd)));
|
||||
}
|
||||
}
|
||||
7
adapter/web/src/test/resources/valid_add_address.json
Normal file
7
adapter/web/src/test/resources/valid_add_address.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"type": "default",
|
||||
"street": "street",
|
||||
"houseNumber": 59,
|
||||
"zipCode": 85748,
|
||||
"country": "Germany"
|
||||
}
|
||||
@@ -10,7 +10,6 @@ public class Address {
|
||||
Integer zipCode;
|
||||
String country;
|
||||
|
||||
|
||||
public enum AddressType {
|
||||
DEFAULT,
|
||||
SHIPPING,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package de.strasser.peter.hexagonal.application.customer.domain;
|
||||
|
||||
import de.strasser.peter.hexagonal.application.customer.exception.DefaultAdressRequiredToActivateExc;
|
||||
import de.strasser.peter.hexagonal.application.customer.exception.UserIsTooYoungExc;
|
||||
import de.strasser.peter.hexagonal.application.customer.exception.TooOldToDeactivateExc;
|
||||
import de.strasser.peter.hexagonal.application.customer.exception.TooYoungExc;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.time.LocalDate;
|
||||
@@ -11,67 +13,63 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Getter
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
public class Customer {
|
||||
private final BigInteger id;
|
||||
private String name;
|
||||
private String hashedPassword;
|
||||
private LocalDate birthday;
|
||||
private int age;
|
||||
private Map<Address.AddressType, Address> addresses;
|
||||
private boolean active;
|
||||
private final BigInteger id;
|
||||
private String name;
|
||||
private String hashedPassword;
|
||||
private LocalDate birthday;
|
||||
private int age;
|
||||
private Map<Address.AddressType, Address> addresses;
|
||||
private boolean active;
|
||||
|
||||
private Customer(BigInteger id, String name, String hashedPassword, LocalDate birthDate, Map<Address.AddressType, Address> addresses, boolean active) {
|
||||
this.id = id;
|
||||
this.active = active;
|
||||
this.age = Period.between(birthDate, LocalDate.now()).getYears();
|
||||
this.birthday = birthDate;
|
||||
this.name = name;
|
||||
this.hashedPassword = hashedPassword;
|
||||
this.addresses = addresses == null ? addresses : new HashMap<>();
|
||||
if (this.age < 18) {
|
||||
throw new UserIsTooYoungExc(age);
|
||||
}
|
||||
private Customer(
|
||||
BigInteger id,
|
||||
String name,
|
||||
String hashedPassword,
|
||||
LocalDate birthDate,
|
||||
Map<Address.AddressType, Address> addresses,
|
||||
boolean active) {
|
||||
this.id = id;
|
||||
this.active = active;
|
||||
this.age = Period.between(birthDate, LocalDate.now()).getYears();
|
||||
this.birthday = birthDate;
|
||||
this.name = name;
|
||||
this.hashedPassword = hashedPassword;
|
||||
this.addresses = addresses != null ? addresses : new HashMap<>();
|
||||
if (this.age < 18) {
|
||||
throw new TooYoungExc(age);
|
||||
}
|
||||
}
|
||||
|
||||
public static Customer newCustomer(
|
||||
String name,
|
||||
String hashedPassword,
|
||||
LocalDate birthDate) {
|
||||
return new Customer(
|
||||
null,
|
||||
name,
|
||||
hashedPassword,
|
||||
birthDate,
|
||||
null,
|
||||
true);
|
||||
public static Customer newCustomer(String name, String hashedPassword, LocalDate birthDate) {
|
||||
return new Customer(null, name, hashedPassword, birthDate, null, false);
|
||||
}
|
||||
|
||||
public static Customer createCustomer(
|
||||
BigInteger id,
|
||||
String name,
|
||||
String hashedPassword,
|
||||
LocalDate birthDate,
|
||||
Map<Address.AddressType, Address> addresses,
|
||||
boolean active) {
|
||||
return new Customer(id, name, hashedPassword, birthDate, addresses, active);
|
||||
}
|
||||
|
||||
public void addAddresses(Map<Address.AddressType, Address> addresses) {
|
||||
this.addresses.putAll(addresses);
|
||||
|
||||
if (this.addresses.containsKey(Address.AddressType.DEFAULT)) {
|
||||
this.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static Customer createCustomer(
|
||||
BigInteger id,
|
||||
String name,
|
||||
String hashedPassword,
|
||||
LocalDate birthDate,
|
||||
Map<Address.AddressType, Address> addresses,
|
||||
boolean active) {
|
||||
return new Customer(
|
||||
id, name, hashedPassword,
|
||||
birthDate,
|
||||
addresses,
|
||||
active);
|
||||
}
|
||||
|
||||
public void activateCustomer() {
|
||||
if (this.addresses == null || this.addresses.containsKey(Address.AddressType.DEFAULT)) {
|
||||
throw new DefaultAdressRequiredToActivateExc();
|
||||
}
|
||||
this.active = true;
|
||||
}
|
||||
|
||||
public void addAddresses(Map<Address.AddressType, Address> addresses) {
|
||||
this.addresses.putAll(addresses);
|
||||
|
||||
if (this.addresses.containsKey(Address.AddressType.DEFAULT)) {
|
||||
this.activateCustomer();
|
||||
}
|
||||
public void deactivate() {
|
||||
if (this.age < 50) {
|
||||
this.active = false;
|
||||
} else {
|
||||
throw new TooOldToDeactivateExc(this.age);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package de.strasser.peter.hexagonal.application.customer.exception;
|
||||
|
||||
public class TooOldToDeactivateExc extends BusinessException {
|
||||
public TooOldToDeactivateExc(int age) {
|
||||
super("Customer is too old to be deactivated. Expected < 50, Actual: " + age);
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@ package de.strasser.peter.hexagonal.application.customer.exception;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
public class UserIsTooYoungExc extends BusinessException {
|
||||
public UserIsTooYoungExc(int age) {
|
||||
public class TooYoungExc extends BusinessException {
|
||||
public TooYoungExc(int age) {
|
||||
super(MessageFormat.format("Customer is too young. Expected: > 18 yrs, Actual: {0}", age));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package de.strasser.peter.hexagonal.application.customer.domain;
|
||||
|
||||
import de.strasser.peter.hexagonal.application.customer.exception.TooOldToDeactivateExc;
|
||||
import de.strasser.peter.hexagonal.application.customer.exception.TooYoungExc;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class CustomerTest {
|
||||
|
||||
@Test
|
||||
public void should_CreateNewCustomer() {
|
||||
final var customer = Customer.newCustomer("name", "pw", LocalDate.of(1980, 1, 1));
|
||||
|
||||
assertNotNull(customer);
|
||||
assertNull(customer.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_ThrowTooYoungErr_When_CreatingNewCustomer() {
|
||||
assertThrows(
|
||||
TooYoungExc.class, () -> Customer.newCustomer("name", "pw", LocalDate.of(2010, 1, 1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_ActivateCustomer_When_AddingDefaultAddress() {
|
||||
final Customer customer = Customer.newCustomer("name", "pw", LocalDate.of(1980, 1, 1));
|
||||
final Address address = new Address("Parkring", 59, 85748, "Germany");
|
||||
|
||||
customer.addAddresses(Collections.singletonMap(Address.AddressType.DEFAULT, address));
|
||||
|
||||
assertTrue(customer.isActive());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_NotActivateCustomer_When_AddingBillingAddress() {
|
||||
final Customer customer = Customer.newCustomer("name", "pw", LocalDate.of(1980, 1, 1));
|
||||
final Address address = new Address("Parkring", 59, 85748, "Germany");
|
||||
|
||||
customer.addAddresses(Collections.singletonMap(Address.AddressType.BILLING, address));
|
||||
|
||||
assertFalse(customer.isActive());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_DeactivateCustomer() {
|
||||
final Customer customer = Customer.newCustomer("name", "pw", LocalDate.of(1980, 1, 1));
|
||||
customer.deactivate();
|
||||
|
||||
assertFalse(customer.isActive());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_ThrowTooOldErr_When_DeactivatingOldCustomer(){
|
||||
final Customer customer = Customer.newCustomer("name", "pw", LocalDate.of(1950, 1, 1));
|
||||
assertThrows(TooOldToDeactivateExc.class, customer::deactivate);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,141 @@
|
||||
package de.strasser.peter.hexagonal.application.customer.service;
|
||||
|
||||
import de.strasser.peter.hexagonal.application.customer.domain.Address;
|
||||
import de.strasser.peter.hexagonal.application.customer.domain.Customer;
|
||||
import de.strasser.peter.hexagonal.application.customer.mapper.AddAddressMapper;
|
||||
import de.strasser.peter.hexagonal.application.customer.port.in.commands.AddAddressCommand;
|
||||
import de.strasser.peter.hexagonal.application.customer.port.out.AddressValidatorAdapter;
|
||||
import de.strasser.peter.hexagonal.application.customer.port.out.LoadCustomerAdapter;
|
||||
import de.strasser.peter.hexagonal.application.customer.port.out.SaveCustomerAdapter;
|
||||
import de.strasser.peter.hexagonal.application.customer.port.out.commands.ValidateAddressCommand;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.boot.test.mock.mockito.SpyBean;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
|
||||
@SpringBootTest
|
||||
class AddressServiceTest {
|
||||
|
||||
@Autowired
|
||||
private AddressService sut;
|
||||
@MockBean
|
||||
private SaveCustomerAdapter saveCustomerAdapterMock;
|
||||
@MockBean
|
||||
private AddressValidatorAdapter addressValidatorAdapterMock;
|
||||
@MockBean
|
||||
private LoadCustomerAdapter loadCustomerAdapterMock;
|
||||
@MockBean
|
||||
private AddAddressMapper addAddressMapperMock;
|
||||
@Autowired private AddressService sut;
|
||||
@MockBean private SaveCustomerAdapter saveCustomerAdapterMock;
|
||||
@MockBean private AddressValidatorAdapter addressValidatorAdapterMock;
|
||||
@MockBean private LoadCustomerAdapter loadCustomerAdapterMock;
|
||||
@SpyBean private AddAddressMapper addAddressMapperMock;
|
||||
|
||||
@Test
|
||||
public void should_AddAddress() {
|
||||
// GIVEN
|
||||
final var customerId = BigInteger.valueOf(13);
|
||||
final var name = "hans";
|
||||
final var birthday = LocalDate.of(1980, 1, 1);
|
||||
final var passwd = "secretPw";
|
||||
|
||||
given(loadCustomerAdapterMock.findById(any()))
|
||||
.willReturn(Customer.createCustomer(customerId, name, passwd, birthday, null, false));
|
||||
|
||||
final String street = "Parkring";
|
||||
final int houseNumber = 57;
|
||||
final int zipCode = 85748;
|
||||
final String country = "Germany";
|
||||
|
||||
final Address validatedAddress = new Address(street, houseNumber, zipCode, country);
|
||||
final ValidateAddressCommand validateAddressCmd =
|
||||
new ValidateAddressCommand(street, houseNumber, zipCode, country);
|
||||
|
||||
given(addressValidatorAdapterMock.validate(validateAddressCmd)).willReturn(validatedAddress);
|
||||
|
||||
final List<AddAddressCommand> addAddressCmds =
|
||||
List.of(new AddAddressCommand("billing", street, houseNumber, zipCode, country));
|
||||
|
||||
sut.addAddresses(customerId, addAddressCmds);
|
||||
|
||||
final Customer customerToBeSaved =
|
||||
Customer.createCustomer(
|
||||
customerId,
|
||||
name,
|
||||
passwd,
|
||||
birthday,
|
||||
Map.of(Address.AddressType.BILLING, validatedAddress),
|
||||
false);
|
||||
then(saveCustomerAdapterMock).should().upsert(eq(customerToBeSaved));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_AddAddressAndActivateCustomer_When_ProvidedDefaultAddress() {
|
||||
// GIVEN
|
||||
final var customerId = BigInteger.valueOf(13);
|
||||
final var name = "hans";
|
||||
final var birthday = LocalDate.of(1980, 1, 1);
|
||||
final var passwd = "secretPw";
|
||||
|
||||
given(loadCustomerAdapterMock.findById(any()))
|
||||
.willReturn(Customer.createCustomer(customerId, name, passwd, birthday, null, false));
|
||||
|
||||
final String street = "Parkring";
|
||||
final int houseNumber = 57;
|
||||
final int zipCode = 85748;
|
||||
final String country = "Germany";
|
||||
|
||||
final Address validatedAddress = new Address(street, houseNumber, zipCode, country);
|
||||
final ValidateAddressCommand validateAddressCmd =
|
||||
new ValidateAddressCommand(street, houseNumber, zipCode, country);
|
||||
|
||||
given(addressValidatorAdapterMock.validate(validateAddressCmd)).willReturn(validatedAddress);
|
||||
|
||||
final List<AddAddressCommand> addAddressCmds =
|
||||
List.of(new AddAddressCommand("default", street, houseNumber, zipCode, country));
|
||||
|
||||
sut.addAddresses(customerId, addAddressCmds);
|
||||
|
||||
final Customer customerToBeSaved =
|
||||
Customer.createCustomer(
|
||||
customerId,
|
||||
name,
|
||||
passwd,
|
||||
birthday,
|
||||
Map.of(Address.AddressType.DEFAULT, validatedAddress),
|
||||
true);
|
||||
then(saveCustomerAdapterMock).should().upsert(eq(customerToBeSaved));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_ThrowError_When_ProvidingInvalidAddress() {
|
||||
// GIVEN
|
||||
final var customerId = BigInteger.valueOf(13);
|
||||
final var name = "hans";
|
||||
final var birthday = LocalDate.of(1980, 1, 1);
|
||||
final var passwd = "secretPw";
|
||||
|
||||
given(loadCustomerAdapterMock.findById(any()))
|
||||
.willReturn(Customer.createCustomer(customerId, name, passwd, birthday, null, false));
|
||||
|
||||
final String street = "Parkring";
|
||||
final int houseNumber = 57;
|
||||
final int zipCode = 85748;
|
||||
final String country = "Germany";
|
||||
|
||||
final ValidateAddressCommand validateAddressCmd =
|
||||
new ValidateAddressCommand(street, houseNumber, zipCode, country);
|
||||
|
||||
given(addressValidatorAdapterMock.validate(validateAddressCmd))
|
||||
.willThrow(new IllegalArgumentException("invalid adress"));
|
||||
|
||||
final List<AddAddressCommand> addAddressCmds =
|
||||
List.of(new AddAddressCommand("billing", street, houseNumber, zipCode, country));
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class, () -> sut.addAddresses(customerId, addAddressCmds));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user