diff --git a/src/main/java/io/autoinvestor/domain/model/Wallet.java b/src/main/java/io/autoinvestor/domain/model/Wallet.java index ab4b514..48902ec 100644 --- a/src/main/java/io/autoinvestor/domain/model/Wallet.java +++ b/src/main/java/io/autoinvestor/domain/model/Wallet.java @@ -99,4 +99,8 @@ 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..fbed85c 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..652d792 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,17 @@ @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 +35,15 @@ 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..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,17 +3,34 @@ import io.autoinvestor.application.UsersWalletReadModel; import io.autoinvestor.application.UsersWalletReadModelDTO; +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 { + + 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..bd50853 --- /dev/null +++ b/src/test/java/io/autoinvestor/PortfolioIntegrationTest.java @@ -0,0 +1,91 @@ +package io.autoinvestor; + +import static org.assertj.core.api.Assertions.assertThat; +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.infrastructure.repositories.InMemoryEventStoreRepository; + +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.test.context.ActiveProfiles; + +@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); + } +} 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