from models import ActionEnum, ConfidenceEnum from analysis_service import ( compute_analysis, _score_pnl, _score_pe, _score_revenue_growth, _score_target_price, ) class TestScorePnl: def test_large_loss_suggests_buy(self): score, reason = _score_pnl(-0.25) assert score == 1 assert "averaging down" in reason def test_large_profit_suggests_sell(self): score, reason = _score_pnl(0.60) assert score == -1 assert "taking profit" in reason def test_moderate_suggests_hold(self): score, reason = _score_pnl(0.10) assert score == 0 class TestScorePe: def test_low_pe(self): score, _ = _score_pe(10.0) assert score == 1 def test_high_pe(self): score, _ = _score_pe(50.0) assert score == -1 def test_normal_pe(self): score, _ = _score_pe(20.0) assert score == 0 def test_negative_pe(self): score, _ = _score_pe(-5.0) assert score == -1 def test_none_pe(self): score, reason = _score_pe(None) assert score == 0 assert reason is None class TestScoreRevenueGrowth: def test_strong_growth(self): score, _ = _score_revenue_growth(0.20) assert score == 1 def test_negative_growth(self): score, _ = _score_revenue_growth(-0.05) assert score == -1 def test_moderate_growth(self): score, _ = _score_revenue_growth(0.05) assert score == 0 def test_none(self): score, reason = _score_revenue_growth(None) assert score == 0 class TestScoreTargetPrice: def test_big_upside(self): score, _ = _score_target_price(100.0, 120.0) assert score == 1 def test_big_downside(self): score, _ = _score_target_price(100.0, 85.0) assert score == -1 def test_near_target(self): score, _ = _score_target_price(100.0, 105.0) assert score == 0 def test_none_price(self): score, _ = _score_target_price(None, 120.0) assert score == 0 class TestComputeAnalysis: def test_strong_buy_signals(self): result = compute_analysis( current_price=100.0, buy_in_price=130.0, # loss > 20% target_price=120.0, # upside > 15% metrics={"pe_ratio": 10.0, "revenue_growth": 0.20}, ) assert result.action == ActionEnum.BUY_MORE assert result.confidence == ConfidenceEnum.HIGH def test_strong_sell_signals(self): result = compute_analysis( current_price=200.0, buy_in_price=100.0, # profit > 50% target_price=170.0, # downside metrics={"pe_ratio": 50.0, "revenue_growth": -0.10}, ) assert result.action == ActionEnum.SELL def test_mixed_signals_hold(self): result = compute_analysis( current_price=100.0, buy_in_price=95.0, target_price=105.0, metrics={"pe_ratio": 20.0, "revenue_growth": 0.05}, ) assert result.action == ActionEnum.HOLD def test_no_data(self): result = compute_analysis( current_price=None, buy_in_price=100.0, target_price=None, metrics={}, ) assert result.action == ActionEnum.HOLD assert result.confidence == ConfidenceEnum.LOW def test_reasons_populated(self): result = compute_analysis( current_price=100.0, buy_in_price=90.0, target_price=110.0, metrics={"pe_ratio": 25.0}, ) assert len(result.reasons) > 0