From e090061a3f4618a25aea132a0206cca33775ee9c Mon Sep 17 00:00:00 2001 From: Eyo Chen Date: Sun, 25 May 2025 18:45:13 +0800 Subject: [PATCH 1/3] refactor: file structure --- src/handler/stock.py | 2 +- src/index.py | 2 +- src/tests/test_stock_handler.py | 2 +- src/tests/test_stock_usecase.py | 2 +- src/usecase/{stock => }/base.py | 0 src/usecase/{stock => }/stock.py | 0 6 files changed, 4 insertions(+), 4 deletions(-) rename src/usecase/{stock => }/base.py (100%) rename src/usecase/{stock => }/stock.py (100%) diff --git a/src/handler/stock.py b/src/handler/stock.py index 12a843d..354c9af 100644 --- a/src/handler/stock.py +++ b/src/handler/stock.py @@ -5,7 +5,7 @@ import proto.stock_pb2 as stock_pb2 import proto.stock_pb2_grpc as stock_pb2_grpc -from usecase.stock.base import AbstractStockUsecase +from usecase.base import AbstractStockUsecase from domain.stock import CreateStock, ActionType, ACTION_MAP diff --git a/src/index.py b/src/index.py index ce51ff9..6b0fdc9 100644 --- a/src/index.py +++ b/src/index.py @@ -5,7 +5,7 @@ from dotenv import load_dotenv from handler.stock import StockService from adapters.stock import StockRepository -from usecase.stock.stock import StockUsecase +from usecase.stock import StockUsecase load_dotenv() diff --git a/src/tests/test_stock_handler.py b/src/tests/test_stock_handler.py index b446148..0476dd8 100644 --- a/src/tests/test_stock_handler.py +++ b/src/tests/test_stock_handler.py @@ -7,7 +7,7 @@ from domain.stock import CreateStock, ActionType from handler.stock import StockService -from usecase.stock.base import AbstractStockUsecase +from usecase.base import AbstractStockUsecase class TestStockServiceCreate: diff --git a/src/tests/test_stock_usecase.py b/src/tests/test_stock_usecase.py index 2059754..dcb091f 100644 --- a/src/tests/test_stock_usecase.py +++ b/src/tests/test_stock_usecase.py @@ -1,7 +1,7 @@ import pytest from datetime import datetime from unittest.mock import Mock -from usecase.stock.stock import StockUsecase +from usecase.stock import StockUsecase from domain.stock import CreateStock, ActionType diff --git a/src/usecase/stock/base.py b/src/usecase/base.py similarity index 100% rename from src/usecase/stock/base.py rename to src/usecase/base.py diff --git a/src/usecase/stock/stock.py b/src/usecase/stock.py similarity index 100% rename from src/usecase/stock/stock.py rename to src/usecase/stock.py From 24e789189c685984667e3cf9a9bdf0edbe650017 Mon Sep 17 00:00:00 2001 From: Eyo Chen Date: Sun, 25 May 2025 18:53:34 +0800 Subject: [PATCH 2/3] feat: add list stock usecase --- src/usecase/base.py | 7 ++++++- src/usecase/stock.py | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/usecase/base.py b/src/usecase/base.py index 275f829..399c24d 100644 --- a/src/usecase/base.py +++ b/src/usecase/base.py @@ -1,8 +1,13 @@ +from typing import List from abc import ABC, abstractmethod -from domain.stock import CreateStock + +from domain.stock import CreateStock, Stock class AbstractStockUsecase(ABC): @abstractmethod def create(self, stock: CreateStock) -> str: """Create a new stock entry.""" + + def list(self, user_id: int) -> List[Stock]: + """List all stock by user id""" diff --git a/src/usecase/stock.py b/src/usecase/stock.py index fa8071b..83e0db4 100644 --- a/src/usecase/stock.py +++ b/src/usecase/stock.py @@ -1,4 +1,6 @@ -from domain.stock import CreateStock +from typing import List + +from domain.stock import CreateStock, Stock from adapters.base import AbstractStockRepository from .base import AbstractStockUsecase @@ -9,3 +11,6 @@ def __init__(self, stock_repo: AbstractStockRepository): def create(self, stock: CreateStock): return self.stock_repo.create(stock) + + def list(self, user_id: int) -> List[Stock]: + return self.stock_repo.list(user_id) From 90e00631b106645b10d386a0bb762a72ec428fcf Mon Sep 17 00:00:00 2001 From: Eyo Chen Date: Sun, 25 May 2025 18:53:44 +0800 Subject: [PATCH 3/3] test: add unit testing --- src/tests/test_stock_usecase.py | 59 ++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/src/tests/test_stock_usecase.py b/src/tests/test_stock_usecase.py index dcb091f..9ff9fd2 100644 --- a/src/tests/test_stock_usecase.py +++ b/src/tests/test_stock_usecase.py @@ -1,8 +1,8 @@ import pytest -from datetime import datetime +from datetime import datetime, timezone from unittest.mock import Mock from usecase.stock import StockUsecase -from domain.stock import CreateStock, ActionType +from domain.stock import CreateStock, ActionType, Stock @pytest.fixture @@ -22,7 +22,7 @@ def test_create(self, stock_usecase): price=150.25, quantity=100, action_type=ActionType.BUY, - created_at=datetime.utcnow(), + created_at=datetime.now(timezone.utc), ) # Define the expected return value from the mock repository @@ -45,7 +45,7 @@ def test_create_handles_repository_error(self, stock_usecase): price=150.25, quantity=100, action_type=ActionType.BUY, - created_at=datetime.utcnow(), + created_at=datetime.now(timezone.utc), ) # Simulate an exception from the repository @@ -55,3 +55,54 @@ def test_create_handles_repository_error(self, stock_usecase): with pytest.raises(Exception, match="Repository error"): usecase.create(mock_stock) mock_repo.create.assert_called_once_with(mock_stock) + + def test_list(self, stock_usecase): + # Arrange + usecase, mock_repo = stock_usecase + user_id = 1 + created_at = datetime.now(timezone.utc) + mock_stocks = [ + Stock( + id="stock_123", + user_id=1, + symbol="AAPL", + price=150.25, + quantity=100, + action_type=ActionType.BUY, + created_at=created_at, + updated_at=created_at, + ), + Stock( + id="stock_124", + user_id=1, + symbol="GOOGL", + price=2800.50, + quantity=10, + action_type=ActionType.BUY, + created_at=created_at, + updated_at=created_at, + ), + ] + mock_repo.list.return_value = mock_stocks + + # Act + result = usecase.list(user_id) + + # Assert + mock_repo.list.assert_called_once_with(user_id) + assert result == mock_stocks + assert len(result) == 2 + assert all(isinstance(stock, Stock) for stock in result) + assert result[0].symbol == "AAPL" + assert result[1].symbol == "GOOGL" + + def test_list_handles_repository_error(self, stock_usecase): + # Arrange + usecase, mock_repo = stock_usecase + user_id = 1 + mock_repo.list.side_effect = Exception("Repository error") + + # Act/Assert + with pytest.raises(Exception, match="Repository error"): + usecase.list(user_id) + mock_repo.list.assert_called_once_with(user_id)