fixed the tests

This commit is contained in:
Tom Hombergs
2019-08-17 09:07:36 +02:00
parent cc2237590a
commit eab6af3485
10 changed files with 96 additions and 57 deletions

View File

@@ -47,4 +47,14 @@ class AccountMapper {
return new ActivityWindow(mappedActivities); return new ActivityWindow(mappedActivities);
} }
ActivityJpaEntity mapToJpaEntity(Activity activity) {
return new ActivityJpaEntity(
activity.getId() == null ? null : activity.getId().getValue(),
activity.getTimestamp(),
activity.getOwnerAccountId().getValue(),
activity.getSourceAccountId().getValue(),
activity.getTargetAccountId().getValue(),
activity.getMoney().getAmount().longValue());
}
} }

View File

@@ -62,18 +62,9 @@ class AccountPersistenceAdapter implements
public void updateActivities(Account account) { public void updateActivities(Account account) {
for (Activity activity : account.getActivityWindow().getActivities()) { for (Activity activity : account.getActivityWindow().getActivities()) {
if (activity.getId() == null) { if (activity.getId() == null) {
activityRepository.save(mapToJpa(activity)); activityRepository.save(accountMapper.mapToJpaEntity(activity));
} }
} }
} }
private ActivityJpaEntity mapToJpa(Activity activity) {
return new ActivityJpaEntity(
activity.getId() == null ? null : activity.getId().getValue(),
activity.getTimestamp(),
activity.getOwnerAccountId().getValue(),
activity.getSourceAccountId().getValue(),
activity.getTargetAccountId().getValue(),
activity.getMoney().getAmount().longValue());
}
} }

View File

@@ -0,0 +1,4 @@
package io.reflectoring.cashpal.adapter.web;
class AccountResource {
}

View File

@@ -4,12 +4,9 @@ import io.reflectoring.cashpal.application.port.in.SendMoneyUseCase;
import io.reflectoring.cashpal.application.port.in.SendMoneyUseCase.SendMoneyCommand; import io.reflectoring.cashpal.application.port.in.SendMoneyUseCase.SendMoneyCommand;
import io.reflectoring.cashpal.domain.Account.AccountId; import io.reflectoring.cashpal.domain.Account.AccountId;
import io.reflectoring.cashpal.domain.Money; import io.reflectoring.cashpal.domain.Money;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@@ -18,24 +15,18 @@ public class SendMoneyController {
private final SendMoneyUseCase sendMoneyUseCase; private final SendMoneyUseCase sendMoneyUseCase;
@PostMapping(path = "/sendMoney") @PostMapping(path = "/accounts/sendMoney/{sourceAccountId}/{targetAccountId}/{amount}")
void sendMoney(@RequestBody SendMoneyForm form) { void sendMoney(
@PathVariable("sourceAccountId") Long sourceAccountId,
@PathVariable("targetAccountId") Long targetAccountId,
@PathVariable("amount") Long amount) {
SendMoneyCommand command = new SendMoneyCommand( SendMoneyCommand command = new SendMoneyCommand(
new AccountId(form.sourceAccountId), new AccountId(sourceAccountId),
new AccountId(form.targetAccountId), new AccountId(targetAccountId),
Money.of(form.amount)); Money.of(amount));
sendMoneyUseCase.sendMoney(command); sendMoneyUseCase.sendMoney(command);
} }
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class SendMoneyForm {
private Long sourceAccountId;
private Long targetAccountId;
private Long amount;
}
} }

View File

