diff --git a/README.md b/README.md index 4556e7df..cd4cf60a 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,7 @@ quarkus dev -e *A la racine du projet* ```bash +mvn process-test-resources mvn test ``` diff --git a/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountEntity.java b/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountEntity.java index da1b9328..958aaf8c 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountEntity.java +++ b/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountEntity.java @@ -29,7 +29,7 @@ public class BankAccountEntity extends PanacheEntityBase { @ManyToMany(mappedBy = "accounts", fetch = FetchType.LAZY, cascade = CascadeType.MERGE) private List customers = new ArrayList<>(); - @OneToMany(mappedBy = "senderBankAccountEntity", cascade = CascadeType.ALL, orphanRemoval = true) + @OneToMany(mappedBy = "emitterBankAccountEntity", cascade = CascadeType.ALL, orphanRemoval = true) @OrderBy("date") private Set issuedTransactions = new HashSet<>(); @@ -76,7 +76,7 @@ public void setIssuedTransactions(Set issuedTransactions) { public void addTransaction(TransactionEntity transaction) { this.issuedTransactions.add(transaction); - transaction.setSenderBankAccountEntity(this); + transaction.setEmitterBankAccountEntity(this); } @Override diff --git a/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountMapper.java b/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountMapper.java index 448f56bb..40529c44 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountMapper.java +++ b/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountMapper.java @@ -2,6 +2,7 @@ import com.cdx.bas.application.bank.customer.CustomerEntity; import com.cdx.bas.application.bank.customer.CustomerRepository; +import com.cdx.bas.application.bank.transaction.TransactionMapper; import com.cdx.bas.application.mapper.DtoEntityMapper; import com.cdx.bas.application.bank.transaction.TransactionEntity; import com.cdx.bas.domain.bank.account.BankAccount; @@ -24,7 +25,7 @@ public class BankAccountMapper implements DtoEntityMapper transactionMapper; + TransactionMapper transactionMapper; @Override public BankAccount toDto(BankAccountEntity entity) { @@ -73,7 +74,7 @@ public BankAccountEntity toEntity(BankAccount dto) { Set newIssuedTransactions = new HashSet<>(); for (Transaction issuedTransactionDto : dto.getIssuedTransactions()) { TransactionEntity newIssuedTransactionEntity = transactionMapper.toEntity(issuedTransactionDto); - newIssuedTransactionEntity.setSenderBankAccountEntity(entity); + newIssuedTransactionEntity.setEmitterBankAccountEntity(entity); newIssuedTransactions.add(newIssuedTransactionEntity); } diff --git a/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountRepository.java b/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountRepository.java index 1a9ea15c..202379a3 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountRepository.java +++ b/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountRepository.java @@ -33,7 +33,7 @@ public class BankAccountRepository implements BankAccountPersistencePort, Panach BankAccountMapper bankAccountMapper; @Override - @Transactional(Transactional.TxType.MANDATORY) + @Transactional public Set getAll() { return findAll(Sort.by("id")).stream() .map(bankAccountEntity -> bankAccountMapper.toDto(bankAccountEntity)) @@ -41,20 +41,20 @@ public Set getAll() { } @Override - @Transactional(Transactional.TxType.MANDATORY) + @Transactional public Optional findById(long id) { return findByIdOptional(id).map(bankAccountMapper::toDto); } @Override - @Transactional(value = MANDATORY) + @Transactional public BankAccount create(BankAccount bankAccount) { getEntityManager().persist(bankAccountMapper.toEntity(bankAccount)); logger.info("BankAccount " + bankAccount.getId() + " created"); return bankAccount; } @Override - @Transactional(value = MANDATORY) + @Transactional public BankAccount update(BankAccount bankAccount) { bankAccount = bankAccountMapper.toDto(getEntityManager().merge(bankAccountMapper.toEntity(bankAccount))); logger.info("BankAccount " + bankAccount.getId() + " updated"); @@ -62,7 +62,7 @@ public BankAccount update(BankAccount bankAccount) { } @Override - @Transactional(value = MANDATORY) + @Transactional public Optional deleteById(long id) { Optional entityOptional = findByIdOptional(id); if (entityOptional.isPresent()) { diff --git a/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountServiceImpl.java b/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountServiceImpl.java index e27e1b54..3aeca5c4 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountServiceImpl.java +++ b/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountServiceImpl.java @@ -32,30 +32,19 @@ public class BankAccountServiceImpl implements BankAccountServicePort { TransactionServicePort transactionService; @Override - @Transactional(Transactional.TxType.MANDATORY) + @Transactional public Set getAll() { return bankAccountRepository.getAll(); } @Override - @Transactional(Transactional.TxType.MANDATORY) + @Transactional public BankAccount findBankAccount(Long bankAccountId){ return bankAccountRepository.findById(bankAccountId).orElse(null); } @Override - public void transferAmountBetweenAccounts(Transaction transaction, BankAccount emitterBankAccount, BankAccount receiverBankAccount) { - BigDecimal euroAmount = ExchangeRateUtils.getEuroAmountFrom(transaction.getCurrency(), transaction.getAmount()); - if (euroAmount.signum() < 0) { - throw new TransactionException("Credit transaction " + transaction.getId() + " should have positive value, actual value: " + euroAmount); - } - emitterBankAccount.getBalance().minus(Money.of(euroAmount)); - receiverBankAccount.getBalance().plus(Money.of(euroAmount)); - logger.debug("add amount " + emitterBankAccount.getBalance() + " " + transaction.getCurrency() - + " to bank account" + receiverBankAccount.getId() + " from bank account " + emitterBankAccount.getId()); - } - - @Override + @Transactional public BankAccount addTransaction(Transaction transaction, BankAccount bankAccount) { Optional optionalStoredTransaction = bankAccount.getIssuedTransactions().stream() .filter(actualTransaction -> actualTransaction.getId().equals(transaction.getId())) @@ -73,10 +62,22 @@ public BankAccount addTransaction(Transaction transaction, BankAccount bankAccou } @Override - @Transactional(Transactional.TxType.MANDATORY) + @Transactional public BankAccount updateBankAccount(BankAccount bankAccount) throws BankAccountException { logger.debug("update bank account" + bankAccount.getId()); bankAccountValidator.validateBankAccount(bankAccount); return bankAccountRepository.update(bankAccount); } + + @Override + public void transferAmountBetweenAccounts(Transaction transaction, BankAccount emitterBankAccount, BankAccount receiverBankAccount) { + BigDecimal euroAmount = ExchangeRateUtils.getEuroAmountFrom(transaction.getCurrency(), transaction.getAmount()); + if (euroAmount.signum() < 0) { + throw new TransactionException("Credit transaction " + transaction.getId() + " should have positive value, actual value: " + euroAmount); + } + emitterBankAccount.getBalance().minus(Money.of(euroAmount)); + receiverBankAccount.getBalance().plus(Money.of(euroAmount)); + logger.debug("add amount " + emitterBankAccount.getBalance() + " " + transaction.getCurrency() + + " to bank account" + receiverBankAccount.getId() + " from bank account " + emitterBankAccount.getId()); + } } diff --git a/application/src/main/java/com/cdx/bas/application/bank/customer/CustomerMapper.java b/application/src/main/java/com/cdx/bas/application/bank/customer/CustomerMapper.java index 11cbc082..6dcd3cdb 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/customer/CustomerMapper.java +++ b/application/src/main/java/com/cdx/bas/application/bank/customer/CustomerMapper.java @@ -1,6 +1,7 @@ package com.cdx.bas.application.bank.customer; import com.cdx.bas.application.bank.account.BankAccountEntity; +import com.cdx.bas.application.bank.account.BankAccountMapper; import com.cdx.bas.application.mapper.DtoEntityMapper; import com.cdx.bas.domain.bank.account.BankAccount; import com.cdx.bas.domain.bank.customer.Customer; @@ -18,7 +19,7 @@ public class CustomerMapper implements DtoEntityMapper { @Inject - DtoEntityMapper bankAccountMapper; + BankAccountMapper bankAccountMapper; @Inject ObjectMapper objectMapper; diff --git a/application/src/main/java/com/cdx/bas/application/bank/customer/CustomerRepository.java b/application/src/main/java/com/cdx/bas/application/bank/customer/CustomerRepository.java index 5d459961..4b79a867 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/customer/CustomerRepository.java +++ b/application/src/main/java/com/cdx/bas/application/bank/customer/CustomerRepository.java @@ -28,7 +28,7 @@ public class CustomerRepository implements CustomerPersistencePort, PanacheRepos private static final Logger logger = Logger.getLogger(CustomerRepository.class); @Inject - private DtoEntityMapper customerMapper; + CustomerMapper customerMapper; @Override @Transactional(value = MANDATORY) diff --git a/application/src/main/java/com/cdx/bas/application/bank/customer/gender/GenderConverter.java b/application/src/main/java/com/cdx/bas/application/bank/customer/gender/GenderConverter.java index 687e3c83..b523b12e 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/customer/gender/GenderConverter.java +++ b/application/src/main/java/com/cdx/bas/application/bank/customer/gender/GenderConverter.java @@ -4,6 +4,8 @@ import jakarta.persistence.AttributeConverter; import jakarta.persistence.Converter; +import static com.cdx.bas.domain.bank.customer.gender.Gender.*; + @Converter public class GenderConverter implements AttributeConverter { diff --git a/application/src/main/java/com/cdx/bas/application/bank/customer/maritalstatus/MaritalStatusConverter.java b/application/src/main/java/com/cdx/bas/application/bank/customer/maritalstatus/MaritalStatusConverter.java index 638e7d57..e62badde 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/customer/maritalstatus/MaritalStatusConverter.java +++ b/application/src/main/java/com/cdx/bas/application/bank/customer/maritalstatus/MaritalStatusConverter.java @@ -4,6 +4,8 @@ import jakarta.persistence.AttributeConverter; import jakarta.persistence.Converter; +import static com.cdx.bas.domain.bank.customer.maritalstatus.MaritalStatus.*; + @Converter public class MaritalStatusConverter implements AttributeConverter { diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionEntity.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionEntity.java index bb8857af..e461495f 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionEntity.java +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionEntity.java @@ -26,8 +26,8 @@ public class TransactionEntity extends PanacheEntityBase { private Long id; @ManyToOne(fetch = FetchType.LAZY, optional = false) - @JoinColumn(name = "sender_account_id", nullable = false) - private BankAccountEntity senderBankAccountEntity; + @JoinColumn(name = "emitter_account_id", nullable = false) + private BankAccountEntity emitterBankAccountEntity; @ManyToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "receiver_account_id", nullable = false) @@ -66,12 +66,12 @@ public void setId(Long id) { this.id = id; } - public BankAccountEntity getSenderBankAccountEntity() { - return senderBankAccountEntity; + public BankAccountEntity getEmitterBankAccountEntity() { + return emitterBankAccountEntity; } - public void setSenderBankAccountEntity(BankAccountEntity senderBankAccountEntity) { - this.senderBankAccountEntity = senderBankAccountEntity; + public void setEmitterBankAccountEntity(BankAccountEntity emitterBankAccountEntity) { + this.emitterBankAccountEntity = emitterBankAccountEntity; } public BankAccountEntity getReceiverBankAccountEntity() { @@ -144,7 +144,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; TransactionEntity that = (TransactionEntity) o; return Objects.equals(id, that.id) - && Objects.equals(senderBankAccountEntity, that.senderBankAccountEntity) + && Objects.equals(emitterBankAccountEntity, that.emitterBankAccountEntity) && Objects.equals(receiverBankAccountEntity, that.receiverBankAccountEntity) && Objects.equals(amount, that.amount) && Objects.equals(currency, that.currency) @@ -156,6 +156,6 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(id, senderBankAccountEntity, receiverBankAccountEntity, amount, currency, type, status, date, label, metadata); + return Objects.hash(id, emitterBankAccountEntity, receiverBankAccountEntity, amount, currency, type, status, date, label, metadata); } } diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionMapper.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionMapper.java index e488f5bb..ce6a8ab0 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionMapper.java +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionMapper.java @@ -36,10 +36,10 @@ public Transaction toDto(TransactionEntity entity) { .label(entity.getLabel()) .build(); - if (entity.getSenderBankAccountEntity() != null) { - dto.setSenderAccountId(entity.getSenderBankAccountEntity().getId()); + if (entity.getEmitterBankAccountEntity() != null) { + dto.setEmitterAccountId(entity.getEmitterBankAccountEntity().getId()); } else { - throw new NoSuchElementException("Transaction does not have sender bank account."); + throw new NoSuchElementException("Transaction does not have emitter bank account."); } if (entity.getReceiverBankAccountEntity() != null) { @@ -73,13 +73,13 @@ public TransactionEntity toEntity(Transaction dto) { entity.setId(dto.getId()); } - BankAccountEntity senderBankAccountEntity = bankAccountRepository.findByIdOptional(dto.getSenderAccountId()) - .orElseThrow(() -> new NoSuchElementException("Transaction does not have sender bank account entity.")); + BankAccountEntity emitterBankAccountEntity = bankAccountRepository.findByIdOptional(dto.getEmitterAccountId()) + .orElseThrow(() -> new NoSuchElementException("Transaction does not have emitter bank account entity.")); BankAccountEntity receiverBankAccountEntity = bankAccountRepository.findByIdOptional(dto.getReceiverAccountId()) .orElseThrow(() -> new NoSuchElementException("Transaction does not have receiver bank account entity.")); - entity.setSenderBankAccountEntity(senderBankAccountEntity); + entity.setEmitterBankAccountEntity(emitterBankAccountEntity); entity.setReceiverBankAccountEntity(receiverBankAccountEntity); entity.setAmount(dto.getAmount()); entity.setCurrency(dto.getCurrency()); diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionRepository.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionRepository.java index 46fdfc40..f4c13f2f 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionRepository.java +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionRepository.java @@ -8,10 +8,11 @@ import io.quarkus.panache.common.Sort; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.transaction.Transactional; import org.jboss.logging.Logger; -import javax.transaction.Transactional; -import javax.transaction.Transactional.TxType; import java.util.Optional; import java.util.PriorityQueue; import java.util.Queue; @@ -24,20 +25,26 @@ * @author Clément Gibert * */ + @RequestScoped public class TransactionRepository implements TransactionPersistencePort, PanacheRepositoryBase { private static final Logger logger = Logger.getLogger(TransactionRepository.class); + + @PersistenceContext + private EntityManager entityManager; @Inject TransactionMapper transactionMapper; @Override + @Transactional public Optional findById(long id) { return findByIdOptional(id).map(transactionMapper::toDto); } @Override + @Transactional public Set getAll() { return findAll(Sort.by("status")).stream() .map(transactionEntity -> transactionMapper.toDto(transactionEntity)) @@ -46,6 +53,7 @@ public Set getAll() { @Override + @Transactional public Set findAllByStatus(TransactionStatus transactionStatus) { return findAll(Sort.by("status")).stream() .filter(transaction -> transaction.getStatus().equals(transactionStatus)) @@ -54,6 +62,7 @@ public Set findAllByStatus(TransactionStatus transactionStatus) { } @Override + @Transactional public Queue findUnprocessedTransactions() { return find("#TransactionEntity.findUnprocessed", Parameters.with("status", TransactionStatus.UNPROCESSED).map()) @@ -63,15 +72,15 @@ public Queue findUnprocessedTransactions() { } @Override - @Transactional(TxType.MANDATORY) + @Transactional public Transaction create(Transaction transaction) { - getEntityManager().persist(transactionMapper.toEntity(transaction)); - logger.info("Transaction " + transaction.getId() + " created"); + entityManager.persist(transactionMapper.toEntity(transaction)); + logger.info("Transaction from " + transaction.getEmitterAccountId() + " to " + transaction.getReceiverAccountId() + " created"); return transaction; } @Override - @Transactional(TxType.MANDATORY) + @Transactional public Transaction update(Transaction transaction) { getEntityManager().merge(transactionMapper.toEntity(transaction)); logger.info("Transaction " + transaction.getId() + " updated"); @@ -79,7 +88,7 @@ public Transaction update(Transaction transaction) { } @Override - @Transactional(TxType.MANDATORY) + @Transactional public Optional deleteById(long id) { Optional entityOptional = findByIdOptional(id); if (entityOptional.isPresent()) { diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionServiceImpl.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionServiceImpl.java index 9f38cc96..689c7c55 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionServiceImpl.java +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionServiceImpl.java @@ -1,17 +1,13 @@ package com.cdx.bas.application.bank.transaction; -import com.cdx.bas.domain.bank.transaction.Transaction; -import com.cdx.bas.domain.bank.transaction.TransactionException; -import com.cdx.bas.domain.bank.transaction.TransactionPersistencePort; -import com.cdx.bas.domain.bank.transaction.TransactionServicePort; -import com.cdx.bas.domain.transaction.*; +import com.cdx.bas.domain.bank.transaction.*; import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; import com.cdx.bas.domain.bank.transaction.type.TransactionTypeProcessingServicePort; import com.cdx.bas.domain.bank.transaction.validation.TransactionValidator; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; +import jakarta.transaction.Transactional; -import javax.transaction.Transactional; import java.util.Set; import static com.cdx.bas.domain.bank.transaction.type.TransactionType.CREDIT; @@ -30,27 +26,55 @@ public class TransactionServiceImpl implements TransactionServicePort { TransactionTypeProcessingServicePort transactionTypeProcessingService; @Override + @Transactional public Set getAll() { return transactionRepository.getAll(); } @Override + @Transactional public Set findAllByStatus(String status) throws IllegalArgumentException { TransactionStatus transactionStatus = TransactionStatus.fromString(status); return transactionRepository.findAllByStatus(transactionStatus); } @Override - @Transactional(Transactional.TxType.MANDATORY) - public void createTransaction(Transaction newTransaction) throws TransactionException { - transactionValidator.validateNewTransaction(newTransaction); - transactionRepository.create(newTransaction); + @Transactional + public void createTransaction(NewTransaction newTransaction) throws TransactionException { + Transaction transactionToCreate = Transaction.builder() + .emitterAccountId(newTransaction.getEmitterAccountId()) + .receiverAccountId(newTransaction.getReceiverAccountId()) + .amount(newTransaction.getAmount()) + .currency(newTransaction.getCurrency()) + .type(newTransaction.getType()) + .status(newTransaction.getStatus()) + .date(newTransaction.getDate()) + .label(newTransaction.getLabel()) + .metadata(newTransaction.getMetadata()) + .build(); + transactionValidator.validateNewTransaction(transactionToCreate); + transactionRepository.create(transactionToCreate); + } + + @Override + @Transactional + public Transaction findTransaction(Long transactionId) { + return transactionRepository.findById(transactionId).orElse(null); + } + + @Override + public void process(Transaction transaction) { + if (CREDIT.equals(transaction.getType())) { + transactionTypeProcessingService.credit(transaction); + } else if (DEPOSIT.equals(transaction.getType())) { + transactionTypeProcessingService.deposit(transaction); + } } @Override public Transaction mergeTransactions(Transaction oldTransaction, Transaction newTransaction){ oldTransaction.setId(newTransaction.getId()); - oldTransaction.setSenderAccountId(newTransaction.getSenderAccountId()); + oldTransaction.setEmitterAccountId(newTransaction.getEmitterAccountId()); oldTransaction.setReceiverAccountId(newTransaction.getReceiverAccountId()); oldTransaction.setAmount(newTransaction.getAmount()); oldTransaction.setCurrency(newTransaction.getCurrency()); @@ -61,13 +85,4 @@ public Transaction mergeTransactions(Transaction oldTransaction, Transaction new oldTransaction.setMetadata(newTransaction.getMetadata()); return oldTransaction; } - - @Override - public void process(Transaction transaction) { - if (CREDIT.equals(transaction.getType())) { - transactionTypeProcessingService.credit(transaction); - } else if (DEPOSIT.equals(transaction.getType())) { - transactionTypeProcessingService.deposit(transaction); - } - } } diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/type/TransactionTypeProcessingServiceImpl.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/type/TransactionTypeProcessingServiceImpl.java index 448a56fe..04216489 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/type/TransactionTypeProcessingServiceImpl.java +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/type/TransactionTypeProcessingServiceImpl.java @@ -6,8 +6,8 @@ import com.cdx.bas.domain.bank.account.BankAccountServicePort; import com.cdx.bas.domain.bank.transaction.Transaction; import com.cdx.bas.domain.bank.transaction.TransactionException; -import com.cdx.bas.domain.bank.transaction.type.TransactionTypeProcessingServicePort; import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; +import com.cdx.bas.domain.bank.transaction.type.TransactionTypeProcessingServicePort; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; @@ -18,7 +18,7 @@ import java.util.Map; import java.util.NoSuchElementException; -import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.REFUSED; +import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.*; import static jakarta.transaction.Transactional.TxType.REQUIRES_NEW; @RequestScoped @@ -39,20 +39,20 @@ public Transaction credit(Transaction transaction) { logger.info("Transaction " + transaction.getId() + " processing..."); try { - BankAccount emitterBankAccount = bankAccountService.findBankAccount(transaction.getSenderAccountId()); + BankAccount emitterBankAccount = bankAccountService.findBankAccount(transaction.getEmitterAccountId()); BankAccount receiverBankAccount = bankAccountService.findBankAccount(transaction.getReceiverAccountId()); Transaction currentTransaction = transactionStatusService.setAsOutstanding(transaction); logger.info("Process credit transaction " + currentTransaction.getId() - + " from bank account " + currentTransaction.getSenderAccountId() + + " from bank account " + currentTransaction.getEmitterAccountId() + " with amount " + currentTransaction.getAmount() + " to bank account " + currentTransaction.getReceiverAccountId()); Map metadata = new HashMap<>(); - metadata.put("sender_amount_before", emitterBankAccount.getBalance().getAmount().toString()); + metadata.put("emitter_amount_before", emitterBankAccount.getBalance().getAmount().toString()); metadata.put("receiver_amount_before", receiverBankAccount.getBalance().getAmount().toString()); bankAccountService.transferAmountBetweenAccounts(currentTransaction, emitterBankAccount, receiverBankAccount); - metadata.put("sender_amount_after", emitterBankAccount.getBalance().getAmount().toString()); + metadata.put("emitter_amount_after", emitterBankAccount.getBalance().getAmount().toString()); metadata.put("receiver_amount_after", receiverBankAccount.getBalance().getAmount().toString()); Transaction completedTransaction = transactionStatusService.setStatus(currentTransaction, COMPLETED, metadata); diff --git a/application/src/main/java/com/cdx/bas/application/scheduler/SchedulerImpl.java b/application/src/main/java/com/cdx/bas/application/scheduler/SchedulerImpl.java index 9860113b..e5d3e7a3 100644 --- a/application/src/main/java/com/cdx/bas/application/scheduler/SchedulerImpl.java +++ b/application/src/main/java/com/cdx/bas/application/scheduler/SchedulerImpl.java @@ -39,20 +39,22 @@ public PriorityQueue getTransactionQueue() { @Override @Scheduled(every = "{scheduler.every}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) public void processQueue() { - if (isActivation()) { + if (isActivated()) { logger.info("Scheduler start every " + getEvery()); if (getTransactionQueue().isEmpty()) { getTransactionQueue().addAll(transactionRepository.findUnprocessedTransactions()); - logger.info("Queue size: " + transactionQueue.size()); - getTransactionQueue().forEach(transaction -> { - transactionService.process(transaction); - }); } + + logger.info("Queue size: " + transactionQueue.size()); + getTransactionQueue().forEach(transaction -> { + transactionService.process(transaction); + getTransactionQueue().remove(transaction); + }); logger.info("Scheduler end"); } } - public boolean isActivation() { + public boolean isActivated() { return activation; } diff --git a/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountRepositoryTest.java b/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountRepositoryTest.java index c01b9086..f5f54d9a 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountRepositoryTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountRepositoryTest.java @@ -12,6 +12,7 @@ import io.quarkus.test.h2.H2DatabaseTestResource; import io.quarkus.test.junit.QuarkusTest; import jakarta.inject.Inject; +import jakarta.transaction.Transactional; import org.junit.jupiter.api.Test; import java.math.BigDecimal; @@ -34,6 +35,7 @@ public class BankAccountRepositoryTest { @Test + @Transactional public void findById_shouldReturnBankAccount_whenAccountIsFound() { long accountId = 1L; Instant date = Instant.now(); @@ -48,6 +50,7 @@ public void findById_shouldReturnBankAccount_whenAccountIsFound() { } @Test + @Transactional public void findById_shouldReturnEmptyOptional_whenAccountIsNotFound() { Optional optionalBankAccount = bankAccountRepository.findById(99999L); diff --git a/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountServiceImplTest.java b/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountServiceImplTest.java index 48af5a6a..5ae8b2b5 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountServiceImplTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountServiceImplTest.java @@ -1,18 +1,31 @@ package com.cdx.bas.application.bank.account; -import com.cdx.bas.domain.bank.account.*; +import com.cdx.bas.application.bank.transaction.TransactionTestUtils; +import com.cdx.bas.domain.bank.account.BankAccount; +import com.cdx.bas.domain.bank.account.BankAccountPersistencePort; +import com.cdx.bas.domain.bank.account.BankAccountServicePort; import com.cdx.bas.domain.bank.account.validation.BankAccountValidator; +import com.cdx.bas.domain.bank.transaction.Transaction; import com.cdx.bas.domain.bank.transaction.TransactionServicePort; -import com.cdx.bas.domain.transaction.*; import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; +import com.cdx.bas.domain.money.Money; import io.quarkus.test.InjectMock; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.h2.H2DatabaseTestResource; import io.quarkus.test.junit.QuarkusTest; import jakarta.inject.Inject; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Optional; + +import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.UNPROCESSED; +import static com.cdx.bas.domain.bank.transaction.type.TransactionType.CREDIT; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.verifyNoMoreInteractions; @QuarkusTest @QuarkusTestResource(H2DatabaseTestResource.class) @@ -27,12 +40,87 @@ public class BankAccountServiceImplTest { @InjectMock TransactionServicePort transactionService; - @InjectMock - TransactionStatusServicePort transactionStatusService; - @Inject BankAccountServicePort bankAccountService; + @Test + public void findBankAccount_shouldFindBankAccount_whenBankAccountExists() { + BankAccount bankAccount = BankAccountTestUtils.createBankAccountUtils(99L, Money.of(new BigDecimal("100"))); + + when(bankAccountRepository.findById(1L)).thenReturn(Optional.of(bankAccount)); + + BankAccount actualBankAccount = bankAccountService.findBankAccount(1L); + assertThat(actualBankAccount).isEqualTo(bankAccount); + verify(bankAccountRepository).findById(1L); + verifyNoMoreInteractions(bankAccountRepository); + verifyNoInteractions(transactionService); + } + + @Test + public void findBankAccount_shouldReturnNull_whenBankAccountDoesNotExist() { + when(bankAccountRepository.findById(1L)).thenReturn(Optional.empty()); + + BankAccount actualBankAccount = bankAccountService.findBankAccount(1L); + assertThat(actualBankAccount).isNull(); + verify(bankAccountRepository).findById(1L); + verifyNoMoreInteractions(bankAccountRepository, bankAccountValidator); + verifyNoInteractions(transactionService); + } + + @Test + public void addTransaction_shouldAddTransactionToBankAccount_whenTransactionDoesNotExist() { + Instant timestamp = Instant.now(); + BankAccount bankAccount = BankAccountTestUtils.createBankAccountUtils(99L, Money.of(new BigDecimal("100"))); + Transaction transaction = TransactionTestUtils.createTransaction(10L, 99L, timestamp); + + + BankAccount actualBankAccount = bankAccountService.addTransaction(transaction, bankAccount); + assertThat(actualBankAccount.getIssuedTransactions().size()).isEqualTo(1); + assertThat(actualBankAccount.getIssuedTransactions()).contains(transaction); + + verifyNoInteractions(transactionService, bankAccountValidator, bankAccountRepository); + } + + @Test + public void addTransaction_shouldUpdateTransactionToBankAccount_whenTransactionExists() { + Instant timestamp = Instant.now(); + BankAccount bankAccount = BankAccountTestUtils.createBankAccountUtils(99L, Money.of(new BigDecimal("100"))); + Transaction transaction = TransactionTestUtils.createTransaction(10L, 99L, timestamp); + bankAccount.getIssuedTransactions().add(transaction); + + when(transactionService.mergeTransactions(transaction, transaction)).thenReturn(transaction); + + BankAccount actualBankAccount = bankAccountService.addTransaction(transaction, bankAccount); + assertThat(actualBankAccount.getIssuedTransactions().size()).isEqualTo(1); + assertThat(actualBankAccount.getIssuedTransactions()).contains(transaction); + + verify(transactionService).mergeTransactions(transaction, transaction); + verifyNoMoreInteractions(transactionService); + verifyNoInteractions(bankAccountValidator, bankAccountRepository); + } + + @Test + public void updateBankAccount_shouldUpdateBankAccount_whenHasValidBankAccount() { + BankAccount bankAccount = BankAccountTestUtils.createBankAccountUtils(99L, Money.of(new BigDecimal("100"))); + + BankAccount actualBankAccount = bankAccountService.updateBankAccount(bankAccount); + assertThat(actualBankAccount).isNull(); + verify(bankAccountValidator).validateBankAccount(bankAccount); + verify(bankAccountRepository).update(bankAccount); + verifyNoMoreInteractions(bankAccountValidator, bankAccountRepository); + verifyNoInteractions(transactionService); + } + + @Test + public void transferAmountBetweenAccounts_shouldAddAmountOfMoneyCorrespondingToTransactionAmount_whenTransactionHasAmount() { + Transaction transaction = TransactionTestUtils.createTransaction(10L, 99L, Instant.now()); + BankAccount bankAccountEmitter = BankAccountTestUtils.createBankAccountUtils(99L, Money.of(new BigDecimal("100"))); + BankAccount bankAccountReceiver = BankAccountTestUtils.createBankAccountUtils(100L, Money.of(new BigDecimal("0"))); + bankAccountService.transferAmountBetweenAccounts(transaction, bankAccountEmitter, bankAccountReceiver); + assertThat(bankAccountEmitter.getBalance().getAmount()).isEqualTo(new BigDecimal("0")); + assertThat(bankAccountReceiver.getBalance().getAmount()).isEqualTo(new BigDecimal("100")); + verifyNoInteractions(transactionService, bankAccountValidator, bankAccountRepository); + } } diff --git a/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountTestUtils.java b/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountTestUtils.java index 7d6d0c64..470246a1 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountTestUtils.java +++ b/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountTestUtils.java @@ -8,10 +8,7 @@ import com.cdx.bas.domain.bank.transaction.Transaction; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; +import java.util.*; public class BankAccountTestUtils { @@ -34,8 +31,8 @@ public static BankAccount createBankAccountUtils(long accountId, Money amountOfM List customersId = new ArrayList<>(); customersId.add(99L); bankAccount.setCustomersId(customersId); - HashSet transactionHistory = new HashSet<>(); - Collections.addAll(transactionHistory, transactions); + Set transactionHistory = new HashSet<>(); + transactionHistory.addAll(List.of(transactions)); bankAccount.setIssuedTransactions(transactionHistory); return bankAccount; } diff --git a/application/src/test/java/com/cdx/bas/application/bank/customer/CustomerRepositoryTest.java b/application/src/test/java/com/cdx/bas/application/bank/customer/CustomerRepositoryTest.java index f8aa6fc9..b7f2e150 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/customer/CustomerRepositoryTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/customer/CustomerRepositoryTest.java @@ -15,6 +15,7 @@ import static com.cdx.bas.domain.bank.customer.gender.Gender.FEMALE; import static com.cdx.bas.domain.bank.customer.gender.Gender.MALE; +import static com.cdx.bas.domain.bank.customer.maritalstatus.MaritalStatus.*; import static org.assertj.core.api.Assertions.assertThat; @QuarkusTest diff --git a/application/src/test/java/com/cdx/bas/application/bank/customer/GenderConverterTest.java b/application/src/test/java/com/cdx/bas/application/bank/customer/GenderConverterTest.java index dd1404d6..7939c9a6 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/customer/GenderConverterTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/customer/GenderConverterTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static com.cdx.bas.domain.bank.customer.gender.Gender.*; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/application/src/test/java/com/cdx/bas/application/bank/customer/MaritalStatusConverterTest.java b/application/src/test/java/com/cdx/bas/application/bank/customer/MaritalStatusConverterTest.java index c2f4ddca..a152fc7f 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/customer/MaritalStatusConverterTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/customer/MaritalStatusConverterTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static com.cdx.bas.domain.bank.customer.maritalstatus.MaritalStatus.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionMapperTest.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionMapperTest.java index dea4d5f0..97c5b8cd 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionMapperTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionMapperTest.java @@ -15,7 +15,10 @@ import java.math.BigDecimal; import java.time.Instant; -import java.util.*; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; @@ -32,11 +35,11 @@ public class TransactionMapperTest { TransactionMapper transactionMapper; @Test - public void toDto_shouldThrowException_whenTransactionDtoDoesNotHaveSenderBankAccount() { + public void toDto_shouldThrowException_whenTransactionDtoDoesNotHaveReceiverBankAccount() { try { BankAccountEntity bankAccountEntity = new BankAccountEntity(); TransactionEntity transactionEntity = new TransactionEntity(); - transactionEntity.setSenderBankAccountEntity(bankAccountEntity); + transactionEntity.setEmitterBankAccountEntity(bankAccountEntity); bankAccountEntity.setId(10L); transactionMapper.toDto(transactionEntity); fail(); @@ -46,27 +49,27 @@ public void toDto_shouldThrowException_whenTransactionDtoDoesNotHaveSenderBankAc } @Test - public void toDto_shouldThrowException_whenTransactionDto_doesNotHave_receiverBankAccount() { + public void toDto_shouldThrowException_whenTransactionDtoDoesNotHaveEmitterBankAccount() { try { transactionMapper.toDto(new TransactionEntity()); fail(); } catch (NoSuchElementException exception) { - assertThat(exception.getMessage()).hasToString("Transaction does not have sender bank account."); + assertThat(exception.getMessage()).hasToString("Transaction does not have emitter bank account."); } } @Test - public void toEntity_shouldThrowException_whenTransactionDto_doesNotHave_senderBankAccount() { + public void toEntity_shouldThrowException_whenTransactionDtoDoesNotHaveEmitterBankAccount() { try { Transaction transaction = Transaction.builder() .id(10L) - .senderAccountId(null) + .emitterAccountId(null) .build(); transactionMapper.toEntity(transaction); fail(); } catch (NoSuchElementException exception) { - assertThat(exception.getMessage()).hasToString("Transaction does not have sender bank account entity."); + assertThat(exception.getMessage()).hasToString("Transaction does not have emitter bank account entity."); } } @@ -76,7 +79,7 @@ public void toEntity_shouldThrowException_whenTransactionDto_doesNotHave_receive try { Transaction transaction = Transaction.builder() .id(10L) - .senderAccountId(99L) + .emitterAccountId(99L) .receiverAccountId(null) .build(); @@ -92,15 +95,15 @@ public void toEntity_shouldThrowException_whenTransactionDto_doesNotHave_receive @Test public void toDto_shouldMapEntityValues_whenEntityHasValues() { Instant date = Instant.now(); - long senderAccountId = 99L; + long emitterAccountId = 99L; long receiverAccountId = 77L; Map metadata = Map.of("amount_before", "0", "amount_after", "100"); - TransactionEntity transactionEntity = TransactionTestUtils.createTransactionEntityUtils(senderAccountId, receiverAccountId, date); + TransactionEntity transactionEntity = TransactionTestUtils.createTransactionEntityUtils(emitterAccountId, receiverAccountId, date); Transaction dto = transactionMapper.toDto(transactionEntity); assertThat(dto.getId()).isEqualTo(10L); - assertThat(dto.getSenderAccountId()).isEqualTo(senderAccountId); + assertThat(dto.getEmitterAccountId()).isEqualTo(emitterAccountId); assertThat(dto.getReceiverAccountId()).isEqualTo(receiverAccountId); assertThat(dto.getAmount()).isEqualTo(new BigDecimal("100")); assertThat(dto.getCurrency()).isEqualTo("EUR"); @@ -114,24 +117,24 @@ public void toDto_shouldMapEntityValues_whenEntityHasValues() { @Test public void toEntity_shouldMapEntityValues_whenDtoHasValues() { Instant date = Instant.now(); - long senderAccountId = 99L; + long emitterAccountId = 99L; long receiverAccountId = 77L; String strMetadata = "{\"amount_after\":\"100\",\"amount_before\":\"0\"}"; Map metadata = new HashMap<>(); metadata.put("amount_before", "0"); metadata.put("amount_after", "100"); - BankAccountEntity senderBankAccountEntity = BankAccountTestUtils.createBankAccountEntity(senderAccountId); + BankAccountEntity emitterBankAccountEntity = BankAccountTestUtils.createBankAccountEntity(emitterAccountId); BankAccountEntity receiverBankAccountEntity = BankAccountTestUtils.createBankAccountEntity(receiverAccountId); - Transaction transaction = TransactionTestUtils.createTransactionUtils(senderAccountId, receiverAccountId, date); + Transaction transaction = TransactionTestUtils.createTransactionUtils(emitterAccountId, receiverAccountId, date); transaction.setMetadata(metadata); - when(bankAccountRepository.findByIdOptional(99L)).thenReturn(Optional.of(senderBankAccountEntity)); + when(bankAccountRepository.findByIdOptional(99L)).thenReturn(Optional.of(emitterBankAccountEntity)); when(bankAccountRepository.findByIdOptional(77L)).thenReturn(Optional.of(receiverBankAccountEntity)); TransactionEntity entity = transactionMapper.toEntity(transaction); assertThat(entity.getId()).isEqualTo(10L); - assertThat(entity.getSenderBankAccountEntity()).usingRecursiveComparison().isEqualTo(senderBankAccountEntity); + assertThat(entity.getEmitterBankAccountEntity()).usingRecursiveComparison().isEqualTo(emitterBankAccountEntity); assertThat(entity.getReceiverBankAccountEntity()).usingRecursiveComparison().isEqualTo(receiverBankAccountEntity); assertThat(entity.getAmount()).usingRecursiveComparison().isEqualTo(new BigDecimal(100)); assertThat(entity.getCurrency()).isEqualTo("EUR"); diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionRepositoryTest.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionRepositoryTest.java index e522ae5c..78097ce6 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionRepositoryTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionRepositoryTest.java @@ -40,7 +40,7 @@ public void findById_shouldFindTransaction_whenIdIsFound() { ZoneOffset.ofHours(1)).toInstant(); Optional expectedTransaction = Optional.of(Transaction.builder() .id(1L) - .senderAccountId(1L) + .emitterAccountId(1L) .receiverAccountId(2L) .amount(new BigDecimal("1600.00")) .currency("EUR") @@ -48,7 +48,7 @@ public void findById_shouldFindTransaction_whenIdIsFound() { .status(COMPLETED) .date(expectedInstant) .label("transaction 1") - .metadata(Map.of("sender_amount_before", "2000", "receiver_amount_before", "0", "sender_amount_after", "400", "receiver_amount_after", "1600")) + .metadata(Map.of("emitter_amount_before", "2000", "receiver_amount_before", "0", "emitter_amount_after", "400", "receiver_amount_after", "1600")) .build()); Optional actualTransaction = transactionRepository.findById(1); @@ -103,7 +103,7 @@ public void update_shouldMergeTransaction() { Transaction expectedTransaction = new Transaction(2L, 6L, 3L, new BigDecimal("9200.00"), "EUR", CREDIT, UNPROCESSED, Instant.parse("2024-11-10T15:00:00+02:00"), - "transaction to process", Map.of("sender_amount_before", "9200", "receiver_amount_before", "10000", "sender_amount_after", "0", "receiver_amount_after", "19200")); + "transaction to process", Map.of("emitter_amount_before", "9200", "receiver_amount_before", "10000", "emitter_amount_after", "0", "receiver_amount_after", "19200")); Optional optionalExpectedTransaction = Optional.of(expectedTransaction); Transaction updatedTransaction = transactionRepository.update(expectedTransaction); diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionServiceImplTest.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionServiceImplTest.java index 61887344..24a0298f 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionServiceImplTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionServiceImplTest.java @@ -1,10 +1,6 @@ package com.cdx.bas.application.bank.transaction; -import com.cdx.bas.domain.bank.transaction.Transaction; -import com.cdx.bas.domain.bank.transaction.TransactionException; -import com.cdx.bas.domain.bank.transaction.TransactionPersistencePort; -import com.cdx.bas.domain.bank.transaction.TransactionServicePort; -import com.cdx.bas.domain.transaction.*; +import com.cdx.bas.domain.bank.transaction.*; import com.cdx.bas.domain.bank.transaction.type.TransactionTypeProcessingServicePort; import com.cdx.bas.domain.bank.transaction.validation.TransactionValidator; import io.quarkus.test.InjectMock; @@ -16,10 +12,12 @@ import java.math.BigDecimal; import java.time.Instant; +import java.util.Optional; import java.util.Set; import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.COMPLETED; import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.UNPROCESSED; +import static com.cdx.bas.domain.bank.transaction.type.TransactionType.*; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; @@ -27,7 +25,7 @@ @QuarkusTestResource(H2DatabaseTestResource.class) public class TransactionServiceImplTest { @InjectMock - TransactionPersistencePort transactionPersistencePort; + TransactionPersistencePort transactionRepository; @InjectMock TransactionTypeProcessingServicePort transactionTypeProcessingServicePort; @@ -47,12 +45,12 @@ public void getAll_shouldReturnAllTransactions_whenRepositoryReturnsTransactions Transaction.builder().id(3L).build() ); - when(transactionPersistencePort.getAll()).thenReturn(transactions); + when(transactionRepository.getAll()).thenReturn(transactions); Set actual = transactionService.getAll(); assertThat(actual).isEqualTo(transactions); - verify(transactionPersistencePort).getAll(); - verifyNoMoreInteractions(transactionPersistencePort); + verify(transactionRepository).getAll(); + verifyNoMoreInteractions(transactionRepository); } @Test @@ -63,12 +61,12 @@ public void findAllByStatus_shouldReturnTransactionCorrespondingToStatus_whenSta Transaction.builder().id(3L).status(COMPLETED).build() ); - when(transactionPersistencePort.findAllByStatus(COMPLETED)).thenReturn(transactions); + when(transactionRepository.findAllByStatus(COMPLETED)).thenReturn(transactions); Set actual = transactionService.findAllByStatus("COMPLETED"); assertThat(actual).isEqualTo(transactions); - verify(transactionPersistencePort).findAllByStatus(COMPLETED); - verifyNoMoreInteractions(transactionPersistencePort); + verify(transactionRepository).findAllByStatus(COMPLETED); + verifyNoMoreInteractions(transactionRepository); } @Test @@ -78,16 +76,18 @@ public void findAllByStatus_shouldThrowException_whenStatusIsInvalid() { } catch (IllegalArgumentException exception) { assertThat(exception.getMessage()).isEqualTo("Invalid status: INVALID"); } - verifyNoInteractions(transactionPersistencePort); + verifyNoInteractions(transactionRepository); } @Test public void createTransaction_shouldCreateTransaction_whenTransactionIsValid() { - Transaction newTransaction = TransactionTestUtils.createTransactionUtils(1L, 100L, Instant.now(), "transaction test"); + Instant timestamp = Instant.now(); + NewTransaction newTransaction = TransactionTestUtils.createNewTransactionUtils(100L, timestamp, "transaction test"); + Transaction transactionToCreate = TransactionTestUtils.createTransactionUtils(null, 100L, timestamp, "transaction test"); transactionService.createTransaction(newTransaction); - verify(transactionValidator).validateNewTransaction(newTransaction); - verify(transactionPersistencePort).create(newTransaction); - verifyNoMoreInteractions(transactionValidator, transactionPersistencePort); + verify(transactionValidator).validateNewTransaction(transactionToCreate); + verify(transactionRepository).create(transactionToCreate); + verifyNoMoreInteractions(transactionValidator, transactionRepository); } @Test @@ -96,13 +96,13 @@ public void createTransaction_shouldThrowException_whenTransactionIsInvalid() { doThrow(new TransactionException("invalid transaction...")).when(transactionValidator).validateNewTransaction(invalidTransaction); try { - transactionService.createTransaction(new Transaction()); + transactionService.createTransaction(new NewTransaction()); } catch (TransactionException exception) { assertThat(exception.getMessage()).isEqualTo("invalid transaction..."); } verify(transactionValidator).validateNewTransaction(invalidTransaction); verifyNoMoreInteractions(transactionValidator); - verifyNoInteractions(transactionPersistencePort); + verifyNoInteractions(transactionRepository); } @Test @@ -110,7 +110,7 @@ public void mergeTransaction_shouldMergeOldTransactionWithNewTransaction_whenOld Transaction oldTransaction = Transaction.builder() .id(1L) .amount(new BigDecimal(100)) - .senderAccountId(10L) + .emitterAccountId(10L) .receiverAccountId(11L) .type(CREDIT) .status(UNPROCESSED) @@ -124,7 +124,7 @@ public void mergeTransaction_shouldMergeOldTransactionWithNewTransaction_whenOld Transaction newTransaction = Transaction.builder() .id(2L) .amount(bigDecimalAfter) - .senderAccountId(20L) + .emitterAccountId(20L) .receiverAccountId(22L) .type(DEBIT) .status(UNPROCESSED) @@ -136,7 +136,7 @@ public void mergeTransaction_shouldMergeOldTransactionWithNewTransaction_whenOld oldTransaction.setId(2L); oldTransaction.setAmount(bigDecimalAfter); - oldTransaction.setSenderAccountId(20L); + oldTransaction.setEmitterAccountId(20L); oldTransaction.setReceiverAccountId(22L); oldTransaction.setType(DEBIT); oldTransaction.setStatus(UNPROCESSED); @@ -146,11 +146,42 @@ public void mergeTransaction_shouldMergeOldTransactionWithNewTransaction_whenOld } @Test - public void processTransaction_shouldProcessBankAccountCredit_whenTransactionHasCreditType() { + public void findTransaction_shouldFindTransaction_whenTransactionExists() { Transaction transaction = Transaction.builder() .id(1L) .amount(new BigDecimal(100)) - .senderAccountId(100L) + .emitterAccountId(100L) + .receiverAccountId(200L) + .type(CREDIT) + .status(UNPROCESSED) + .date(Instant.now()) + .label("deposit of 100 euros") + .build(); + + when(transactionRepository.findById(1L)).thenReturn(Optional.of(transaction)); + + Transaction actualTransaction = transactionService.findTransaction(1L); + assertThat(actualTransaction).isEqualTo(transaction); + verify(transactionRepository).findById(1L); + verifyNoMoreInteractions(transactionRepository); + } + + @Test + public void findTransaction_shouldReturnNull_whenTransactionDoesNotExist() { + when(transactionRepository.findById(1L)).thenReturn(Optional.empty()); + + Transaction actualTransaction = transactionService.findTransaction(1L); + assertThat(actualTransaction).isNull(); + verify(transactionRepository).findById(1L); + verifyNoMoreInteractions(transactionRepository); + } + + @Test + public void process_shouldProcessBankAccountCredit_whenTransactionHasCreditType() { + Transaction transaction = Transaction.builder() + .id(1L) + .amount(new BigDecimal(100)) + .emitterAccountId(100L) .receiverAccountId(200L) .type(CREDIT) .status(UNPROCESSED) @@ -164,11 +195,11 @@ public void processTransaction_shouldProcessBankAccountCredit_whenTransactionHas } @Test - public void processTransaction_shouldProcessBankAccountDeposit_whenTransactionHasDepositType() { + public void process_shouldProcessBankAccountDeposit_whenTransactionHasDepositType() { Transaction transaction = Transaction.builder() .id(1L) .amount(new BigDecimal(100)) - .senderAccountId(100L) + .emitterAccountId(100L) .receiverAccountId(200L) .type(DEPOSIT) .status(UNPROCESSED) diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionStatusServiceImplTest.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionStatusServiceImplTest.java index 3d80f961..c5e95420 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionStatusServiceImplTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionStatusServiceImplTest.java @@ -12,6 +12,7 @@ import java.util.HashMap; import java.util.Map; +import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.*; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionTestUtils.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionTestUtils.java index 3d32fce4..9e057c31 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionTestUtils.java +++ b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionTestUtils.java @@ -1,6 +1,7 @@ package com.cdx.bas.application.bank.transaction; import com.cdx.bas.application.bank.account.BankAccountTestUtils; +import com.cdx.bas.domain.bank.transaction.NewTransaction; import com.cdx.bas.domain.bank.transaction.Transaction; import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; import com.cdx.bas.domain.bank.transaction.type.TransactionType; @@ -13,14 +14,14 @@ import static com.cdx.bas.domain.bank.transaction.type.TransactionType.CREDIT; public class TransactionTestUtils { - public static Transaction createTransactionUtils(long senderAccountId, long receiverAccountId, + public static Transaction createTransactionUtils(long emitterAccountId, long receiverAccountId, BigDecimal amount, TransactionStatus status, Instant date, Map metadata) { return Transaction.builder() .id(1L) .amount(amount) .currency("EUR") - .senderAccountId(senderAccountId) + .emitterAccountId(emitterAccountId) .receiverAccountId(receiverAccountId) .type(TransactionType.CREDIT) .status(status) @@ -29,11 +30,11 @@ public static Transaction createTransactionUtils(long senderAccountId, long rece .metadata(metadata).build(); } - public static Transaction createTransactionUtils(long id, long amount, Instant date, String label) { + public static Transaction createTransactionUtils(Long id, Long amount, Instant date, String label) { return Transaction.builder() .id(id) .amount(new BigDecimal(amount)) - .senderAccountId(99L) + .emitterAccountId(99L) .receiverAccountId(77L) .type(TransactionType.CREDIT) .status(TransactionStatus.UNPROCESSED) @@ -42,11 +43,23 @@ public static Transaction createTransactionUtils(long id, long amount, Instant d .build(); } - public static Transaction createTransactionUtils(long senderAccountId, long receiverAccountId, Instant instantDate) { + public static NewTransaction createNewTransactionUtils(long amount, Instant date, String label) { + return NewTransaction.builder() + .amount(new BigDecimal(amount)) + .emitterAccountId(99L) + .receiverAccountId(77L) + .type(TransactionType.CREDIT) + .status(TransactionStatus.UNPROCESSED) + .date(date) + .label(label) + .build(); + } + + public static Transaction createTransactionUtils(Long emitterAccountId, Long receiverAccountId, Instant instantDate) { Map metadata = Map.of("amount_before", "0", "amount_after", "100"); return Transaction.builder() .id(10L) - .senderAccountId(senderAccountId) + .emitterAccountId(emitterAccountId) .receiverAccountId(receiverAccountId) .amount(new BigDecimal(100)) .currency("EUR") @@ -58,13 +71,14 @@ public static Transaction createTransactionUtils(long senderAccountId, long rece .build(); } - public static Transaction createTransaction(long id, long senderAccountId, Instant instantDate) { + public static Transaction createTransaction(Long id, Long emitterBankAccount, Instant instantDate) { return Transaction.builder() .id(id) .type(CREDIT) - .senderAccountId(senderAccountId) + .emitterAccountId(emitterBankAccount) .receiverAccountId(77L) .amount(new BigDecimal("100")) + .currency("EUR") .status(ERROR) .date(instantDate) .label("transaction test") @@ -75,7 +89,7 @@ public static Transaction createTransactionUtils(long accountId, Instant instant Map metadata = Map.of("amount_before", "0", "amount_after", "350"); return Transaction.builder() .id(2L) - .senderAccountId(accountId) + .emitterAccountId(accountId) .receiverAccountId(77L) .amount(new BigDecimal(100)) .type(TransactionType.CREDIT) @@ -89,7 +103,7 @@ public static Transaction createTransactionUtils(long accountId, Instant instant public static TransactionEntity createTransactionEntity(long id, Instant instantDate) { TransactionEntity transactionEntity = new TransactionEntity(); transactionEntity.setId(id); - transactionEntity.setSenderBankAccountEntity(null); + transactionEntity.setEmitterBankAccountEntity(null); transactionEntity.setReceiverBankAccountEntity(null); transactionEntity.setAmount(new BigDecimal("100")); transactionEntity.setType(TransactionType.CREDIT); @@ -99,10 +113,10 @@ public static TransactionEntity createTransactionEntity(long id, Instant instant return transactionEntity; } - public static TransactionEntity createTransactionEntityUtils(long senderAccountId, long receiverAccountId, Instant instantDate) { + public static TransactionEntity createTransactionEntityUtils(long emitterAccountId, long receiverAccountId, Instant instantDate) { TransactionEntity transactionEntity = new TransactionEntity(); transactionEntity.setId(10L); - transactionEntity.setSenderBankAccountEntity(BankAccountTestUtils.createBankAccountEntity(senderAccountId)); + transactionEntity.setEmitterBankAccountEntity(BankAccountTestUtils.createBankAccountEntity(emitterAccountId)); transactionEntity.setReceiverBankAccountEntity(BankAccountTestUtils.createBankAccountEntity(receiverAccountId)); transactionEntity.setAmount(new BigDecimal("100")); transactionEntity.setCurrency("EUR"); diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionTypeProcessingServiceImplTest.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionTypeProcessingServiceImplTest.java index ee48b53f..8c284b86 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionTypeProcessingServiceImplTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionTypeProcessingServiceImplTest.java @@ -4,11 +4,11 @@ import com.cdx.bas.domain.bank.account.BankAccount; import com.cdx.bas.domain.bank.account.BankAccountException; import com.cdx.bas.domain.bank.account.BankAccountServicePort; -import com.cdx.bas.domain.money.Money; import com.cdx.bas.domain.bank.transaction.Transaction; import com.cdx.bas.domain.bank.transaction.TransactionException; import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; import com.cdx.bas.domain.bank.transaction.type.TransactionTypeProcessingServicePort; +import com.cdx.bas.domain.money.Money; import io.quarkus.test.InjectMock; import io.quarkus.test.junit.QuarkusTest; import jakarta.inject.Inject; @@ -21,6 +21,7 @@ import java.util.Map; import java.util.NoSuchElementException; +import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.*; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; @@ -37,47 +38,47 @@ class TransactionTypeProcessingServiceImplTest { TransactionTypeProcessingServicePort transactionProcessingService; @Test - public void credit_shouldThrowNoSuchElementException_whenSenderAccountIsNotFound() { - long senderAccountId = 99L; + public void credit_shouldThrowNoSuchElementException_whenEmitterAccountIsNotFound() { + long emitterAccountId = 99L; long receiverAccountId = 77L; Money amountOfMoney = Money.of(new BigDecimal("0")); Instant instantDate = Instant.now(); - Transaction transaction = TransactionTestUtils.createTransactionUtils(senderAccountId, receiverAccountId, new BigDecimal("1000"), UNPROCESSED, instantDate, new HashMap<>()); - Map metadata = Map.of("error", "Sender bank account 99 is not found."); - Transaction erroredTransaction = TransactionTestUtils.createTransactionUtils(senderAccountId, receiverAccountId, new BigDecimal("1000"), ERROR, instantDate, metadata); + Transaction transaction = TransactionTestUtils.createTransactionUtils(emitterAccountId, receiverAccountId, new BigDecimal("1000"), UNPROCESSED, instantDate, new HashMap<>()); + Map metadata = Map.of("error", "Emitter bank account 99 is not found."); + Transaction erroredTransaction = TransactionTestUtils.createTransactionUtils(emitterAccountId, receiverAccountId, new BigDecimal("1000"), ERROR, instantDate, metadata); - when(bankAccountService.findBankAccount(senderAccountId)).thenThrow(new NoSuchElementException("Sender bank account 99 is not found.")); + when(bankAccountService.findBankAccount(emitterAccountId)).thenThrow(new NoSuchElementException("Emitter bank account 99 is not found.")); when(transactionStatusService.setStatus(eq(transaction), eq(ERROR), eq(metadata))).thenReturn(erroredTransaction); Transaction returnedTransaction = transactionProcessingService.credit(transaction); assertThat(returnedTransaction).usingRecursiveComparison() - .isEqualTo(TransactionTestUtils.createTransactionUtils(senderAccountId, receiverAccountId, new BigDecimal("1000"), ERROR, instantDate, metadata)); - verify(bankAccountService).findBankAccount(eq(senderAccountId)); + .isEqualTo(TransactionTestUtils.createTransactionUtils(emitterAccountId, receiverAccountId, new BigDecimal("1000"), ERROR, instantDate, metadata)); + verify(bankAccountService).findBankAccount(eq(emitterAccountId)); verify(transactionStatusService).setStatus(eq(transaction), eq(ERROR), eq(metadata)); verifyNoMoreInteractions(bankAccountService, transactionStatusService); } @Test public void credit_shouldThrowNoSuchElementException_whenReceiverAccountIsNotFound() { - long senderAccountId = 99L; + long emitterAccountId = 99L; long receiverAccountId = 77L; Money amountOfMoney = Money.of(new BigDecimal("0")); Instant instantDate = Instant.now(); - Transaction transaction = TransactionTestUtils.createTransactionUtils(senderAccountId, receiverAccountId, new BigDecimal("1000"), UNPROCESSED, instantDate, new HashMap<>()); + Transaction transaction = TransactionTestUtils.createTransactionUtils(emitterAccountId, receiverAccountId, new BigDecimal("1000"), UNPROCESSED, instantDate, new HashMap<>()); Map metadata = Map.of("error", "Receiver bank account 77 is not found."); - Transaction erroredTransaction = TransactionTestUtils.createTransactionUtils(senderAccountId, receiverAccountId, new BigDecimal("1000"), ERROR, instantDate, metadata); - BankAccount senderBankAccount = BankAccountTestUtils.createBankAccountUtils(senderAccountId, "0", transaction); + Transaction erroredTransaction = TransactionTestUtils.createTransactionUtils(emitterAccountId, receiverAccountId, new BigDecimal("1000"), ERROR, instantDate, metadata); + BankAccount emitterBankAccount = BankAccountTestUtils.createBankAccountUtils(emitterAccountId, "0", transaction); - when(bankAccountService.findBankAccount(senderAccountId)).thenReturn(senderBankAccount); + when(bankAccountService.findBankAccount(emitterAccountId)).thenReturn(emitterBankAccount); when(bankAccountService.findBankAccount(receiverAccountId)).thenThrow(new NoSuchElementException("Receiver bank account 77 is not found.")); when(transactionStatusService.setStatus(eq(transaction), eq(ERROR), eq(metadata))).thenReturn(erroredTransaction); Transaction returnedTransaction = transactionProcessingService.credit(transaction); assertThat(returnedTransaction).usingRecursiveComparison() - .isEqualTo (TransactionTestUtils.createTransactionUtils(senderAccountId, receiverAccountId, new BigDecimal("1000"), ERROR, instantDate, metadata)); - verify(bankAccountService).findBankAccount(eq(senderAccountId)); + .isEqualTo (TransactionTestUtils.createTransactionUtils(emitterAccountId, receiverAccountId, new BigDecimal("1000"), ERROR, instantDate, metadata)); + verify(bankAccountService).findBankAccount(eq(emitterAccountId)); verify(bankAccountService).findBankAccount(eq(receiverAccountId)); verify(transactionStatusService).setStatus(eq(transaction), eq(ERROR), eq(metadata)); verifyNoMoreInteractions(bankAccountService, transactionStatusService); @@ -94,12 +95,12 @@ public void credit_shouldReturnCompletedTransaction_whenAccountsAreFoundAndCredi BankAccount emitterBankAccount = BankAccountTestUtils.createBankAccountUtils(emitterBankAccountId, amountOfMoney, transaction); BankAccount receiverBankAccount = BankAccountTestUtils.createBankAccountUtils(receiverAccountId, amountOfMoney); - Map metadataAfter = Map.of("sender_amount_before", "1000", + Map metadataAfter = Map.of("emitter_amount_before", "1000", "receiver_amount_before", "0", - "sender_amount_after", "0", + "emitter_amount_after", "0", "receiver_amount_after", "1000"); Transaction completedTransaction = TransactionTestUtils.createTransactionUtils(emitterBankAccountId, receiverAccountId, new BigDecimal("1000"), COMPLETED, instantDate, metadataAfter); - BankAccount updatedSenderBankAccount = BankAccountTestUtils.createBankAccountUtils(emitterBankAccountId, amountOfMoney, completedTransaction); + BankAccount updatedEmitterBankAccount = BankAccountTestUtils.createBankAccountUtils(emitterBankAccountId, amountOfMoney, completedTransaction); when(bankAccountService.findBankAccount(emitterBankAccountId)).thenReturn(emitterBankAccount); when(bankAccountService.findBankAccount(receiverAccountId)).thenReturn(receiverBankAccount); @@ -110,7 +111,7 @@ public void credit_shouldReturnCompletedTransaction_whenAccountsAreFoundAndCredi .thenReturn(new BigDecimal("0")) .thenReturn(new BigDecimal("1000")); when(transactionStatusService.setStatus(outstandingTransaction, COMPLETED, metadataAfter)).thenReturn(completedTransaction); - when(bankAccountService.addTransaction(completedTransaction, emitterBankAccount)).thenReturn(updatedSenderBankAccount); + when(bankAccountService.addTransaction(completedTransaction, emitterBankAccount)).thenReturn(updatedEmitterBankAccount); Transaction returnedTransaction = transactionProcessingService.credit(transaction); @@ -134,9 +135,9 @@ public void credit_shouldAddMoneyToTheReceiverAccount_whenAccountsAreFound_fromC Transaction transaction = TransactionTestUtils.createTransactionUtils(emitterBankAccountId, receiverAccountId, new BigDecimal("1000"), UNPROCESSED, instantDate, new HashMap<>()); Transaction outstandingTransaction = TransactionTestUtils.createTransactionUtils(emitterBankAccountId, receiverAccountId, new BigDecimal("1000"), OUTSTANDING, instantDate, new HashMap<>()); - Map metadataAfter = Map.of("sender_amount_before", "1000", + Map metadataAfter = Map.of("emitter_amount_before", "1000", "receiver_amount_before", "0", - "sender_amount_after", "0", + "emitter_amount_after", "0", "receiver_amount_after", "1000"); Transaction completedTransaction = TransactionTestUtils.createTransactionUtils(emitterBankAccountId, receiverAccountId, new BigDecimal("1000"), COMPLETED, instantDate, metadataAfter); BankAccount updatedEmitterBankAccount = BankAccountTestUtils.createBankAccountUtils(emitterBankAccountId, amountOfMoney, completedTransaction); @@ -211,14 +212,13 @@ public void credit_shouldReturnErroredTransaction_whenAddTransactionThrowsBankAc Transaction transaction = TransactionTestUtils.createTransactionUtils(emitterBankAccountId, receiverBankAccountId, new BigDecimal("1000"), UNPROCESSED, instantDate, new HashMap<>()); Transaction outstandingTransaction = TransactionTestUtils.createTransactionUtils(emitterBankAccountId, receiverBankAccountId, new BigDecimal("1000"), OUTSTANDING, instantDate, new HashMap<>()); - Map metadataDuringProcess = Map.of("sender_amount_before", "0", + Map metadataDuringProcess = Map.of("emitter_amount_before", "0", "receiver_amount_before", "0", - "sender_amount_after", "-1000", + "emitter_amount_after", "-1000", "receiver_amount_after", "1000"); Transaction completedTransaction = TransactionTestUtils.createTransactionUtils(emitterBankAccountId, receiverBankAccountId, new BigDecimal("1000"), COMPLETED, instantDate, new HashMap<>()); BankAccount updatedEmitterBankAccount = BankAccountTestUtils.createBankAccountUtils(emitterBankAccountId, amountOfMoney, completedTransaction); Map metadataAfterError = Map.of("error", "Amount of credit should not be negative"); - Transaction refusedTransaction = TransactionTestUtils.createTransactionUtils(emitterBankAccountId, receiverBankAccountId, new BigDecimal("1000"), ERROR, instantDate, metadataAfterError); when(bankAccountService.findBankAccount(emitterBankAccountId)).thenReturn(emitterBankAccount); when(bankAccountService.findBankAccount(receiverBankAccountId)).thenReturn(receiverBankAccount); diff --git a/application/src/test/java/com/cdx/bas/application/scheduler/SchedulerTest.java b/application/src/test/java/com/cdx/bas/application/scheduler/SchedulerTest.java index b3c11aa1..0a06991c 100644 --- a/application/src/test/java/com/cdx/bas/application/scheduler/SchedulerTest.java +++ b/application/src/test/java/com/cdx/bas/application/scheduler/SchedulerTest.java @@ -4,7 +4,6 @@ import com.cdx.bas.domain.bank.transaction.Transaction; import com.cdx.bas.domain.bank.transaction.TransactionPersistencePort; import com.cdx.bas.domain.bank.transaction.TransactionServicePort; -import com.cdx.bas.domain.transaction.*; import io.quarkus.test.InjectMock; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.junit.TestProfile; @@ -62,7 +61,7 @@ public void processQueue_shouldRunSchedulerProcess_withOrderedQueues_whenQueueIs assertThat(queue.peek()).usingRecursiveComparison().isEqualTo (TransactionTestUtils.createTransactionUtils(5L, 59L, Instant.MIN, "Fifth transaction")); verify(transactionService).process(queue.poll()); clock = Clock.fixed(Instant.parse("2022-12-06T10:14:00Z"), ZoneId.of("UTC")); - assertThat(queue.peek()).usingRecursiveComparison().isEqualTo (TransactionTestUtils.createTransactionUtils(3L, 150, Instant.now(clock), "Third transaction")); + assertThat(queue.peek()).usingRecursiveComparison().isEqualTo (TransactionTestUtils.createTransactionUtils(3L, 150L, Instant.now(clock), "Third transaction")); verify(transactionService).process(queue.poll()); clock = Clock.fixed(Instant.parse("2022-12-07T10:14:00Z"), ZoneId.of("UTC")); assertThat(queue.peek()).usingRecursiveComparison().isEqualTo (TransactionTestUtils.createTransactionUtils(2L, 399L, Instant.now(clock), "Second transaction")); @@ -83,7 +82,7 @@ static Queue createCreditTransactionsUtils() { clock = Clock.fixed(Instant.parse("2022-12-07T10:14:00Z"), ZoneId.of("UTC")); queue.add (TransactionTestUtils.createTransactionUtils(2L, 399L, Instant.now(clock), "Second transaction")); clock = Clock.fixed(Instant.parse("2022-12-06T10:14:00Z"), ZoneId.of("UTC")); - queue.add (TransactionTestUtils.createTransactionUtils(3L, 150, Instant.now(clock), "Third transaction")); + queue.add (TransactionTestUtils.createTransactionUtils(3L, 150L, Instant.now(clock), "Third transaction")); clock = Clock.fixed(Instant.parse("2022-12-07T10:18:00Z"), ZoneId.of("UTC")); queue.add (TransactionTestUtils.createTransactionUtils(4L, 1000L, Instant.now(clock), "Fourth transaction")); queue.add (TransactionTestUtils.createTransactionUtils(5L, 59L, Instant.MIN, "Fifth transaction")); diff --git a/client/.attach_pid69276 b/client/.attach_pid69276 new file mode 100644 index 00000000..e69de29b diff --git a/client/pom.xml b/client/pom.xml index f9fbc949..a04c991a 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -84,8 +84,9 @@ test - io.quarkus - quarkus-smallrye-openapi + org.projectlombok + lombok + provided diff --git a/client/src/main/java/com/cdx/bas/client/bank/customer/CustomerResource.java b/client/src/main/java/com/cdx/bas/client/bank/customer/CustomerResource.java index 634bbd94..27858660 100644 --- a/client/src/main/java/com/cdx/bas/client/bank/customer/CustomerResource.java +++ b/client/src/main/java/com/cdx/bas/client/bank/customer/CustomerResource.java @@ -25,7 +25,7 @@ public class CustomerResource { CustomerPersistencePort customerPersistencePort; @GET - @Transactional(value = REQUIRED) + @Transactional @Produces(MediaType.APPLICATION_JSON) public Set getAll() { //TODO use service @@ -34,7 +34,7 @@ public Set getAll() { @GET @Path("/{id}") - @Transactional(value = REQUIRED) + @Transactional @Produces(MediaType.APPLICATION_JSON) public Optional getCustomer(@PathParam("id") long id) { //TODO use service diff --git a/client/src/main/java/com/cdx/bas/client/bank/transaction/TransactionResource.java b/client/src/main/java/com/cdx/bas/client/bank/transaction/TransactionResource.java index 0421dfe4..c789cea6 100644 --- a/client/src/main/java/com/cdx/bas/client/bank/transaction/TransactionResource.java +++ b/client/src/main/java/com/cdx/bas/client/bank/transaction/TransactionResource.java @@ -1,11 +1,9 @@ package com.cdx.bas.client.bank.transaction; -import com.cdx.bas.domain.bank.transaction.Transaction; -import com.cdx.bas.domain.bank.transaction.TransactionControllerPort; -import com.cdx.bas.domain.bank.transaction.TransactionException; -import com.cdx.bas.domain.bank.transaction.TransactionServicePort; +import com.cdx.bas.domain.bank.transaction.*; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import jakarta.transaction.Transactional; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -15,7 +13,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.transaction.Transactional; import java.util.Collections; import java.util.Set; @@ -29,16 +26,18 @@ public class TransactionResource implements TransactionControllerPort { TransactionServicePort transactionServicePort; @GET - @Override @Produces(MediaType.APPLICATION_JSON) + @Transactional + @Override public Set getAll() { return transactionServicePort.getAll(); } @GET @Path("/{status}") - @Override @Produces(MediaType.APPLICATION_JSON) + @Transactional + @Override public Set getAllByStatus(@PathParam("status") String status) { try { return transactionServicePort.findAllByStatus(status); @@ -48,6 +47,15 @@ public Set getAllByStatus(@PathParam("status") String status) { } } + @GET() + @Path("/{id}") + @Produces(MediaType.APPLICATION_JSON) + @Transactional + @Override + public Transaction findById(@PathParam("id") long id) { + return transactionServicePort.findTransaction(id); + } + @POST @Path("/{id}") @Consumes(MediaType.APPLICATION_JSON) @@ -58,9 +66,9 @@ public Set getAllByStatus(@PathParam("status") String status) { @APIResponse(responseCode = "400", description = "Transaction invalid check error details"), @APIResponse(responseCode = "500", description = "Unexpected error happened") }) + @Transactional @Override - @Transactional(Transactional.TxType.REQUIRES_NEW) - public Response deposit(@PathParam("id") Long id, Transaction depositTransaction) { + public Response deposit(@PathParam("id") Long id, NewTransaction depositTransaction) { try { transactionServicePort.createTransaction(depositTransaction); return Response.status(Response.Status.ACCEPTED).entity("Deposit transaction accepted").build(); diff --git a/client/src/test/java/com/cdx/bas/client/bank/account/BankAccountResourceTest.java b/client/src/test/java/com/cdx/bas/client/bank/account/BankAccountResourceTest.java index 1e2ab841..bfde26e1 100644 --- a/client/src/test/java/com/cdx/bas/client/bank/account/BankAccountResourceTest.java +++ b/client/src/test/java/com/cdx/bas/client/bank/account/BankAccountResourceTest.java @@ -1,29 +1,113 @@ package com.cdx.bas.client.bank.account; +import com.cdx.bas.domain.bank.account.BankAccount; +import com.cdx.bas.domain.bank.account.checking.CheckingBankAccount; +import com.cdx.bas.domain.bank.account.mma.MMABankAccount; +import com.cdx.bas.domain.bank.account.saving.SavingBankAccount; +import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; +import com.cdx.bas.domain.money.Money; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.h2.H2DatabaseTestResource; import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static com.cdx.bas.domain.bank.transaction.type.TransactionType.CREDIT; +import static org.assertj.core.api.Assertions.assertThat; @QuarkusTest @QuarkusTestResource(H2DatabaseTestResource.class) class BankAccountResourceTest { + @Inject + BankAccountResource bankAccountResource; + @Test - public void deposit_shouldCreateDepositTransaction_whenBankAccountFound_andDepositTransactionIsValid() { + public void getAll_shouldReturnAllBankAccount() { + Set expectedCustomers = Set.of( + new CheckingBankAccount(1L, Money.of(new BigDecimal("400.00")), List.of(1L), new HashSet<>()), + new CheckingBankAccount(2L, Money.of(new BigDecimal("1600.00")), List.of(2L, 3L), new HashSet<>()), + new SavingBankAccount(3L, Money.of(new BigDecimal("19200.00")), List.of(4L), new HashSet<>()), + new CheckingBankAccount(4L, Money.of(new BigDecimal("500.00")), List.of(3L), new HashSet<>()), + new MMABankAccount(5L, Money.of(new BigDecimal("65000.00")), List.of(1L), new HashSet<>()), + new SavingBankAccount(6L, Money.of(new BigDecimal("999.00")), List.of(5L), new HashSet<>()), + new CheckingBankAccount(7L, Money.of(new BigDecimal("0.00")), List.of(6L), new HashSet<>()), + new SavingBankAccount(8L, Money.of(new BigDecimal("200000.00")), List.of(6L), new HashSet<>()) + ); + Set actualTransactions = bankAccountResource.getAll(); + assertThat(actualTransactions) + .usingRecursiveComparison() + .ignoringCollectionOrder() + .ignoringFields("issuedTransactions") + .isEqualTo(expectedCustomers); } @Test - public void deposit_shouldReturnHTTPError_whenBankAccountFound_butDepositTransactionIsInvalid() { + public void findById_shouldReturnBankAccount_whenBankAccountFound() { + BankAccount expectedBankAccount = new SavingBankAccount(); + expectedBankAccount.setId(6L); + expectedBankAccount.setBalance(Money.of(new BigDecimal("999.00"))); + expectedBankAccount.setCustomersId(Collections.singletonList(5L)); + BankAccount actualBankAccount = bankAccountResource.findById(6); + assertThat(actualBankAccount) + .usingRecursiveComparison() + .ignoringCollectionOrder() + .ignoringFields("issuedTransactions") + .isEqualTo(expectedBankAccount); } @Test - public void deposit_shouldReturnHTTPError_whenBankAccountFound_butDepositAmountReach() { + public void findById_shouldReturnBankAccountWithTransactions_whenTransactionsFoundInBankAccount() { + Transaction transaction1 = Transaction.builder() + .id(3L) + .emitterAccountId(6L) + .receiverAccountId(3L) + .amount(new BigDecimal("9200.00")) + .currency("EUR") + .type(CREDIT) + .status(TransactionStatus.COMPLETED) + .date(Instant.parse("2024-07-10T14:00:00Z")) + .label("transaction 3") + .build(); - } + Transaction transaction2 = Transaction.builder() + .id(2L) + .emitterAccountId(6L) + .receiverAccountId(3L) + .amount(new BigDecimal("9200.00")) + .currency("EUR") + .type(CREDIT) + .status(TransactionStatus.ERROR) + .date(Instant.parse("2024-07-10T14:00:00Z")) + .label("transaction 2") + .build(); + Set issuedTransaction = new HashSet<>(); + issuedTransaction.add(transaction1); + issuedTransaction.add(transaction2); + + BankAccount actualBankAccount = bankAccountResource.findById(6); + assertThat(actualBankAccount.getIssuedTransactions()) + .usingRecursiveComparison() + .ignoringCollectionOrder() + .ignoringFields("metadata") + .isEqualTo(issuedTransaction); + + } + + @Test + public void findById_shouldReturnEmptyTransaction_whenTransactionNotFound() { + BankAccount actualTransaction = bankAccountResource.findById(99L); + assertThat(actualTransaction).isNull(); + } } \ No newline at end of file diff --git a/client/src/test/java/com/cdx/bas/client/bank/customer/CustomerResourceTest.java b/client/src/test/java/com/cdx/bas/client/bank/customer/CustomerResourceTest.java index cae339ca..8e57cb32 100644 --- a/client/src/test/java/com/cdx/bas/client/bank/customer/CustomerResourceTest.java +++ b/client/src/test/java/com/cdx/bas/client/bank/customer/CustomerResourceTest.java @@ -15,6 +15,7 @@ import static com.cdx.bas.domain.bank.customer.gender.Gender.FEMALE; import static com.cdx.bas.domain.bank.customer.gender.Gender.MALE; +import static com.cdx.bas.domain.bank.customer.maritalstatus.MaritalStatus.*; import static org.assertj.core.api.Assertions.assertThat; @QuarkusTest diff --git a/client/src/test/java/com/cdx/bas/client/bank/transaction/TransactionResourceTest.java b/client/src/test/java/com/cdx/bas/client/bank/transaction/TransactionResourceTest.java index 7be89287..276c7e80 100644 --- a/client/src/test/java/com/cdx/bas/client/bank/transaction/TransactionResourceTest.java +++ b/client/src/test/java/com/cdx/bas/client/bank/transaction/TransactionResourceTest.java @@ -1,25 +1,28 @@ package com.cdx.bas.client.bank.transaction; +import com.cdx.bas.domain.bank.transaction.NewTransaction; import com.cdx.bas.domain.bank.transaction.Transaction; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.h2.H2DatabaseTestResource; import io.quarkus.test.junit.QuarkusTest; import jakarta.inject.Inject; import jakarta.ws.rs.core.Response; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import java.math.BigDecimal; import java.time.Instant; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; +import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.*; import static com.cdx.bas.domain.bank.transaction.type.TransactionType.CREDIT; import static com.cdx.bas.domain.bank.transaction.type.TransactionType.DEBIT; import static org.assertj.core.api.Assertions.assertThat; @QuarkusTest +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) @QuarkusTestResource(H2DatabaseTestResource.class) class TransactionResourceTest { @@ -27,11 +30,12 @@ class TransactionResourceTest { TransactionResource transactionResource; @Test + @Order(1) public void getAll_shouldReturnAllTransactions() { Set expectedCustomers = Set.of( - new Transaction(1L, 1L, 2L, new BigDecimal("1600.00"), "EUR", CREDIT, COMPLETED, Instant.parse("2024-06-06T11:00:00+00:00"), "transaction 1", Map.of("sender_amount_before", "2000", "receiver_amount_before", "0", "sender_amount_after", "400", "receiver_amount_after", "1600")), + new Transaction(1L, 1L, 2L, new BigDecimal("1600.00"), "EUR", CREDIT, COMPLETED, Instant.parse("2024-06-06T11:00:00+00:00"), "transaction 1", Map.of("emitter_amount_before", "2000", "receiver_amount_before", "0", "emitter_amount_after", "400", "receiver_amount_after", "1600")), new Transaction(2L, 6L, 3L, new BigDecimal("9200.00"), "EUR", CREDIT, ERROR, Instant.parse("2024-07-10T14:00:00+00:00"), "transaction 2", Map.of("error", "Transaction 2 deposit error for amount 9200 ...")), - new Transaction(3L, 6L, 3L, new BigDecimal("9200.00"), "EUR", CREDIT, COMPLETED, Instant.parse("2024-07-10T14:00:00+00:00"), "transaction 3", Map.of("sender_amount_before", "9200", "receiver_amount_before", "10000", "sender_amount_after", "0", "receiver_amount_after", "19200")), + new Transaction(3L, 6L, 3L, new BigDecimal("9200.00"), "EUR", CREDIT, COMPLETED, Instant.parse("2024-07-10T14:00:00+00:00"), "transaction 3", Map.of("emitter_amount_before", "9200", "receiver_amount_before", "10000", "emitter_amount_after", "0", "receiver_amount_after", "19200")), new Transaction(4L, 5L, 1L, new BigDecimal("100000.00"), "EUR", CREDIT, REFUSED, Instant.parse("2024-07-10T14:00:00+00:00"), "transaction 4", Map.of("error", "Transaction 4 deposit error for amount 100000 ...")), new Transaction(5L, 2L, 1L, new BigDecimal("600.99"), "EUR", CREDIT, UNPROCESSED, Instant.parse("2024-11-06T17:00:00+00:00"), "transaction 5", new HashMap<>()), new Transaction(6L, 1L, 7L, new BigDecimal("2000.00"), "EUR", DEBIT, UNPROCESSED, Instant.parse("2024-11-06T17:30:00+00:00"), "transaction 6", new HashMap<>()), @@ -48,6 +52,7 @@ public void getAll_shouldReturnAllTransactions() { } @Test + @Order(2) public void getAllByStatus_shouldReturnEmptySet_whenStatusIsInvalid() { Set expectedCustomers = Collections.emptySet(); @@ -59,10 +64,11 @@ public void getAllByStatus_shouldReturnEmptySet_whenStatusIsInvalid() { } @Test + @Order(3) public void getAllByStatus_shouldReturnTransactionWithCompletedStatus() { Set expectedCustomers = Set.of( - new Transaction(1L, 1L, 2L, new BigDecimal("1600.00"), "EUR", CREDIT, COMPLETED, Instant.parse("2024-06-06T11:00:00+00:00"), "transaction 1", Map.of("sender_amount_before", "2000", "receiver_amount_before", "0", "sender_amount_after", "400", "receiver_amount_after", "1600")), - new Transaction(3L, 6L, 3L, new BigDecimal("9200.00"), "EUR", CREDIT, COMPLETED, Instant.parse("2024-07-10T14:00:00+00:00"), "transaction 3", Map.of("sender_amount_before", "9200", "receiver_amount_before", "10000", "sender_amount_after", "0", "receiver_amount_after", "19200")) + new Transaction(1L, 1L, 2L, new BigDecimal("1600.00"), "EUR", CREDIT, COMPLETED, Instant.parse("2024-06-06T11:00:00+00:00"), "transaction 1", Map.of("emitter_amount_before", "2000", "receiver_amount_before", "0", "emitter_amount_after", "400", "receiver_amount_after", "1600")), + new Transaction(3L, 6L, 3L, new BigDecimal("9200.00"), "EUR", CREDIT, COMPLETED, Instant.parse("2024-07-10T14:00:00+00:00"), "transaction 3", Map.of("emitter_amount_before", "9200", "receiver_amount_before", "10000", "emitter_amount_after", "0", "receiver_amount_after", "19200")) ); Set actualTransactions = transactionResource.getAllByStatus("completed"); @@ -73,6 +79,7 @@ public void getAllByStatus_shouldReturnTransactionWithCompletedStatus() { } @Test + @Order(4) public void getAllByStatus_shouldReturnTransactionWithUnprocessedStatus() { Set expectedCustomers = Set.of( new Transaction(5L, 2L, 1L, new BigDecimal("600.99"), "EUR", CREDIT, UNPROCESSED, Instant.parse("2024-11-06T17:00:00+00:00"), "transaction 5", new HashMap<>()), @@ -90,6 +97,7 @@ public void getAllByStatus_shouldReturnTransactionWithUnprocessedStatus() { } @Test + @Order(5) public void getAllByStatus_shouldReturnTransactionWithErrorStatus() { Set expectedCustomers = Set.of( new Transaction(2L, 6L, 3L, new BigDecimal("9200.00"), "EUR", CREDIT, ERROR, Instant.parse("2024-07-10T14:00:00+00:00"), "transaction 2", Map.of("error", "Transaction 2 deposit error for amount 9200 ...")) @@ -103,6 +111,7 @@ public void getAllByStatus_shouldReturnTransactionWithErrorStatus() { } @Test + @Order(6) public void getAllByStatus_shouldReturnTransactionWithRefusedStatus() { Set expectedCustomers = Set.of( new Transaction(4L, 5L, 1L, new BigDecimal("100000.00"), "EUR", CREDIT, REFUSED, Instant.parse("2024-07-10T14:00:00+00:00"), "transaction 4", Map.of("error", "Transaction 4 deposit error for amount 100000 ...")) @@ -115,27 +124,52 @@ public void getAllByStatus_shouldReturnTransactionWithRefusedStatus() { .isEqualTo(expectedCustomers); } + @Order(7) @Test - public void deposit_shouldReturnAccepted202Response_whenTransactionIsValidated() { - Transaction validTransaction = new Transaction(null, 1L, 2L, new BigDecimal("1000.00"), "EUR", CREDIT, UNPROCESSED, Instant.parse("2024-06-06T11:00:00+00:00"), "transaction 1", new HashMap<>()); + public void findById_shouldReturnTransaction_whenTransactionFound() { + Transaction expectedTransaction = new Transaction(1L, 1L, 2L, new BigDecimal("1600.00"), "EUR", CREDIT, COMPLETED, Instant.parse("2024-06-06T11:00:00+00:00"), "transaction 1", Map.of("emitter_amount_before", "2000", "receiver_amount_before", "0", "emitter_amount_after", "400", "receiver_amount_after", "1600")); + + Transaction actualTransaction = transactionResource.findById(1L); + assertThat(actualTransaction).isEqualTo(expectedTransaction); - Response actualResponse = transactionResource.deposit(1L, validTransaction); - assertThat(actualResponse).isEqualTo(""); } + @Order(8) @Test - public void deposit_shouldReturnError400Response_whenTransactionIsInvalid() { - Transaction validTransaction = new Transaction(null, 1L, 2L, new BigDecimal("1000.00"), "EUR", CREDIT, UNPROCESSED, Instant.parse("2024-06-06T11:00:00+00:00"), "transaction 1", new HashMap<>()); - - Response actualResponse = transactionResource.deposit(1L, validTransaction); - assertThat(actualResponse).isEqualTo(""); + public void findById_shouldReturnEmptyTransaction_whenTransactionNotFound() { + Transaction actualTransaction = transactionResource.findById(99L); + assertThat(actualTransaction).isNull(); } @Test - public void deposit_shouldReturnError500Response_whenInternalErrorHappened() { - Transaction validTransaction = new Transaction(null, 1L, 2L, new BigDecimal("1000.00"), "EUR", CREDIT, UNPROCESSED, Instant.parse("2024-06-06T11:00:00+00:00"), "transaction 1", new HashMap<>()); + @Order(9) + public void deposit_shouldReturnAccepted202Response_whenTransactionIsValidated() { + NewTransaction validNewTransaction = new NewTransaction(1L, 2L, new BigDecimal("1000.00"), "EUR", CREDIT, UNPROCESSED, Instant.parse("2024-06-06T11:00:00+00:00"), "transaction 1", new HashMap<>()); + Transaction expectedCreatedTransaction = new Transaction(10L, 1L, 2L, new BigDecimal("1000.00"), "EUR", CREDIT, UNPROCESSED, Instant.parse("2024-06-06T11:00:00+00:00"), "transaction 1", new HashMap<>()); - Response actualResponse = transactionResource.deposit(1L, validTransaction); - assertThat(actualResponse).isEqualTo(""); + Response actualResponse = transactionResource.deposit(1L, validNewTransaction); + Transaction actualTransaction = transactionResource.findById(10L); + assertThat(actualResponse.getEntity()).isEqualTo("Deposit transaction accepted"); + assertThat(actualTransaction).isEqualTo(expectedCreatedTransaction); + } + + @Test + @Order(10) + public void deposit_shouldReturnError400Response_whenTransactionIsInvalid() { + NewTransaction invalidNewTransaction = new NewTransaction(); + List expectedLines = Arrays.asList( + "Status must not be null.", + "Label must not be null.", + "Type must not be null.", + "receiver account id must not be null.", + "Date must not be null.", + "currency should be in the exchange rate map.", + "Currency must not be null.", + "Amount must not be null.", + "Unexpected transaction status."); + + Response actualResponse = transactionResource.deposit(1L, invalidNewTransaction); + List actualLines = Arrays.asList(actualResponse.getEntity().toString().split("\n")); + assertThat(actualLines).containsExactlyInAnyOrderElementsOf(expectedLines); } } \ No newline at end of file diff --git a/domain/pom.xml b/domain/pom.xml index 7fc2edd9..d90a7bb7 100644 --- a/domain/pom.xml +++ b/domain/pom.xml @@ -50,7 +50,6 @@ org.projectlombok lombok - 1.18.30 provided diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/account/BankAccount.java b/domain/src/main/java/com/cdx/bas/domain/bank/account/BankAccount.java index dd72aeff..f9084116 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/account/BankAccount.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/account/BankAccount.java @@ -7,9 +7,12 @@ import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import lombok.*; import java.util.*; - +@Data +@AllArgsConstructor +@NoArgsConstructor public abstract class BankAccount { @NotNull(message="id must not be null.") @@ -34,72 +37,7 @@ public BankAccount(AccountType type) { this.type = type; } - public BankAccount(Long id, AccountType type, Money balance, List customersId, Set issuedTransactions, Set receivedTransactions) { - this.id = id; - this.type = type; - this.customersId = customersId; - this.issuedTransactions = issuedTransactions; - this.balance = balance; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public AccountType getType() { - return type; - } - - public void setType(AccountType type) { - this.type = type; - } - - public Money getBalance() { - return balance; - } - - public void setBalance(Money balance) { - this.balance = balance; - } - - public List getCustomersId() { - return customersId; - } - - public void setCustomersId(List customersId) { - this.customersId = customersId; - } - - public Set getIssuedTransactions() { - return issuedTransactions; - } - - public void setIssuedTransactions(Set issuedTransactions) { - this.issuedTransactions = issuedTransactions; - } - public void addTransaction(Transaction transaction) { issuedTransactions.add(transaction); } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - BankAccount that = (BankAccount) o; - return Objects.equals(id, that.id) - && type == that.type - && Objects.equals(balance, that.balance) - && Objects.equals(customersId, that.customersId) - && Objects.equals(issuedTransactions, that.issuedTransactions); - } - - @Override - public int hashCode() { - return Objects.hash(id, type, balance, customersId, issuedTransactions); - } } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/account/checking/CheckingBankAccount.java b/domain/src/main/java/com/cdx/bas/domain/bank/account/checking/CheckingBankAccount.java index 66b566e4..7e95d7d9 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/account/checking/CheckingBankAccount.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/account/checking/CheckingBankAccount.java @@ -1,38 +1,36 @@ package com.cdx.bas.domain.bank.account.checking; -import jakarta.validation.constraints.NotNull; - -import com.cdx.bas.domain.bank.account.type.AccountType; import com.cdx.bas.domain.bank.account.BankAccount; +import com.cdx.bas.domain.bank.account.type.AccountType; +import com.cdx.bas.domain.bank.transaction.Transaction; import com.cdx.bas.domain.money.Amount; import com.cdx.bas.domain.money.Money; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; +import java.util.Set; + +import static com.cdx.bas.domain.bank.account.type.AccountType.*; /** * Checking account (transaction account/current account) */ public class CheckingBankAccount extends BankAccount { - - @NotNull(message="balance must not be null.") + @Amount(min=-600, max=100000, message="balance amount must be between -600 and 100000.") - protected Money balance; + public Money getBalance() { + return super.balance; + } public CheckingBankAccount() { - super(AccountType.CHECKING); + super(CHECKING); } -// public CheckingBankAccount(Long id, Money balance, List customersId, Set transactions) { -// super(id, AccountType.CHECKING, balance, customersId, transactions); -// this.balance = balance; -// System.out.println(super.balance); -// System.out.println(this.balance); -// } - - public Money getBalance() { - return balance; + public CheckingBankAccount(Long id, Money balance, List customersId, Set issuedTransactions) { + super(id, CHECKING, balance, customersId, issuedTransactions); } - public void setBalance(Money balance) { - super.balance = balance; - this.balance = balance; - } } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/account/mma/MMABankAccount.java b/domain/src/main/java/com/cdx/bas/domain/bank/account/mma/MMABankAccount.java index a21dff4d..009a8b3a 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/account/mma/MMABankAccount.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/account/mma/MMABankAccount.java @@ -1,36 +1,35 @@ package com.cdx.bas.domain.bank.account.mma; -import jakarta.validation.constraints.NotNull; - -import com.cdx.bas.domain.bank.account.type.AccountType; import com.cdx.bas.domain.bank.account.BankAccount; +import com.cdx.bas.domain.bank.account.type.AccountType; +import com.cdx.bas.domain.bank.transaction.Transaction; import com.cdx.bas.domain.money.Amount; import com.cdx.bas.domain.money.Money; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; +import java.util.Set; + +import static com.cdx.bas.domain.bank.account.type.AccountType.*; /** * Money Market Account */ public class MMABankAccount extends BankAccount { - - @NotNull(message="balance must not be null.") + @Amount(min=1000, max=250000, message="balance amount must be between 1000 and 250000.") - protected Money balance; - - public MMABankAccount() { - super(AccountType.MMA); + public Money getBalance() { + return super.balance; } - -// public MMABankAccount(Long id, Money balance, List customersId, Set transactions) { -// super(id, AccountType.MMA, balance, customersId, transactions); -// this.balance = balance; -// } - public Money getBalance() { - return balance; + public MMABankAccount() { + super(MMA); } - public void setBalance(Money balance) { - super.balance = balance; - this.balance = balance; + public MMABankAccount(Long id, Money balance, List customersId, Set issuedTransactions) { + super(id, MMA, balance, customersId, issuedTransactions); } } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/account/saving/SavingBankAccount.java b/domain/src/main/java/com/cdx/bas/domain/bank/account/saving/SavingBankAccount.java index 0d0be9c0..59866e6d 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/account/saving/SavingBankAccount.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/account/saving/SavingBankAccount.java @@ -1,36 +1,39 @@ package com.cdx.bas.domain.bank.account.saving; -import jakarta.validation.constraints.NotNull; - -import com.cdx.bas.domain.bank.account.type.AccountType; import com.cdx.bas.domain.bank.account.BankAccount; +import com.cdx.bas.domain.bank.account.type.AccountType; +import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; +import com.cdx.bas.domain.bank.transaction.type.TransactionType; import com.cdx.bas.domain.money.Amount; import com.cdx.bas.domain.money.Money; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Set; + +import static com.cdx.bas.domain.bank.account.type.AccountType.*; /** * Saving Account (French Livret A) */ public class SavingBankAccount extends BankAccount { - - @NotNull(message="balance must not be null.") + @Amount(min=1, max=22950, message="balance amount must be between 1 and 22950.") - protected Money balance; - - public SavingBankAccount() { - super(AccountType.SAVING); + public Money getBalance() { + return super.balance; } - -// public SavingBankAccount(Long id, Money balance, List customersId, Set transactions) { -// super(id, AccountType.SAVING, balance, customersId, transactions); -// this.balance = balance; -// } - public Money getBalance() { - return balance; + public SavingBankAccount() { + super(SAVING); } - public void setBalance(Money balance) { - super.balance = balance; - this.balance = balance; + public SavingBankAccount(Long id, Money balance, List customersId, Set issuedTransactions) { + super(id, SAVING, balance, customersId, issuedTransactions); } } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/NewTransaction.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/NewTransaction.java new file mode 100644 index 00000000..679296fb --- /dev/null +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/NewTransaction.java @@ -0,0 +1,38 @@ +package com.cdx.bas.domain.bank.transaction; + +import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; +import com.cdx.bas.domain.bank.transaction.type.TransactionType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class NewTransaction { + + private Long emitterAccountId; + + private Long receiverAccountId; + + private BigDecimal amount; + + private String currency; + + private TransactionType type; + + private TransactionStatus status; + + private Instant date; + + private String label; + + private Map metadata = new HashMap<>(); +} diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/Transaction.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/Transaction.java index 5d854050..e113fbd8 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/Transaction.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/Transaction.java @@ -1,11 +1,11 @@ package com.cdx.bas.domain.bank.transaction; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; import com.cdx.bas.domain.bank.transaction.type.TransactionType; import com.cdx.bas.domain.bank.transaction.validation.*; import com.cdx.bas.domain.currency.validation.ValidCurrency; -import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; -import com.cdx.bas.domain.transaction.validation.*; import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Null; import lombok.AllArgsConstructor; @@ -26,20 +26,20 @@ @AllArgsConstructor public class Transaction implements Comparable { - @Min(value = 1, message = "Id must be positive and greater than 0 for existing transaction.", groups = ExistingTransaction.class) - @NotNull(message = "Id must not be null for existing transaction.", groups = ExistingTransaction.class) - @Null(message = "Id must be null for new transaction.", groups = NewTransaction.class) + @Min(value = 1, message = "Id must be positive and greater than 0 for existing transaction.", groups = ExistingTransactionGroup.class) + @NotNull(message = "Id must not be null for existing transaction.", groups = ExistingTransactionGroup.class) + @Null(message = "Id must be null for new transaction.", groups = NewTransactionGroup.class) private Long id; - @Null(message = "Sender account must be null for cash movement.", groups = CashMovement.class) - @NotNull(message = "Sender account id must not be null.", groups = AccountMovement.class) - private Long senderAccountId; + @Null(message = "Emitter account must be null for cash movement.", groups = CashMovementGroup.class) + @NotNull(message = "Emitter account id must not be null.", groups = AccountMovementGroup.class) + private Long emitterAccountId; @NotNull(message = "receiver account id must not be null.") private Long receiverAccountId; - @Min(value = 10, message = "Amount must be greater than 10 for cash movement.", groups = CashMovement.class) - @Min(value = 1, message = "Amount must be positive and greater than 0.", groups = AccountMovement.class) + @Min(value = 10, message = "Amount must be greater than 10 for cash movement.", groups = CashMovementGroup.class) + @Min(value = 1, message = "Amount must be positive and greater than 0.", groups = AccountMovementGroup.class) @NotNull(message = "Amount must not be null.") private BigDecimal amount; @@ -50,7 +50,7 @@ public class Transaction implements Comparable { @NotNull(message = "Type must not be null.") private TransactionType type; - @ValidStatus(expectedStatus = UNPROCESSED, groups = NewTransaction.class) + @ValidStatus(expectedStatus = UNPROCESSED, groups = NewTransactionGroup.class) @NotNull(message = "Status must not be null.") private TransactionStatus status; @@ -60,7 +60,8 @@ public class Transaction implements Comparable { @NotNull(message = "Label must not be null.") private String label; - @NotNull(message = "Bill must be define for cash movements.", groups = CashMovement.class) + @NotNull(message = "Metadata must not be null for cash movements.", groups = CashMovementGroup.class) + @NotEmpty(message = "Bill must be define for cash movements.", groups = CashMovementGroup.class) private Map metadata = new HashMap<>(); @Override diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionControllerPort.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionControllerPort.java index 62a41300..f22ec95d 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionControllerPort.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionControllerPort.java @@ -1,5 +1,6 @@ package com.cdx.bas.domain.bank.transaction; +import com.cdx.bas.domain.bank.account.BankAccount; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.core.Response; @@ -22,13 +23,21 @@ public interface TransactionControllerPort { */ public Set getAllByStatus(@PathParam("status") String status) ; + /** + * Find Transaction from its id + * + * @param id of Transaction + * @return Transaction corresponding to the id + */ + public Transaction findById(long id); + /** * Make a deposit on bank account * * @param id of BankAccount - * @param depositTransaction to add to the BankAccount + * @param depositNewTransaction to add to the BankAccount * @return Response with status corresponding to transaction validation or not */ - public Response deposit(Long id, Transaction depositTransaction); + public Response deposit(Long id, NewTransaction depositNewTransaction); } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionServicePort.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionServicePort.java index afae2d83..44b1324f 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionServicePort.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionServicePort.java @@ -1,5 +1,7 @@ package com.cdx.bas.domain.bank.transaction; +import com.cdx.bas.domain.bank.account.BankAccount; + import java.util.Set; public interface TransactionServicePort { @@ -22,9 +24,9 @@ public interface TransactionServicePort { /** * add current transaction * - * @param transaction to add + * @param newTransaction to add */ - void createTransaction(Transaction transaction); + void createTransaction(NewTransaction newTransaction); /** * merge two transactions @@ -34,11 +36,18 @@ public interface TransactionServicePort { */ Transaction mergeTransactions(Transaction oldTransaction, Transaction newTransaction); + /** + * find Transaction from id + * + * @param transactionId + * @return Transaction found + */ + Transaction findTransaction(Long transactionId); + /** * Process the transaction depending on its type * * @param transaction to process */ void process(Transaction transaction); - } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/type/TransactionType.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/type/TransactionType.java index bcc57a96..61236bd8 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/type/TransactionType.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/type/TransactionType.java @@ -1,5 +1,5 @@ package com.cdx.bas.domain.bank.transaction.type; public enum TransactionType { - CREDIT, DEBIT, DEPOSIT + CREDIT, DEBIT, DEPOSIT, WITHDRAW } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/AccountMovement.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/AccountMovementGroup.java similarity index 76% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/AccountMovement.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/AccountMovementGroup.java index 9f6ca0a0..d63f5584 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/AccountMovement.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/AccountMovementGroup.java @@ -3,5 +3,5 @@ /** * Transaction group that move money from an account to another */ -public interface AccountMovement { +public interface AccountMovementGroup { } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/CashMovement.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/CashMovementGroup.java similarity index 78% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/CashMovement.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/CashMovementGroup.java index 6a09762c..8fd96eef 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/CashMovement.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/CashMovementGroup.java @@ -3,5 +3,5 @@ /** * Transaction group that use cash money to transfer to an account */ -public interface CashMovement { +public interface CashMovementGroup { } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/ExistingTransaction.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/ExistingTransactionGroup.java similarity index 72% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/ExistingTransaction.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/ExistingTransactionGroup.java index 36b3c2af..1925731a 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/ExistingTransaction.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/ExistingTransactionGroup.java @@ -3,5 +3,5 @@ /** * Transaction group for existing transactions */ -public interface ExistingTransaction { +public interface ExistingTransactionGroup { } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/NewTransaction.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/NewTransactionGroup.java similarity index 73% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/NewTransaction.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/NewTransactionGroup.java index 0c55806c..3f6db896 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/NewTransaction.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/NewTransactionGroup.java @@ -3,5 +3,5 @@ /** * Transaction group for new transactions */ -public interface NewTransaction { +public interface NewTransactionGroup { } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/TransactionValidator.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/TransactionValidator.java index b88c5d8d..1fd45e27 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/TransactionValidator.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/TransactionValidator.java @@ -10,7 +10,7 @@ import java.util.HashSet; import java.util.Set; -import static com.cdx.bas.domain.bank.transaction.type.TransactionType.DEPOSIT; +import static com.cdx.bas.domain.bank.transaction.type.TransactionType.*; @RequestScoped @@ -20,19 +20,20 @@ public class TransactionValidator { Validator validator; public void validateNewTransaction(Transaction transaction) throws TransactionException { - validateTransaction(transaction, NewTransaction.class); + validateTransaction(transaction, NewTransactionGroup.class); } public void validateExistingTransaction(Transaction transaction) throws TransactionException { - validateTransaction(transaction, ExistingTransaction.class); + validateTransaction(transaction, ExistingTransactionGroup.class); } - private void validateTransaction(Transaction transaction, Class group) throws TransactionException { + private void validateTransaction(Transaction transaction, Class stateGroup) throws TransactionException { Set> violations = new HashSet<>(validator.validate(transaction)); - if (DEPOSIT.equals(transaction.getType())) { - violations.addAll(validator.validate(transaction, CashMovement.class, group)); - } else { - violations.addAll(validator.validate(transaction, AccountMovement.class, group)); + violations.addAll(validator.validate(transaction, stateGroup)); + if (DEPOSIT.equals(transaction.getType()) || WITHDRAW.equals(transaction.getType())) { + violations.addAll(validator.validate(transaction, CashMovementGroup.class)); + } else if(CREDIT.equals(transaction.getType()) || DEBIT.equals(transaction.getType())) { + violations.addAll(validator.validate(transaction, AccountMovementGroup.class)); } checkConstraintViolation(violations); } diff --git a/domain/src/main/java/com/cdx/bas/domain/money/Amount.java b/domain/src/main/java/com/cdx/bas/domain/money/Amount.java index ec8068b5..7684b816 100644 --- a/domain/src/main/java/com/cdx/bas/domain/money/Amount.java +++ b/domain/src/main/java/com/cdx/bas/domain/money/Amount.java @@ -1,6 +1,7 @@ package com.cdx.bas.domain.money; import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; @@ -13,7 +14,7 @@ @NotNull @Documented -@Target({ FIELD }) +@Target({ FIELD, METHOD }) @Retention(RUNTIME) @Constraint(validatedBy = AmountValidator.class) public @interface Amount { diff --git a/domain/src/test/java/com/cdx/bas/domain/bank/transaction/TransactionValidatorTest.java b/domain/src/test/java/com/cdx/bas/domain/bank/transaction/TransactionValidatorTest.java index be932bd3..2bdc4ec5 100644 --- a/domain/src/test/java/com/cdx/bas/domain/bank/transaction/TransactionValidatorTest.java +++ b/domain/src/test/java/com/cdx/bas/domain/bank/transaction/TransactionValidatorTest.java @@ -14,6 +14,7 @@ import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.UNPROCESSED; import static com.cdx.bas.domain.bank.transaction.type.TransactionType.*; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; @QuarkusTest class TransactionValidatorTest { @@ -25,7 +26,7 @@ class TransactionValidatorTest { public void validateNewTransaction_shouldDoNothing_whenNewCreditTransactionIsValid(){ Transaction creditTransaction = Transaction.builder() .id(null) - .senderAccountId(1L) + .emitterAccountId(1L) .receiverAccountId(2L) .amount(new BigDecimal("1")) .currency("EUR") @@ -43,10 +44,11 @@ public void validateNewTransaction_shouldTrowTransactionException_whenNewCreditT .build(); try { transactionValidator.validateNewTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class); String[] lines = transactionException.getMessage().split("\\r?\\n"); - assertThat(lines).hasSize(10); + assertThat(lines).hasSize(9); } } @@ -54,7 +56,7 @@ public void validateNewTransaction_shouldTrowTransactionException_whenNewCreditT public void validateNewTransaction_shouldTrowTransactionException_whenNewTransactionHasId(){ Transaction creditTransaction = Transaction.builder() .id(99L) - .senderAccountId(1L) + .emitterAccountId(1L) .receiverAccountId(2L) .amount(new BigDecimal("1")) .currency("EUR") @@ -65,6 +67,7 @@ public void validateNewTransaction_shouldTrowTransactionException_whenNewTransac .build(); try { transactionValidator.validateNewTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class) .hasMessage("Id must be null for new transaction.\n"); @@ -75,7 +78,7 @@ public void validateNewTransaction_shouldTrowTransactionException_whenNewTransac public void validateNewTransaction_shouldTrowTransactionException_whenNewCreditTransactionAmountIsLowerThanMin(){ Transaction creditTransaction = Transaction.builder() .id(null) - .senderAccountId(1L) + .emitterAccountId(1L) .receiverAccountId(2L) .amount(new BigDecimal("0")) .currency("EUR") @@ -86,6 +89,7 @@ public void validateNewTransaction_shouldTrowTransactionException_whenNewCreditT .build(); try { transactionValidator.validateNewTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class) .hasMessage("Amount must be positive and greater than 0.\n"); @@ -96,7 +100,7 @@ public void validateNewTransaction_shouldTrowTransactionException_whenNewCreditT public void validateNewTransaction_shouldDoNothing_whenNewDebitTransactionIsValid(){ Transaction creditTransaction = Transaction.builder() .id(null) - .senderAccountId(1L) + .emitterAccountId(1L) .receiverAccountId(2L) .amount(new BigDecimal("1")) .currency("EUR") @@ -112,7 +116,7 @@ public void validateNewTransaction_shouldDoNothing_whenNewDebitTransactionIsVali public void validateNewTransaction_shouldTrowTransactionException_whenNewDebitTransactionHasId(){ Transaction creditTransaction = Transaction.builder() .id(99L) - .senderAccountId(1L) + .emitterAccountId(1L) .receiverAccountId(2L) .amount(new BigDecimal("1")) .currency("EUR") @@ -123,6 +127,7 @@ public void validateNewTransaction_shouldTrowTransactionException_whenNewDebitTr .build(); try { transactionValidator.validateNewTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class) .hasMessage("Id must be null for new transaction.\n"); @@ -133,7 +138,7 @@ public void validateNewTransaction_shouldTrowTransactionException_whenNewDebitTr public void validateNewTransaction_shouldTrowTransactionException_whenNewDebitTransactionAmountIsLowerThanMin(){ Transaction creditTransaction = Transaction.builder() .id(null) - .senderAccountId(1L) + .emitterAccountId(1L) .receiverAccountId(2L) .amount(new BigDecimal("0")) .currency("EUR") @@ -144,6 +149,7 @@ public void validateNewTransaction_shouldTrowTransactionException_whenNewDebitTr .build(); try { transactionValidator.validateNewTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class) .hasMessage("Amount must be positive and greater than 0.\n"); @@ -154,7 +160,7 @@ public void validateNewTransaction_shouldTrowTransactionException_whenNewDebitTr public void validateExistingTransaction_shouldDoNothing_whenExistingCreditTransactionIsValid(){ Transaction creditTransaction = Transaction.builder() .id(100L) - .senderAccountId(1L) + .emitterAccountId(1L) .receiverAccountId(2L) .amount(new BigDecimal("1")) .currency("EUR") @@ -172,10 +178,11 @@ public void validateNewTransaction_shouldTrowTransactionException_whenExistingTr .build(); try { transactionValidator.validateExistingTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class); String[] lines = transactionException.getMessage().split("\\r?\\n"); - assertThat(lines).hasSize(10); + assertThat(lines).hasSize(9); } } @@ -183,7 +190,7 @@ public void validateNewTransaction_shouldTrowTransactionException_whenExistingTr public void validateNewTransaction_shouldTrowTransactionException_whenExistingCreditTransactionAmountIsLowerThanMin(){ Transaction creditTransaction = Transaction.builder() .id(99L) - .senderAccountId(1L) + .emitterAccountId(1L) .receiverAccountId(2L) .amount(new BigDecimal("0")) .currency("EUR") @@ -194,6 +201,7 @@ public void validateNewTransaction_shouldTrowTransactionException_whenExistingCr .build(); try { transactionValidator.validateExistingTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class) .hasMessage("Amount must be positive and greater than 0.\n"); @@ -204,7 +212,7 @@ public void validateNewTransaction_shouldTrowTransactionException_whenExistingCr public void validateExistingTransaction_shouldDoNothing_whenExistingDebitTransactionIsValid(){ Transaction creditTransaction = Transaction.builder() .id(100L) - .senderAccountId(1L) + .emitterAccountId(1L) .receiverAccountId(2L) .amount(new BigDecimal("1")) .currency("EUR") @@ -220,7 +228,7 @@ public void validateExistingTransaction_shouldDoNothing_whenExistingDebitTransac public void validateNewTransaction_shouldTrowTransactionException_whenExistingDebitTransactionAmountIsLowerThanMin(){ Transaction creditTransaction = Transaction.builder() .id(99L) - .senderAccountId(1L) + .emitterAccountId(1L) .receiverAccountId(2L) .amount(new BigDecimal("0")) .currency("EUR") @@ -231,6 +239,7 @@ public void validateNewTransaction_shouldTrowTransactionException_whenExistingDe .build(); try { transactionValidator.validateExistingTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class) .hasMessage("Amount must be positive and greater than 0.\n"); @@ -241,7 +250,7 @@ public void validateNewTransaction_shouldTrowTransactionException_whenExistingDe public void validateNewTransaction_shouldDoNothing_whenNewDepositTransactionIsValid(){ Transaction creditTransaction = Transaction.builder() .id(null) - .senderAccountId(null) + .emitterAccountId(null) .receiverAccountId(2L) .amount(new BigDecimal("20")) .currency("EUR") @@ -257,8 +266,8 @@ public void validateNewTransaction_shouldDoNothing_whenNewDepositTransactionIsVa @Test public void validateNewTransaction_shouldThrowTransactionException_whenNewDepositTransactionHasSendAccountId(){ Transaction creditTransaction = Transaction.builder() - .id(99L) - .senderAccountId(null) + .id(null) + .emitterAccountId(1L) .receiverAccountId(2L) .amount(new BigDecimal("20")) .currency("EUR") @@ -269,10 +278,11 @@ public void validateNewTransaction_shouldThrowTransactionException_whenNewDeposi .metadata(Map.of("bill", "10,10")) .build(); try { - transactionValidator.validateExistingTransaction(creditTransaction); + transactionValidator.validateNewTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class) - .hasMessage("Sender account must be null for cash movement.\n"); + .hasMessageContaining("Emitter account must be null for cash movement.\n"); } } @@ -280,7 +290,7 @@ public void validateNewTransaction_shouldThrowTransactionException_whenNewDeposi public void validateNewTransaction_shouldThrowTransactionException_whenNewDepositTransactionHasAmountLowerThanMin(){ Transaction creditTransaction = Transaction.builder() .id(null) - .senderAccountId(null) + .emitterAccountId(null) .receiverAccountId(2L) .amount(new BigDecimal("5")) .currency("EUR") @@ -292,6 +302,7 @@ public void validateNewTransaction_shouldThrowTransactionException_whenNewDeposi .build(); try { transactionValidator.validateNewTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class) .hasMessage("Amount must be greater than 10 for cash movement.\n"); @@ -302,7 +313,7 @@ public void validateNewTransaction_shouldThrowTransactionException_whenNewDeposi public void validateNewTransaction_shouldThrowTransactionException_whenNewDepositTransactionHasEmptyMetadata(){ Transaction creditTransaction = Transaction.builder() .id(null) - .senderAccountId(null) + .emitterAccountId(null) .receiverAccountId(2L) .amount(new BigDecimal("10")) .currency("EUR") @@ -314,6 +325,7 @@ public void validateNewTransaction_shouldThrowTransactionException_whenNewDeposi .build(); try { transactionValidator.validateNewTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class) .hasMessage("Bill must be define for cash movements.\n"); @@ -324,7 +336,7 @@ public void validateNewTransaction_shouldThrowTransactionException_whenNewDeposi public void validateNewTransaction_shouldThrowTransactionException_whenNewDepositTransactionHasWrongStatus(){ Transaction creditTransaction = Transaction.builder() .id(null) - .senderAccountId(null) + .emitterAccountId(null) .receiverAccountId(2L) .amount(new BigDecimal("10")) .currency("EUR") @@ -336,6 +348,7 @@ public void validateNewTransaction_shouldThrowTransactionException_whenNewDeposi .build(); try { transactionValidator.validateNewTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class) .hasMessage("Unexpected transaction status.\n"); @@ -346,7 +359,7 @@ public void validateNewTransaction_shouldThrowTransactionException_whenNewDeposi public void validateNewTransaction_shouldThrowTransactionException_whenNewDepositTransactionHasNullMetadata(){ Transaction creditTransaction = Transaction.builder() .id(null) - .senderAccountId(null) + .emitterAccountId(null) .receiverAccountId(2L) .amount(new BigDecimal("10")) .currency("EUR") @@ -358,9 +371,11 @@ public void validateNewTransaction_shouldThrowTransactionException_whenNewDeposi .build(); try { transactionValidator.validateNewTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class) - .hasMessage("Bill must be define for cash movements.\n"); + .hasMessageContaining("Metadata must not be null for cash movements.\n") + .hasMessageContaining("Bill must be define for cash movements.\n"); } } @@ -368,7 +383,7 @@ public void validateNewTransaction_shouldThrowTransactionException_whenNewDeposi public void validateExistingTransaction_shouldDoNothing_whenExistingDepositTransactionIsValid(){ Transaction creditTransaction = Transaction.builder() .id(100L) - .senderAccountId(null) + .emitterAccountId(null) .receiverAccountId(2L) .amount(new BigDecimal("20")) .currency("EUR") @@ -385,7 +400,7 @@ public void validateExistingTransaction_shouldDoNothing_whenExistingDepositTrans public void validateExistingTransaction_shouldThrowTransactionException_whenExistingDepositTransactionHasAmountLowerThanMin(){ Transaction creditTransaction = Transaction.builder() .id(99L) - .senderAccountId(null) + .emitterAccountId(null) .receiverAccountId(2L) .amount(new BigDecimal("5")) .currency("EUR") @@ -397,6 +412,7 @@ public void validateExistingTransaction_shouldThrowTransactionException_whenExis .build(); try { transactionValidator.validateExistingTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class) .hasMessage("Amount must be greater than 10 for cash movement.\n"); @@ -407,7 +423,7 @@ public void validateExistingTransaction_shouldThrowTransactionException_whenExis public void validateExistingTransaction_shouldThrowTransactionException_whenExistingDepositTransactionHasEmptyMetadata(){ Transaction creditTransaction = Transaction.builder() .id(99L) - .senderAccountId(null) + .emitterAccountId(null) .receiverAccountId(2L) .amount(new BigDecimal("10")) .currency("EUR") @@ -419,6 +435,7 @@ public void validateExistingTransaction_shouldThrowTransactionException_whenExis .build(); try { transactionValidator.validateExistingTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class) .hasMessage("Bill must be define for cash movements.\n"); @@ -429,7 +446,7 @@ public void validateExistingTransaction_shouldThrowTransactionException_whenExis public void validateExistingTransaction_shouldThrowTransactionException_whenExistingDepositTransactionHasNullMetadata(){ Transaction creditTransaction = Transaction.builder() .id(99L) - .senderAccountId(null) + .emitterAccountId(null) .receiverAccountId(2L) .amount(new BigDecimal("10")) .currency("EUR") @@ -441,9 +458,11 @@ public void validateExistingTransaction_shouldThrowTransactionException_whenExis .build(); try { transactionValidator.validateExistingTransaction(creditTransaction); + fail(); } catch (TransactionException transactionException) { assertThat(transactionException).isInstanceOf(TransactionException.class) - .hasMessage("Bill must be define for cash movements.\n"); + .hasMessageContaining("Metadata must not be null for cash movements.\n") + .hasMessageContaining("Bill must be define for cash movements.\n"); } } } \ No newline at end of file diff --git a/env/init-pg.sql b/env/init-pg.sql index 23575603..f240bcb9 100644 --- a/env/init-pg.sql +++ b/env/init-pg.sql @@ -44,7 +44,7 @@ SET TIME ZONE 'Europe/Paris'; CREATE TABLE basapp.transactions ( transaction_id BIGSERIAL UNIQUE NOT NULL, - sender_account_id bigint, + emitter_account_id bigint, receiver_account_id bigint NOT NULL, type VARCHAR(25) NOT NULL, amount DECIMAL NOT NULL, @@ -54,7 +54,7 @@ SET TIME ZONE 'Europe/Paris'; label text NOT NULL, metadata jsonb, CONSTRAINT pk_transaction PRIMARY KEY (transaction_id), - CONSTRAINT fk_sender_account_id FOREIGN KEY(sender_account_id) REFERENCES basapp.bank_accounts(account_id), + CONSTRAINT fk_emitter_account_id FOREIGN KEY(emitter_account_id) REFERENCES basapp.bank_accounts(account_id), CONSTRAINT fk_receiver_account_id FOREIGN KEY(receiver_account_id) REFERENCES basapp.bank_accounts(account_id) ); diff --git a/env/insert-pg.sql b/env/insert-pg.sql index f3903cfd..0a2becb0 100644 --- a/env/insert-pg.sql +++ b/env/insert-pg.sql @@ -29,15 +29,16 @@ VALUES (1, 1), (4, 3), (5, 1), (6, 5), - (7, 6); + (7, 6), + (8, 6); -INSERT INTO basapp.transactions (transaction_id, sender_account_id, receiver_account_id, type, amount, currency, status, date, label, metadata) -VALUES (1, 1, 2, 'CREDIT', 1600.00, 'EUR', 'COMPLETED', '2022-06-06T12:00:00+01:00', 'transaction 1', '{"sender_amount_before" : "2000", "receiver_amount_before" : "0", "sender_amount_after" : "400", "receiver_amount_after" : "1600"}'::jsonb), - (2, 6, 3, 'CREDIT', 9200.00, 'EUR', 'ERROR', '2022-07-10T15:00:00+01:00', 'transaction 2', '{"error" : "Transaction 2 deposit error for amount 9200 ..."}'::jsonb), - (3, 6, 3, 'CREDIT', 9200.00, 'EUR', 'COMPLETED', '2022-07-10T15:00:00+01:00', 'transaction 3', '{"sender_amount_before" : "9200", "receiver_amount_before" : "10000", "sender_amount_after" : "0", "receiver_amount_after" : "19200"}'::jsonb), - (4, 5, 1, 'CREDIT', 100000.00, 'EUR', 'REFUSED', '2022-07-10T15:00:00+01:00', 'transaction 4', '{"error" : "Transaction 4 deposit error for amount 100000 ..."}'::jsonb), - (5, 2, 1, 'CREDIT', 600.99, 'EUR', 'UNPROCESSED', '2022-11-06 18:00:00+01:00', 'transaction 3', null::jsonb), - (6, 1, 7, 'DEBIT', 2000.00, 'EUR', 'UNPROCESSED', '2022-11-06 18:30:00+01:00', 'transaction 4', null::jsonb), - (7, 3, 1, 'CREDIT', 1000.00, 'EUR', 'UNPROCESSED', '2022-12-06 18:00:00+01:00', 'transaction 5', null::jsonb), - (8, 4, 2, 'CREDIT', 300.80, 'EUR', 'UNPROCESSED', '2022-12-06 19:00:00+01:00', 'transaction 6', null::jsonb), - (9, 8, 7, 'DEBIT', 5000.00, 'EUR', 'UNPROCESSED', '2022-12-06 19:00:10+01:00', 'transaction 7', null::jsonb); \ No newline at end of file +INSERT INTO basapp.transactions (transaction_id, emitter_account_id, receiver_account_id, type, amount, currency, status, date, label, metadata) +VALUES (1, 1, 2, 'CREDIT', 1600.00, 'EUR', 'COMPLETED', '2024-06-06T12:00:00+01:00', 'transaction 1', '{"emitter_amount_before" : "2000", "receiver_amount_before" : "0", "emitter_amount_after" : "400", "receiver_amount_after" : "1600"}'::jsonb), + (2, 6, 3, 'CREDIT', 9200.00, 'EUR', 'ERROR', '2024-07-10T15:00:00+01:00', 'transaction 2', '{"error" : "Transaction 2 deposit error for amount 9200 ..."}'::jsonb), + (3, 6, 3, 'CREDIT', 9200.00, 'EUR', 'COMPLETED', '2024-07-10T15:00:00+01:00', 'transaction 3', '{"emitter_amount_before" : "9200", "receiver_amount_before" : "10000", "emitter_amount_after" : "0", "receiver_amount_after" : "19200"}'::jsonb), + (4, 5, 1, 'CREDIT', 100000.00, 'EUR', 'REFUSED', '2024-07-10T15:00:00+01:00', 'transaction 4', '{"error" : "Transaction 4 deposit error for amount 100000 ..."}'::jsonb), + (5, 2, 1, 'CREDIT', 600.99, 'EUR', 'UNPROCESSED', '2024-11-06 18:00:00+01:00', 'transaction 3', null::jsonb), + (6, 1, 7, 'DEBIT', 2000.00, 'EUR', 'UNPROCESSED', '2024-11-06 18:30:00+01:00', 'transaction 4', null::jsonb), + (7, 3, 1, 'CREDIT', 1000.00, 'EUR', 'UNPROCESSED', '2024-12-06 18:00:00+01:00', 'transaction 5', null::jsonb), + (8, 4, 2, 'CREDIT', 300.80, 'EUR', 'UNPROCESSED', '2024-12-06 19:00:00+01:00', 'transaction 6', null::jsonb), + (9, 8, 7, 'DEBIT', 5000.00, 'EUR', 'UNPROCESSED', '2024-12-06 19:00:10+01:00', 'transaction 7', null::jsonb); \ No newline at end of file diff --git a/env/test-resources/init-test-h2.sql b/env/test-resources/init-test-h2.sql index 0968b9ba..0d0f857d 100644 --- a/env/test-resources/init-test-h2.sql +++ b/env/test-resources/init-test-h2.sql @@ -43,7 +43,7 @@ SET TIME ZONE 'Europe/Paris'; CREATE TABLE IF NOT EXISTS basapp.transactions ( transaction_id BIGSERIAL UNIQUE NOT NULL, - sender_account_id bigint, + emitter_account_id bigint, receiver_account_id bigint NOT NULL, type VARCHAR(25) NOT NULL, amount DECIMAL NOT NULL, @@ -53,7 +53,7 @@ SET TIME ZONE 'Europe/Paris'; label text NOT NULL, metadata jsonb, CONSTRAINT pk_transaction PRIMARY KEY (transaction_id), - CONSTRAINT fk_sender_account_id FOREIGN KEY(sender_account_id) REFERENCES basapp.bank_accounts(account_id), + CONSTRAINT fk_emitter_account_id FOREIGN KEY(emitter_account_id) REFERENCES basapp.bank_accounts(account_id), CONSTRAINT fk_receiver_account_id FOREIGN KEY(receiver_account_id) REFERENCES basapp.bank_accounts(account_id) ); diff --git a/env/test-resources/insert-test-h2.sql b/env/test-resources/insert-test-h2.sql index cdbd3b51..fa622691 100644 --- a/env/test-resources/insert-test-h2.sql +++ b/env/test-resources/insert-test-h2.sql @@ -30,12 +30,13 @@ VALUES (1, 1), (4, 3), (5, 1), (6, 5), - (7, 6); + (7, 6), + (8, 6); -INSERT INTO basapp.transactions (transaction_id, sender_account_id, receiver_account_id, type, amount, currency, status, date, label, metadata) -VALUES (1, 1, 2, 'CREDIT', 1600.00, 'EUR', 'COMPLETED', TIMESTAMP WITH TIME ZONE '2024-06-06 12:00:00+01:00', 'transaction 1', '{"sender_amount_before" : "2000", "receiver_amount_before" : "0", "sender_amount_after" : "400", "receiver_amount_after" : "1600"}'), +INSERT INTO basapp.transactions (transaction_id, emitter_account_id, receiver_account_id, type, amount, currency, status, date, label, metadata) +VALUES (1, 1, 2, 'CREDIT', 1600.00, 'EUR', 'COMPLETED', TIMESTAMP WITH TIME ZONE '2024-06-06 12:00:00+01:00', 'transaction 1', '{"emitter_amount_before" : "2000", "receiver_amount_before" : "0", "emitter_amount_after" : "400", "receiver_amount_after" : "1600"}'), (2, 6, 3, 'CREDIT', 9200.00, 'EUR', 'ERROR', TIMESTAMP WITH TIME ZONE '2024-07-10 15:00:00+01:00', 'transaction 2', '{"error" : "Transaction 2 deposit error for amount 9200 ..."}'), - (3, 6, 3, 'CREDIT', 9200.00, 'EUR', 'COMPLETED', TIMESTAMP WITH TIME ZONE '2024-07-10 15:00:00+01:00', 'transaction 3', '{"sender_amount_before" : "9200", "receiver_amount_before" : "10000", "sender_amount_after" : "0", "receiver_amount_after" : "19200"}'), + (3, 6, 3, 'CREDIT', 9200.00, 'EUR', 'COMPLETED', TIMESTAMP WITH TIME ZONE '2024-07-10 15:00:00+01:00', 'transaction 3', '{"emitter_amount_before" : "9200", "receiver_amount_before" : "10000", "emitter_amount_after" : "0", "receiver_amount_after" : "19200"}'), (4, 5, 1, 'CREDIT', 100000.00, 'EUR', 'REFUSED', TIMESTAMP WITH TIME ZONE '2024-07-10 15:00:00+01:00', 'transaction 4', '{"error" : "Transaction 4 deposit error for amount 100000 ..."}'), (5, 2, 1, 'CREDIT', 600.99, 'EUR', 'UNPROCESSED', TIMESTAMP WITH TIME ZONE '2024-11-06 18:00:00+01:00', 'transaction 5', null), (6, 1, 7, 'DEBIT', 2000.00, 'EUR', 'UNPROCESSED', TIMESTAMP WITH TIME ZONE '2024-11-06 18:30:00+01:00', 'transaction 6', null),