from datetime import datetime, timezone from pydantic import ValidationError from models import ( ActionEnum, AnalysisResult, ConfidenceEnum, Holding, HoldingAnalysis, MetricsResponse, PortfolioRequest, PortfolioResponse, QuoteResponse, ) def test_holding_valid(): h = Holding(symbol="AAPL", shares=100, buy_in_price=150.0) assert h.symbol == "AAPL" assert h.shares == 100 assert h.buy_in_price == 150.0 def test_holding_swedish_symbol(): h = Holding(symbol="VOLV-B.ST", shares=50, buy_in_price=250.0) assert h.symbol == "VOLV-B.ST" def test_holding_symbol_uppercased(): h = Holding(symbol="aapl", shares=10, buy_in_price=150.0) assert h.symbol == "AAPL" def test_holding_invalid_symbol_format(): try: Holding(symbol="AAPL;DROP TABLE", shares=10, buy_in_price=150.0) assert False, "Should have raised" except ValidationError: pass def test_holding_symbol_too_long(): try: Holding(symbol="A" * 21, shares=10, buy_in_price=150.0) assert False, "Should have raised" except ValidationError: pass def test_holding_invalid_shares(): try: Holding(symbol="AAPL", shares=0, buy_in_price=150.0) assert False, "Should have raised" except ValidationError: pass def test_holding_invalid_price(): try: Holding(symbol="AAPL", shares=10, buy_in_price=-5.0) assert False, "Should have raised" except ValidationError: pass def test_portfolio_request_empty(): try: PortfolioRequest(holdings=[]) assert False, "Should have raised" except ValidationError: pass def test_portfolio_request_too_many(): holdings = [ Holding(symbol="AAPL", shares=1, buy_in_price=100) for _ in range(51) ] try: PortfolioRequest(holdings=holdings) assert False, "Should have raised" except ValidationError: pass def test_portfolio_request_valid(): req = PortfolioRequest( holdings=[Holding(symbol="AAPL", shares=10, buy_in_price=150)] ) assert len(req.holdings) == 1 def test_quote_response_defaults(): q = QuoteResponse(symbol="AAPL") assert q.price is None assert q.change is None def test_metrics_response(): m = MetricsResponse(symbol="AAPL", pe_ratio=25.0, roe=0.15) assert m.pe_ratio == 25.0 assert m.pb_ratio is None def test_analysis_result(): a = AnalysisResult( action=ActionEnum.BUY_MORE, confidence=ConfidenceEnum.HIGH, reasons=["Low PE", "Strong growth"], ) assert a.action == ActionEnum.BUY_MORE assert len(a.reasons) == 2 def test_holding_analysis(): ha = HoldingAnalysis( symbol="AAPL", current_price=180.0, buy_in_price=150.0, shares=100, pnl=3000.0, pnl_percent=0.2, analysis=AnalysisResult( action=ActionEnum.HOLD, confidence=ConfidenceEnum.MEDIUM, reasons=["Within hold range"], ), ) assert ha.pnl == 3000.0 def test_portfolio_response(): pr = PortfolioResponse( holdings=[ HoldingAnalysis( symbol="AAPL", buy_in_price=150.0, shares=100, analysis=AnalysisResult( action=ActionEnum.HOLD, confidence=ConfidenceEnum.LOW, reasons=["No data"], ), ) ], analyzed_at=datetime.now(timezone.utc), ) assert len(pr.holdings) == 1