@@ -1,7 +1,5 @@
package io.reflectoring.cashpal.adapter.web; package io.reflectoring.cashpal.adapter.web;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.reflectoring.cashpal.adapter.web.SendMoneyController.SendMoneyForm;
import io.reflectoring.cashpal.application.port.in.SendMoneyUseCase; import io.reflectoring.cashpal.application.port.in.SendMoneyUseCase;
import io.reflectoring.cashpal.application.port.in.SendMoneyUseCase.SendMoneyCommand; import io.reflectoring.cashpal.application.port.in.SendMoneyUseCase.SendMoneyCommand;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -22,20 +20,11 @@ class SendMoneyControllerTest {
@MockBean @MockBean
private SendMoneyUseCase sendMoneyUseCase; private SendMoneyUseCase sendMoneyUseCase;
private ObjectMapper jsonMapper = new ObjectMapper();
@Test @Test
void testSendMoney() throws Exception { void testSendMoney() throws Exception {
SendMoneyForm form = new SendMoneyForm( mockMvc.perform(post("/accounts/sendMoney/{sourceAccountId}/{targetAccountId}/{amount}", 41L, 42L, 500)
41L, .header("Content-Type", "application/json"))
42L,
500L
);
mockMvc.perform(post("/sendMoney")
.header("Content-Type", "application/json")
.content(jsonMapper.writeValueAsString(form)))
.andExpect(status().isOk()); .andExpect(status().isOk());
then(sendMoneyUseCase).should().sendMoney(any(SendMoneyCommand.class)); then(sendMoneyUseCase).should().sendMoney(any(SendMoneyCommand.class));

View File

@@ -17,15 +17,18 @@ public interface SendMoneyUseCase {
class SendMoneyCommand extends SelfValidating<SendMoneyCommand> { class SendMoneyCommand extends SelfValidating<SendMoneyCommand> {
@NotNull @NotNull
private Account.AccountId sourceAccountId; private final Account.AccountId sourceAccountId;
@NotNull @NotNull
private Account.AccountId targetAccountId; private final Account.AccountId targetAccountId;
@NotNull @NotNull
private Money money; private final Money money;
public SendMoneyCommand(Account.AccountId sourceAccountId, Account.AccountId targetAccountId, Money money) { public SendMoneyCommand(
Account.AccountId sourceAccountId,
Account.AccountId targetAccountId,
Money money) {
this.sourceAccountId = sourceAccountId; this.sourceAccountId = sourceAccountId;
this.targetAccountId = targetAccountId; this.targetAccountId = targetAccountId;
this.money = money; this.money = money;

View File

@@ -53,7 +53,9 @@ public class Account {
* Calculates the total balance of the account by adding the activity values to the baseline balance. * Calculates the total balance of the account by adding the activity values to the baseline balance.
*/ */
public Money calculateBalance() { public Money calculateBalance() {
return Money.add(this.activityWindow.calculateBalance(this.id), this.baselineBalance); return Money.add(
this.baselineBalance,
this.activityWindow.calculateBalance(this.id));
} }
/** /**
@@ -63,22 +65,39 @@ public class Account {
*/ */
public boolean withdraw(Money money, AccountId targetAccountId) { public boolean withdraw(Money money, AccountId targetAccountId) {
if(Money.add(this.calculateBalance(), money.negate()).isNegative()){ if (!mayWithdraw(money)) {
return false; return false;
} }
Activity withdrawal = new Activity(null, this.id, this.id, targetAccountId, LocalDateTime.now(), money); Activity withdrawal = new Activity(
this.id,
this.id,
targetAccountId,
LocalDateTime.now(),
money);
this.activityWindow.addActivity(withdrawal); this.activityWindow.addActivity(withdrawal);
return true; return true;
} }
private boolean mayWithdraw(Money money) {
return Money.add(
this.calculateBalance(),
money.negate())
.isPositiveOrZero();
}
/** /**
* Tries to deposit a certain amount of money to this account. * Tries to deposit a certain amount of money to this account.
* If sucessful, creates a new activity with a positive value. * If sucessful, creates a new activity with a positive value.
* @return true if the deposit was successful, false if not. * @return true if the deposit was successful, false if not.
*/ */
public boolean deposit(Money money, AccountId sourceAccountId) { public boolean deposit(Money money, AccountId sourceAccountId) {
Activity deposit = new Activity(null, this.id, sourceAccountId, this.id, LocalDateTime.now(), money); Activity deposit = new Activity(
this.id,
sourceAccountId,
this.id,
LocalDateTime.now(),
money);
this.activityWindow.addActivity(deposit); this.activityWindow.addActivity(deposit);
return true; return true;
} }

View File

@@ -4,16 +4,18 @@ import java.time.LocalDateTime;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Value; import lombok.Value;
/** /**
* A money transfer activity between {@link Account}s. * A money transfer activity between {@link Account}s.
*/ */
@Value @Value
@RequiredArgsConstructor
public class Activity { public class Activity {
@Getter @Getter
private final ActivityId id; private ActivityId id;
/** /**
* The account that owns this activity. * The account that owns this activity.
@@ -50,6 +52,20 @@ public class Activity {
@NonNull @NonNull
private final Money money; private final Money money;
public Activity(
@NonNull Account.AccountId ownerAccountId,
@NonNull Account.AccountId sourceAccountId,
@NonNull Account.AccountId targetAccountId,
@NonNull LocalDateTime timestamp,
@NonNull Money money) {
this.id = null;
this.ownerAccountId = ownerAccountId;
this.sourceAccountId = sourceAccountId;
this.targetAccountId = targetAccountId;
this.timestamp = timestamp;
this.money = money;
}
@Value @Value
public static class ActivityId { public static class ActivityId {
private final Long value; private final Long value;

View File

@@ -37,6 +37,14 @@ public class Money {
return new Money(a.amount.add(b.amount)); return new Money(a.amount.add(b.amount));
} }
public Money minus(Money money){
return new Money(this.amount.subtract(money.amount));
}
public Money plus(Money money){
return new Money(this.amount.add(money.amount));
}
public static Money subtract(Money a, Money b) { public static Money subtract(Money a, Money b) {
return new Money(a.amount.subtract(b.amount)); return new Money(a.amount.subtract(b.amount));
} }

View File

@@ -60,11 +60,8 @@ class SendMoneyServiceTest {
@Test @Test
void transactionSucceeds() { void transactionSucceeds() {
AccountId sourceAccountId = new AccountId(41L); Account sourceAccount = givenSourceAccount();
Account sourceAccount = givenAnAccountWithId(sourceAccountId); Account targetAccount = givenTargetAccount();
AccountId targetAccountId = new AccountId(42L);
Account targetAccount = givenAnAccountWithId(targetAccountId);
givenWithdrawalWillSucceed(sourceAccount); givenWithdrawalWillSucceed(sourceAccount);
givenDepositWillSucceed(targetAccount); givenDepositWillSucceed(targetAccount);
@@ -72,14 +69,17 @@ class SendMoneyServiceTest {
Money money = Money.of(500L); Money money = Money.of(500L);
SendMoneyCommand command = new SendMoneyCommand( SendMoneyCommand command = new SendMoneyCommand(
sourceAccountId, sourceAccount.getId(),
targetAccountId, targetAccount.getId(),
money); money);
boolean success = sendMoneyService.sendMoney(command); boolean success = sendMoneyService.sendMoney(command);
assertThat(success).isTrue(); assertThat(success).isTrue();
AccountId sourceAccountId = sourceAccount.getId();
AccountId targetAccountId = targetAccount.getId();
then(accountLock).should().lockAccount(eq(sourceAccountId)); then(accountLock).should().lockAccount(eq(sourceAccountId));
then(sourceAccount).should().withdraw(eq(money), eq(targetAccountId)); then(sourceAccount).should().withdraw(eq(money), eq(targetAccountId));
then(accountLock).should().releaseAccount(eq(sourceAccountId)); then(accountLock).should().releaseAccount(eq(sourceAccountId));
@@ -121,6 +121,14 @@ class SendMoneyServiceTest {
.willReturn(true); .willReturn(true);
} }
private Account givenTargetAccount(){
return givenAnAccountWithId(new AccountId(42L));
}
private Account givenSourceAccount(){
return givenAnAccountWithId(new AccountId(41L));
}
private Account givenAnAccountWithId(AccountId id) { private Account givenAnAccountWithId(AccountId id) {
Account account = Mockito.mock(Account.class); Account account = Mockito.mock(Account.class);
given(account.getId()) given(account.getId())