diff options
| author | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-18 01:25:05 -0700 |
|---|---|---|
| committer | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-18 01:25:05 -0700 |
| commit | 23fcb2087473d13fa0bcc9adf3f0e10039f249fb (patch) | |
| tree | 1cc46dcbb36f7a3ab41a1478fa72655bb48c0e55 /backend/tests | |
| parent | 8a7dff97216fd301c7f3c4f20bebec917451d911 (diff) | |
feat: add valuation math helpers and VALUATION_CACHE
Diffstat (limited to 'backend/tests')
| -rw-r--r-- | backend/tests/test_api.py | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index 395bc12..a5c4eb5 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -18,6 +18,7 @@ def clear_service_caches() -> None: data_service.SHARES_CACHE.clear() data_service.RATIO_CACHE.clear() data_service.FINANCIALS_CACHE.clear() + data_service.VALUATION_CACHE.clear() def quarterly_frame(rows: dict[str, list[float]]) -> pd.DataFrame: @@ -627,3 +628,59 @@ def test_valuation_schema_structure() -> None: assert resp.dcf.intrinsic_value_per_share == 182.0 assert resp.ev_ebitda.multiple_used == 20.0 assert resp.ev_revenue.available is False + + +def test_build_fcf_series_happy_path() -> None: + cf = annual_frame({ + "Operating Cash Flow": [100.0, 90.0, 80.0, 70.0], + "Capital Expenditure": [-10.0, -9.0, -8.0, -7.0], + }) + result = data_service._build_fcf_series(cf) + assert result is not None + assert len(result) == 4 + # most recent year FCF = 100 + (-10) = 90 + assert result.iloc[-1] == 90.0 + + +def test_build_fcf_series_empty_df() -> None: + result = data_service._build_fcf_series(pd.DataFrame()) + assert result is None + + +def test_build_fcf_series_missing_capex() -> None: + cf = annual_frame({"Operating Cash Flow": [100.0, 90.0, 80.0, 70.0]}) + result = data_service._build_fcf_series(cf) + assert result is None + + +def test_build_multiple_result_empty() -> None: + result = data_service._build_multiple_result({}) + assert result == {"available": False} + + +def test_build_multiple_result_valid() -> None: + raw = { + "implied_price_per_share": 178.0, + "implied_ev": 1_000.0, + "equity_value": 900.0, + "net_debt": 100.0, + "target_multiple_used": 20.0, + } + result = data_service._build_multiple_result(raw) + assert result["available"] is True + assert result["implied_price_per_share"] == 178.0 + assert result["multiple_used"] == 20.0 + + +def test_dcf_capped_growth_rate_caps_extremes() -> None: + # growth of 200% should be capped at 50% + series = pd.Series([10.0, 30.0], index=pd.to_datetime(["2022", "2023"])) + result = data_service._dcf_capped_growth_rate(series) + assert result == 0.50 + + +def test_dcf_capped_growth_rate_skips_sign_flip() -> None: + # negative to positive is a sign flip — should skip and return None (no usable periods) + series = pd.Series([-10.0, 20.0], index=pd.to_datetime(["2022", "2023"])) + result = data_service._dcf_capped_growth_rate(series) + assert result is None |
