From 3bded5a44f391c93e446fb6dddcd5977a43f8ebc Mon Sep 17 00:00:00 2001 From: AlfredoMate Date: Wed, 4 Jun 2025 19:24:24 +0200 Subject: [PATCH 1/2] tests --- .../io/autoinvestor/domain/model/Wallet.java | 5 + .../domain/model/WalletState.java | 4 + .../InMemoryHoldingsReadModel.java | 23 +++- .../InMemoryUsersWalletReadModel.java | 22 +++- .../PortfolioIntegrationTest.java | 107 ++++++++++++++++++ src/test/resources/application.properties | 2 + 6 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 src/test/java/io/autoinvestor/PortfolioIntegrationTest.java create mode 100644 src/test/resources/application.properties diff --git a/src/main/java/io/autoinvestor/domain/model/Wallet.java b/src/main/java/io/autoinvestor/domain/model/Wallet.java index ab4b514..2ee88af 100644 --- a/src/main/java/io/autoinvestor/domain/model/Wallet.java +++ b/src/main/java/io/autoinvestor/domain/model/Wallet.java @@ -99,4 +99,9 @@ private void whenHoldingUpdated(HoldingWasUpdatedEvent event) { private void whenHoldingDeleted(HoldingWasDeletedEvent event) { this.state = this.state.withHoldingDeleted(event); } + + public WalletState getState () { + return this.state; + } + } diff --git a/src/main/java/io/autoinvestor/domain/model/WalletState.java b/src/main/java/io/autoinvestor/domain/model/WalletState.java index d5dd298..facb11b 100644 --- a/src/main/java/io/autoinvestor/domain/model/WalletState.java +++ b/src/main/java/io/autoinvestor/domain/model/WalletState.java @@ -54,4 +54,8 @@ public WalletState withHoldingDeleted(HoldingWasDeletedEvent event) { holdings.remove(AssetId.of(payload.assetId())); return new WalletState(this.walletId, this.userId, this.holdings); } + + public String getWalletIdString (){ + return this.walletId.value(); + } } diff --git a/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryHoldingsReadModel.java b/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryHoldingsReadModel.java index cfa943b..30077ba 100644 --- a/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryHoldingsReadModel.java +++ b/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryHoldingsReadModel.java @@ -3,6 +3,8 @@ import io.autoinvestor.application.HoldingsReadModel; import io.autoinvestor.application.HoldingsReadModelDTO; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import org.springframework.context.annotation.Profile; @@ -11,8 +13,19 @@ @Repository @Profile("local") public class InMemoryHoldingsReadModel implements HoldingsReadModel { + + Collection holdings = new ArrayList<>(); + + public InMemoryHoldingsReadModel () { + holdings.add(new HoldingsReadModelDTO("user-1", + "asset-1", + 1, + 1)); + } @Override - public void add(HoldingsReadModelDTO dto) {} + public void add(HoldingsReadModelDTO dto) { + holdings.add(dto); + } @Override public void update(HoldingsReadModelDTO dto) {} @@ -24,11 +37,17 @@ public boolean delete(String userId, String assetId) { @Override public List getHoldings(String userId) { - return List.of(); + return holdings.stream() + .filter(doc -> doc.userId().equals(userId)) + .toList(); } @Override public boolean assetAlreadyExists(String userIs, String assetId) { return false; } + + public void clear() { + holdings.clear(); + } } diff --git a/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryUsersWalletReadModel.java b/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryUsersWalletReadModel.java index 3d34d44..6e4f898 100644 --- a/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryUsersWalletReadModel.java +++ b/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryUsersWalletReadModel.java @@ -6,14 +6,32 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Repository; +import java.util.HashMap; +import java.util.Map; + @Repository @Profile("local") public class InMemoryUsersWalletReadModel implements UsersWalletReadModel { + + private final Map readModel = new HashMap<>(); + + public InMemoryUsersWalletReadModel() { + readModel.put("user-1", "wallet-1"); + readModel.put("user-2", "wallet-2"); + } @Override - public void add(UsersWalletReadModelDTO dto) {} + public void add(UsersWalletReadModelDTO dto) { + readModel.put(dto.userId(), dto.walletId()); + } @Override public String getWalletId(String userId) { - return ""; + return readModel.get(userId); } + + public void clear() { + readModel.clear(); + } + + } diff --git a/src/test/java/io/autoinvestor/PortfolioIntegrationTest.java b/src/test/java/io/autoinvestor/PortfolioIntegrationTest.java new file mode 100644 index 0000000..0b2b3e4 --- /dev/null +++ b/src/test/java/io/autoinvestor/PortfolioIntegrationTest.java @@ -0,0 +1,107 @@ +package io.autoinvestor; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import io.autoinvestor.application.NewHoldingUseCase.NewHoldingCommand; +import io.autoinvestor.application.NewHoldingUseCase.NewHoldingCommandHandler; +import io.autoinvestor.application.WalletCreatedUseCase.WalletCreateCommand; +import io.autoinvestor.domain.model.Wallet; +import io.autoinvestor.infrastructure.read_models.InMemoryHoldingsReadModel; +import io.autoinvestor.infrastructure.read_models.InMemoryUsersWalletReadModel; +import io.autoinvestor.application.WalletCreatedUseCase.WalletCreatedHandler; +import io.autoinvestor.infrastructure.repositories.InMemoryEventStoreRepository; +import io.autoinvestor.application.*; +import io.autoinvestor.domain.model.UserId; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.Collection; +import java.util.List; + +@SpringBootTest +@AutoConfigureMockMvc +@ActiveProfiles("local") +class PortfolioIntegrationTest { + + @Autowired + private InMemoryUsersWalletReadModel readModelUserWallet; + + @Autowired + private WalletCreatedHandler walletCreatedCommandHandler; + + @Autowired + private InMemoryHoldingsReadModel holdingsReadModel; + + @Autowired + private NewHoldingCommandHandler newHoldingCommandHandler; + + @Autowired + private InMemoryEventStoreRepository eventStore; + @BeforeEach + void resetState() { + readModelUserWallet.clear(); + holdingsReadModel.clear(); + } + + @Test + void createWalletHandler_shouldCreateWalletForUser() { + // GIVEN: a user ID that does not exist yet + String userId = "user-1"; + + // PRECONDITION: portfolioRepository has no wallet for "user-1" + assertThat(readModelUserWallet.getWalletId(userId)).isNull(); + + // WHEN: we invoke the CreateWalletCommandHandler with the id of the user + walletCreatedCommandHandler.handle(new WalletCreateCommand(userId)); + + + // THEN: the portfolioRepository should now contain a wallet for "user-1" + assertThat(readModelUserWallet.getWalletId(userId)).isNotEmpty(); + + } + + + @Test + void createAsset_shouldCreateAssetForUser() { + String userId = "user-1"; + String walletId = "wallet-1"; + String assetId = "asset-id"; + int amount = 1; + int boughtPrice = 1; + + // Add wallet to usersWalletReadModel + + + // Simulate wallet creation event and save it in the event store + Wallet wallet = Wallet.create(userId); + // Wallet.create generates WalletWasCreatedEvent inside + + readModelUserWallet.add(new UsersWalletReadModelDTO(wallet.getState().getWalletIdString(), userId)); + // Save the wallet creation event(s) in the event store + eventStore.save(wallet); + + // Now asset should NOT exist yet + assertThat(holdingsReadModel.assetAlreadyExists(userId, assetId)).isFalse(); + + // Call your handler + newHoldingCommandHandler.handle(new NewHoldingCommand(assetId, userId, amount, boughtPrice)); + + // Verify the holding is added + Collection holdingsOfUser = holdingsReadModel.getHoldings(userId); + assertThat(holdingsOfUser) + .extracting(HoldingsReadModelDTO::assetId) + .contains(assetId); + } + + +} \ No newline at end of file diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..f868be3 --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,2 @@ +spring.application.name=portfolio +spring.profiles.active=local \ No newline at end of file From ab5695905b4e720eb2e306ab99fb051e582a7ac1 Mon Sep 17 00:00:00 2001 From: AlfredoMate Date: Wed, 4 Jun 2025 19:38:10 +0200 Subject: [PATCH 2/2] spotless apply --- .../io/autoinvestor/domain/model/Wallet.java | 3 +- .../domain/model/WalletState.java | 2 +- .../InMemoryHoldingsReadModel.java | 12 ++--- .../InMemoryUsersWalletReadModel.java | 9 ++-- .../PortfolioIntegrationTest.java | 46 ++++++------------- 5 files changed, 25 insertions(+), 47 deletions(-) diff --git a/src/main/java/io/autoinvestor/domain/model/Wallet.java b/src/main/java/io/autoinvestor/domain/model/Wallet.java index 2ee88af..48902ec 100644 --- a/src/main/java/io/autoinvestor/domain/model/Wallet.java +++ b/src/main/java/io/autoinvestor/domain/model/Wallet.java @@ -100,8 +100,7 @@ private void whenHoldingDeleted(HoldingWasDeletedEvent event) { this.state = this.state.withHoldingDeleted(event); } - public WalletState getState () { + public WalletState getState() { return this.state; } - } diff --git a/src/main/java/io/autoinvestor/domain/model/WalletState.java b/src/main/java/io/autoinvestor/domain/model/WalletState.java index facb11b..fbed85c 100644 --- a/src/main/java/io/autoinvestor/domain/model/WalletState.java +++ b/src/main/java/io/autoinvestor/domain/model/WalletState.java @@ -55,7 +55,7 @@ public WalletState withHoldingDeleted(HoldingWasDeletedEvent event) { return new WalletState(this.walletId, this.userId, this.holdings); } - public String getWalletIdString (){ + public String getWalletIdString() { return this.walletId.value(); } } diff --git a/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryHoldingsReadModel.java b/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryHoldingsReadModel.java index 30077ba..652d792 100644 --- a/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryHoldingsReadModel.java +++ b/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryHoldingsReadModel.java @@ -16,12 +16,10 @@ public class InMemoryHoldingsReadModel implements HoldingsReadModel { Collection holdings = new ArrayList<>(); - public InMemoryHoldingsReadModel () { - holdings.add(new HoldingsReadModelDTO("user-1", - "asset-1", - 1, - 1)); + public InMemoryHoldingsReadModel() { + holdings.add(new HoldingsReadModelDTO("user-1", "asset-1", 1, 1)); } + @Override public void add(HoldingsReadModelDTO dto) { holdings.add(dto); @@ -37,9 +35,7 @@ public boolean delete(String userId, String assetId) { @Override public List getHoldings(String userId) { - return holdings.stream() - .filter(doc -> doc.userId().equals(userId)) - .toList(); + return holdings.stream().filter(doc -> doc.userId().equals(userId)).toList(); } @Override diff --git a/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryUsersWalletReadModel.java b/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryUsersWalletReadModel.java index 6e4f898..68030ff 100644 --- a/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryUsersWalletReadModel.java +++ b/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryUsersWalletReadModel.java @@ -3,12 +3,12 @@ import io.autoinvestor.application.UsersWalletReadModel; import io.autoinvestor.application.UsersWalletReadModelDTO; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Repository; - import java.util.HashMap; import java.util.Map; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + @Repository @Profile("local") public class InMemoryUsersWalletReadModel implements UsersWalletReadModel { @@ -19,6 +19,7 @@ public InMemoryUsersWalletReadModel() { readModel.put("user-1", "wallet-1"); readModel.put("user-2", "wallet-2"); } + @Override public void add(UsersWalletReadModelDTO dto) { readModel.put(dto.userId(), dto.walletId()); @@ -32,6 +33,4 @@ public String getWalletId(String userId) { public void clear() { readModel.clear(); } - - } diff --git a/src/test/java/io/autoinvestor/PortfolioIntegrationTest.java b/src/test/java/io/autoinvestor/PortfolioIntegrationTest.java index 0b2b3e4..bd50853 100644 --- a/src/test/java/io/autoinvestor/PortfolioIntegrationTest.java +++ b/src/test/java/io/autoinvestor/PortfolioIntegrationTest.java @@ -1,52 +1,42 @@ package io.autoinvestor; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import io.autoinvestor.application.*; import io.autoinvestor.application.NewHoldingUseCase.NewHoldingCommand; import io.autoinvestor.application.NewHoldingUseCase.NewHoldingCommandHandler; import io.autoinvestor.application.WalletCreatedUseCase.WalletCreateCommand; +import io.autoinvestor.application.WalletCreatedUseCase.WalletCreatedHandler; import io.autoinvestor.domain.model.Wallet; import io.autoinvestor.infrastructure.read_models.InMemoryHoldingsReadModel; import io.autoinvestor.infrastructure.read_models.InMemoryUsersWalletReadModel; -import io.autoinvestor.application.WalletCreatedUseCase.WalletCreatedHandler; import io.autoinvestor.infrastructure.repositories.InMemoryEventStoreRepository; -import io.autoinvestor.application.*; -import io.autoinvestor.domain.model.UserId; +import java.util.Collection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.web.servlet.MockMvc; - -import java.util.Collection; -import java.util.List; @SpringBootTest @AutoConfigureMockMvc @ActiveProfiles("local") class PortfolioIntegrationTest { - @Autowired - private InMemoryUsersWalletReadModel readModelUserWallet; + @Autowired private InMemoryUsersWalletReadModel readModelUserWallet; - @Autowired - private WalletCreatedHandler walletCreatedCommandHandler; + @Autowired private WalletCreatedHandler walletCreatedCommandHandler; - @Autowired - private InMemoryHoldingsReadModel holdingsReadModel; + @Autowired private InMemoryHoldingsReadModel holdingsReadModel; - @Autowired - private NewHoldingCommandHandler newHoldingCommandHandler; + @Autowired private NewHoldingCommandHandler newHoldingCommandHandler; + + @Autowired private InMemoryEventStoreRepository eventStore; - @Autowired - private InMemoryEventStoreRepository eventStore; @BeforeEach void resetState() { readModelUserWallet.clear(); @@ -64,13 +54,10 @@ void createWalletHandler_shouldCreateWalletForUser() { // WHEN: we invoke the CreateWalletCommandHandler with the id of the user walletCreatedCommandHandler.handle(new WalletCreateCommand(userId)); - // THEN: the portfolioRepository should now contain a wallet for "user-1" assertThat(readModelUserWallet.getWalletId(userId)).isNotEmpty(); - } - @Test void createAsset_shouldCreateAssetForUser() { String userId = "user-1"; @@ -81,12 +68,12 @@ void createAsset_shouldCreateAssetForUser() { // Add wallet to usersWalletReadModel - // Simulate wallet creation event and save it in the event store Wallet wallet = Wallet.create(userId); // Wallet.create generates WalletWasCreatedEvent inside - readModelUserWallet.add(new UsersWalletReadModelDTO(wallet.getState().getWalletIdString(), userId)); + readModelUserWallet.add( + new UsersWalletReadModelDTO(wallet.getState().getWalletIdString(), userId)); // Save the wallet creation event(s) in the event store eventStore.save(wallet); @@ -94,14 +81,11 @@ void createAsset_shouldCreateAssetForUser() { assertThat(holdingsReadModel.assetAlreadyExists(userId, assetId)).isFalse(); // Call your handler - newHoldingCommandHandler.handle(new NewHoldingCommand(assetId, userId, amount, boughtPrice)); + newHoldingCommandHandler.handle( + new NewHoldingCommand(assetId, userId, amount, boughtPrice)); // Verify the holding is added Collection holdingsOfUser = holdingsReadModel.getHoldings(userId); - assertThat(holdingsOfUser) - .extracting(HoldingsReadModelDTO::assetId) - .contains(assetId); + assertThat(holdingsOfUser).extracting(HoldingsReadModelDTO::assetId).contains(assetId); } - - -} \ No newline at end of file +}