"""Boundary and invalid-input tests for the DCF engine.""" import pandas as pd import pytest from app.services.data_service import _run_dcf def _fcf_series(base: float, growth: float) -> pd.Series: """Return a two-point historical FCF series whose median YoY growth equals `growth`.""" prior = base / (1 + growth) return pd.Series([prior, base], index=pd.to_datetime(["2023-09-30", "2024-09-30"])) def _expected_mid_year_enterprise_value( base_fcf: float, growth: float, wacc: float, terminal_growth: float, projection_years: int, ) -> float: projected = [base_fcf * ((1 + growth) ** year) for year in range(1, projection_years + 1)] discounted = [fcf / ((1 + wacc) ** (year - 0.5)) for year, fcf in enumerate(projected, start=1)] terminal_fcf = projected[-1] * (1 + terminal_growth) terminal_value = terminal_fcf / (wacc - terminal_growth) terminal_value_pv = terminal_value / ((1 + wacc) ** (projection_years - 0.5)) return sum(discounted) + terminal_value_pv def test_run_dcf_two_year_horizon_uses_mid_year_discounting() -> None: fcf = _fcf_series(100.0, 0.05) expected_enterprise_value = _expected_mid_year_enterprise_value( base_fcf=100.0, growth=0.05, wacc=0.10, terminal_growth=0.03, projection_years=2, ) result = _run_dcf( fcf_series=fcf, shares_outstanding=10.0, wacc=0.10, terminal_growth=0.03, projection_years=2, ) assert result["enterprise_value"] == pytest.approx(expected_enterprise_value) def test_run_dcf_terminal_growth_equals_wacc_is_error() -> None: fcf = _fcf_series(100.0, 0.05) result = _run_dcf( fcf_series=fcf, shares_outstanding=10.0, wacc=0.10, terminal_growth=0.10, projection_years=5, ) assert "error" in result assert "Terminal growth" in result["error"] assert "intrinsic_value_per_share" not in result def test_run_dcf_terminal_growth_above_wacc_is_error() -> None: fcf = _fcf_series(100.0, 0.05) result = _run_dcf( fcf_series=fcf, shares_outstanding=10.0, wacc=0.10, terminal_growth=0.11, projection_years=5, ) assert "error" in result assert "Terminal growth" in result["error"] assert "intrinsic_value_per_share" not in result def test_run_dcf_zero_wacc_is_error() -> None: fcf = _fcf_series(100.0, 0.05) result = _run_dcf( fcf_series=fcf, shares_outstanding=10.0, wacc=0.0, terminal_growth=0.03, projection_years=5, ) assert "error" in result assert "WACC" in result["error"] assert "intrinsic_value_per_share" not in result def test_run_dcf_negative_base_fcf_is_error() -> None: """The most recent FCF is negative, making the DCF not meaningful.""" fcf = pd.Series([100.0, -50.0], index=pd.to_datetime(["2023-09-30", "2024-09-30"])) result = _run_dcf( fcf_series=fcf, shares_outstanding=10.0, wacc=0.10, terminal_growth=0.03, projection_years=5, ) assert "error" in result assert "negative" in result["error"].lower() or "zero" in result["error"].lower() assert "intrinsic_value_per_share" not in result def test_run_dcf_zero_base_fcf_is_error() -> None: fcf = pd.Series([100.0, 0.0], index=pd.to_datetime(["2023-09-30", "2024-09-30"])) result = _run_dcf( fcf_series=fcf, shares_outstanding=10.0, wacc=0.10, terminal_growth=0.03, projection_years=5, ) assert "error" in result assert "zero" in result["error"].lower() assert "intrinsic_value_per_share" not in result def test_run_dcf_zero_shares_returns_empty() -> None: fcf = _fcf_series(100.0, 0.05) result = _run_dcf( fcf_series=fcf, shares_outstanding=0.0, wacc=0.10, terminal_growth=0.03, projection_years=5, ) assert result == {} def test_run_dcf_negative_shares_returns_empty() -> None: fcf = _fcf_series(100.0, 0.05) result = _run_dcf( fcf_series=fcf, shares_outstanding=-1.0, wacc=0.10, terminal_growth=0.03, projection_years=5, ) assert result == {} def test_run_dcf_insufficient_history_returns_empty() -> None: fcf = pd.Series([100.0], index=pd.to_datetime(["2024-09-30"])) result = _run_dcf( fcf_series=fcf, shares_outstanding=10.0, wacc=0.10, terminal_growth=0.03, projection_years=5, ) assert result == {}