summaryrefslogtreecommitdiff
path: root/backend/tests/test_api.py
diff options
context:
space:
mode:
Diffstat (limited to 'backend/tests/test_api.py')
-rw-r--r--backend/tests/test_api.py132
1 files changed, 132 insertions, 0 deletions
diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py
index af43975..94c9c20 100644
--- a/backend/tests/test_api.py
+++ b/backend/tests/test_api.py
@@ -19,6 +19,8 @@ def clear_service_caches() -> None:
data_service.RATIO_CACHE.clear()
data_service.FINANCIALS_CACHE.clear()
data_service.VALUATION_CACHE.clear()
+ data_service.HIST_RATIOS_CACHE.clear()
+ data_service.RATIOS_ENDPOINT_CACHE.clear()
def quarterly_frame(rows: dict[str, list[float]]) -> pd.DataFrame:
@@ -940,3 +942,133 @@ def test_valuation_route_returns_structure(monkeypatch) -> None:
assert result["dcf"]["intrinsic_value_per_share"] == 182.0
assert result["ev_ebitda"]["multiple_used"] == 20.0
assert result["ev_revenue"]["available"] is False
+
+
+def test_compute_historical_ratios_margins(monkeypatch) -> None:
+ clear_service_caches()
+ monkeypatch.setattr(
+ data_service,
+ "get_income_statement",
+ lambda symbol, quarterly=False: annual_frame(
+ {
+ "Total Revenue": [100.0, 90.0, 80.0, 70.0],
+ "Gross Profit": [50.0, 45.0, 40.0, 35.0],
+ "Operating Income": [20.0, 18.0, 16.0, 14.0],
+ "Net Income": [10.0, 9.0, 8.0, 7.0],
+ "EBITDA": [25.0, 22.5, 20.0, 17.5],
+ }
+ ),
+ )
+ monkeypatch.setattr(
+ data_service,
+ "get_balance_sheet",
+ lambda symbol, quarterly=False: annual_frame(
+ {
+ "Stockholders Equity": [50.0, 45.0, 40.0, 35.0],
+ "Total Assets": [100.0, 90.0, 80.0, 70.0],
+ "Total Debt": [20.0, 18.0, 16.0, 14.0],
+ "Current Assets": [30.0, 27.0, 24.0, 21.0],
+ "Current Liabilities": [15.0, 13.5, 12.0, 10.5],
+ "Cash And Cash Equivalents": [5.0, 4.5, 4.0, 3.5],
+ }
+ ),
+ )
+ monkeypatch.setattr(data_service, "get_cash_flow", lambda symbol, quarterly=False: annual_frame({"Operating Cash Flow": [1.0, 1.0, 1.0, 1.0]}))
+ monkeypatch.setattr(data_service, "get_shares_outstanding", lambda symbol: 1_000.0)
+ monkeypatch.setattr(data_service, "get_price_history", lambda symbol, period="5y": [])
+
+ result = data_service.compute_historical_ratios("AAPL")
+
+ assert result["gross_margin"] == [0.5, 0.5, 0.5, 0.5]
+ assert result["operating_margin"] == [0.2, 0.2, 0.2, 0.2]
+ assert result["net_margin"] == [0.1, 0.1, 0.1, 0.1]
+ assert result["ebitda_margin"] == [0.25, 0.25, 0.25, 0.25]
+ assert result["roe"] == [0.2, 0.2, 0.2, 0.2]
+ assert result["roa"] == [0.1, 0.1, 0.1, 0.1]
+ assert result["debt_to_equity"] == [0.4, 0.4, 0.4, 0.4]
+ assert result["current_ratio"] == [2.0, 2.0, 2.0, 2.0]
+ assert result["trailing_pe"] == [None, None, None, None]
+ assert result["ev_to_ebitda"] == [None, None, None, None]
+ assert result["price_to_book"] == [None, None, None, None]
+ assert result["price_to_sales"] == [None, None, None, None]
+
+
+def test_compute_historical_ratios_empty_income(monkeypatch) -> None:
+ clear_service_caches()
+ monkeypatch.setattr(data_service, "get_income_statement", lambda symbol, quarterly=False: pd.DataFrame())
+ monkeypatch.setattr(data_service, "get_balance_sheet", lambda symbol, quarterly=False: pd.DataFrame())
+ monkeypatch.setattr(data_service, "get_cash_flow", lambda symbol, quarterly=False: pd.DataFrame())
+ monkeypatch.setattr(data_service, "get_shares_outstanding", lambda symbol: None)
+ monkeypatch.setattr(data_service, "get_price_history", lambda symbol, period="5y": [])
+
+ result = data_service.compute_historical_ratios("AAPL")
+
+ assert result == {}
+
+
+def test_get_ratios_quick_ratio(monkeypatch) -> None:
+ clear_service_caches()
+ monkeypatch.setattr(data_service, "compute_ttm_ratios", lambda symbol: {"market_cap": 1_000.0, "current_ratio": 2.0})
+ monkeypatch.setattr(
+ data_service,
+ "compute_historical_ratios",
+ lambda symbol: {
+ "current_ratio": [1.4, 1.5, 1.6, 1.8],
+ },
+ )
+ monkeypatch.setattr(data_service, "get_company_info", lambda symbol: {})
+ monkeypatch.setattr(
+ data_service,
+ "get_income_statement",
+ lambda symbol, quarterly=True: quarterly_frame(
+ {
+ "Total Revenue": [100.0, 100.0, 100.0, 100.0],
+ }
+ ),
+ )
+ monkeypatch.setattr(
+ data_service,
+ "get_balance_sheet",
+ lambda symbol, quarterly=True: quarterly_frame(
+ {
+ "Current Assets": [200.0, 0.0, 0.0, 0.0],
+ "Current Liabilities": [100.0, 0.0, 0.0, 0.0],
+ "Inventory": [30.0, 0.0, 0.0, 0.0],
+ }
+ ),
+ )
+ monkeypatch.setattr(data_service, "get_cash_flow", lambda symbol, quarterly=True: quarterly_frame({"Operating Cash Flow": [1.0, 1.0, 1.0, 1.0]}))
+ monkeypatch.setattr(data_service, "get_price_history", lambda symbol, period="5y": [])
+
+ result = data_service.get_ratios("AAPL")
+
+ assert result["quick_ratio"]["value"] == 1.7
+ assert result["quick_ratio"]["spark"] == []
+ assert result["current_ratio"]["value"] == 2.0
+ assert result["current_ratio"]["spark"] == [1.4, 1.5, 1.6, 1.8]
+
+
+def test_get_ratios_interest_coverage(monkeypatch) -> None:
+ clear_service_caches()
+ monkeypatch.setattr(data_service, "compute_ttm_ratios", lambda symbol: {"market_cap": 1_000.0})
+ monkeypatch.setattr(data_service, "compute_historical_ratios", lambda symbol: {})
+ monkeypatch.setattr(data_service, "get_company_info", lambda symbol: {})
+ monkeypatch.setattr(
+ data_service,
+ "get_income_statement",
+ lambda symbol, quarterly=True: quarterly_frame(
+ {
+ "Total Revenue": [100.0, 100.0, 100.0, 100.0],
+ "EBIT": [42.5, 42.5, 42.5, 42.5],
+ "Interest Expense": [5.0, 5.0, 5.0, 5.0],
+ }
+ ),
+ )
+ monkeypatch.setattr(data_service, "get_balance_sheet", lambda symbol, quarterly=True: quarterly_frame({}))
+ monkeypatch.setattr(data_service, "get_cash_flow", lambda symbol, quarterly=True: quarterly_frame({"Operating Cash Flow": [1.0, 1.0, 1.0, 1.0]}))
+ monkeypatch.setattr(data_service, "get_price_history", lambda symbol, period="5y": [])
+
+ result = data_service.get_ratios("AAPL")
+
+ assert result["interest_coverage"]["value"] == 8.5
+ assert result["interest_coverage"]["spark"] == []