From 5694d4e8b9c5e081a898b76185fce472c9e8f457 Mon Sep 17 00:00:00 2001 From: Eyo Chen Date: Sat, 7 Jun 2025 10:30:55 +0800 Subject: [PATCH 1/3] feat: add get portfolio adapter --- src/adapters/portfolio.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/adapters/portfolio.py b/src/adapters/portfolio.py index b5582d8..ec4de53 100644 --- a/src/adapters/portfolio.py +++ b/src/adapters/portfolio.py @@ -3,7 +3,7 @@ from pymongo import MongoClient from pymongo.database import Database from .base import AbstractPortfolioRepository -from domain.portfolio import Portfolio +from domain.portfolio import Portfolio, Holding class PortfolioRepository(AbstractPortfolioRepository): @@ -12,6 +12,23 @@ def __init__(self, mongo_client: MongoClient, database_name: str = "stock_db"): self.db: Database = self.client[database_name] self.collection = self.db["portfolio"] + def get(self, user_id: int) -> Portfolio: + result = self.collection.find_one({"user_id": user_id}) + if result is None: + return None + + return Portfolio( + user_id=result["user_id"], + cash_balance=result["cash_balance"], + total_money_in=result["total_money_in"], + holdings=[ + Holding(symbol=holding["symbol"], shares=holding["shares"], total_cost=holding["total_cost"]) + for holding in result["holdings"] + ], + created_at=result["created_at"], + updated_at=result["updated_at"], + ) + def update(self, portfolio: Portfolio) -> None: portfolio.updated_at = datetime.now(timezone.utc) self.collection.replace_one({"user_id": portfolio.user_id}, asdict(portfolio), upsert=True) From 995616aec39f42fd80382f94800d495a7d7f4b54 Mon Sep 17 00:00:00 2001 From: Eyo Chen Date: Sat, 7 Jun 2025 10:31:00 +0800 Subject: [PATCH 2/3] test: add integration testing --- src/tests/test_portfolio_adapters.py | 45 ++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/tests/test_portfolio_adapters.py b/src/tests/test_portfolio_adapters.py index 7148a62..7c1fe8b 100644 --- a/src/tests/test_portfolio_adapters.py +++ b/src/tests/test_portfolio_adapters.py @@ -84,3 +84,48 @@ def test_update_existing_portfolio(self, portfolio_repository): assert result["holdings"][0]["symbol"] == "AAPL" assert result["holdings"][0]["shares"] == 20 assert result["holdings"][0]["total_cost"] == 3000.0 + + def test_get_existing_portfolio(self, portfolio_repository): + # Arrange + created_at = datetime.now(timezone.utc) + portfolio = Portfolio( + user_id=1, + cash_balance=1000.0, + total_money_in=1000.0, + holdings=[Holding(symbol="AAPL", shares=10, total_cost=1500.0)], + created_at=created_at, + updated_at=created_at, + ) + portfolio_repository.collection.insert_one(asdict(portfolio)) + + # Action + result = portfolio_repository.get(user_id=1) + + # Assertion + assert result is not None + assert result.user_id == portfolio.user_id + assert result.cash_balance == portfolio.cash_balance + assert result.total_money_in == portfolio.total_money_in + assert len(result.holdings) == 1 + assert result.holdings[0].symbol == "AAPL" + assert result.holdings[0].shares == 10 + assert result.holdings[0].total_cost == 1500.0 + + def test_get_non_existent_portfolio(self, portfolio_repository): + # Arrange + created_at = datetime.now(timezone.utc) + portfolio = Portfolio( + user_id=1, + cash_balance=1000.0, + total_money_in=1000.0, + holdings=[Holding(symbol="AAPL", shares=10, total_cost=1500.0)], + created_at=created_at, + updated_at=created_at, + ) + portfolio_repository.collection.insert_one(asdict(portfolio)) + + # Action + result = portfolio_repository.get(user_id=999) + + # Assertion + assert result is None From fe45abf3d4bb9abb9a6b212a91c09bb431d0b072 Mon Sep 17 00:00:00 2001 From: Eyo Chen Date: Sat, 7 Jun 2025 10:47:03 +0800 Subject: [PATCH 3/3] refactor: use expected result --- src/tests/test_portfolio_adapters.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/tests/test_portfolio_adapters.py b/src/tests/test_portfolio_adapters.py index 7c1fe8b..3b49b80 100644 --- a/src/tests/test_portfolio_adapters.py +++ b/src/tests/test_portfolio_adapters.py @@ -1,5 +1,6 @@ import pytest from datetime import datetime, timezone +from unittest.mock import ANY from dataclasses import asdict from pymongo import MongoClient from domain.portfolio import Portfolio, Holding @@ -60,7 +61,7 @@ def test_update_existing_portfolio(self, portfolio_repository): created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc), ) - result = portfolio_repository.collection.insert_one(asdict(initial_portfolio)) + portfolio_repository.collection.insert_one(asdict(initial_portfolio)) updated_portfolio = Portfolio( user_id=1, @@ -97,19 +98,20 @@ def test_get_existing_portfolio(self, portfolio_repository): updated_at=created_at, ) portfolio_repository.collection.insert_one(asdict(portfolio)) + expected_result = Portfolio( + user_id=1, + cash_balance=1000.0, + total_money_in=1000.0, + holdings=[Holding(symbol="AAPL", shares=10, total_cost=1500.0)], + created_at=ANY, + updated_at=ANY, + ) # Action result = portfolio_repository.get(user_id=1) # Assertion - assert result is not None - assert result.user_id == portfolio.user_id - assert result.cash_balance == portfolio.cash_balance - assert result.total_money_in == portfolio.total_money_in - assert len(result.holdings) == 1 - assert result.holdings[0].symbol == "AAPL" - assert result.holdings[0].shares == 10 - assert result.holdings[0].total_cost == 1500.0 + assert result == expected_result def test_get_non_existent_portfolio(self, portfolio_repository): # Arrange