| """Tests for dashboard/financials.py — fiscal calendar robustness. |
| |
| Each company has a different fiscal year end month: |
| AMD — December (fy_end_month=12) |
| MSFT — June (fy_end_month=6) |
| AAPL — September (fy_end_month=9) ← what the old hardcoded table assumed |
| NVDA — January (fy_end_month=1) |
| """ |
| from __future__ import annotations |
| from unittest.mock import patch |
|
|
| import pytest |
|
|
|
|
| |
| |
| |
|
|
| class TestQuarterLabelAMD: |
| """AMD fiscal year ends December.""" |
|
|
| def test_q1_fy24(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2024-03-31", fy_end_month=12) == "Q1 FY24 · MAR" |
|
|
| def test_q2_fy24(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2024-06-30", fy_end_month=12) == "Q2 FY24 · JUN" |
|
|
| def test_q3_fy24(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2024-09-30", fy_end_month=12) == "Q3 FY24 · SEP" |
|
|
| def test_q4_fy24(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2024-12-31", fy_end_month=12) == "Q4 FY24 · DEC" |
|
|
|
|
| class TestQuarterLabelMSFT: |
| """MSFT fiscal year ends June.""" |
|
|
| def test_q1_fy25(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2024-09-30", fy_end_month=6) == "Q1 FY25 · SEP" |
|
|
| def test_q2_fy25(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2024-12-31", fy_end_month=6) == "Q2 FY25 · DEC" |
|
|
| def test_q3_fy25(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2025-03-31", fy_end_month=6) == "Q3 FY25 · MAR" |
|
|
| def test_q4_fy25(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2025-06-30", fy_end_month=6) == "Q4 FY25 · JUN" |
|
|
|
|
| class TestQuarterLabelAAPL: |
| """AAPL fiscal year ends September — must match existing correct behavior.""" |
|
|
| def test_q1_fy25(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2024-12-31", fy_end_month=9) == "Q1 FY25 · DEC" |
|
|
| def test_q2_fy25(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2025-03-31", fy_end_month=9) == "Q2 FY25 · MAR" |
|
|
| def test_q3_fy25(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2025-06-30", fy_end_month=9) == "Q3 FY25 · JUN" |
|
|
| def test_q4_fy25(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2025-09-30", fy_end_month=9) == "Q4 FY25 · SEP" |
|
|
|
|
| class TestQuarterLabelNVDA: |
| """NVDA fiscal year ends January.""" |
|
|
| def test_q1_fy25(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2024-04-30", fy_end_month=1) == "Q1 FY25 · APR" |
|
|
| def test_q2_fy25(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2024-07-31", fy_end_month=1) == "Q2 FY25 · JUL" |
|
|
| def test_q3_fy25(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2024-10-31", fy_end_month=1) == "Q3 FY25 · OCT" |
|
|
| def test_q4_fy25(self): |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("2025-01-31", fy_end_month=1) == "Q4 FY25 · JAN" |
|
|
|
|
| class TestQuarterLabelEdgeCases: |
|
|
| def test_invalid_date_returns_raw_string(self): |
| """Bad date string must not crash — returns original string.""" |
| from dashboard.financials import _quarter_label |
| assert _quarter_label("not-a-date", fy_end_month=12) == "not-a-date" |
|
|
| def test_none_fy_end_month_returns_raw_date(self): |
| """When fy_end_month is None (unknown), return the date string unchanged.""" |
| from dashboard.financials import _quarter_label |
| result = _quarter_label("2024-06-30", fy_end_month=None) |
| assert result == "2024-06-30" |
|
|
|
|
| class TestFinancialDataQuality: |
|
|
| @staticmethod |
| def _row(period, filing_date, form_type, status="VERIFIED", warnings=None): |
| return { |
| "period": period, |
| "filing_date": filing_date, |
| "form_type": form_type, |
| "period_basis": "annual" if form_type == "10-K" else "quarter", |
| "revenue": 10_000_000_000.0, |
| "revenue_yoy_pct": 12.5, |
| "eps": 2.5, |
| "gross_margin": 0.6, |
| "operating_margin": 0.4, |
| "free_cash_flow": 3_000_000_000.0, |
| "data_quality_status": status, |
| "quality_warnings": warnings or [], |
| } |
|
|
| def test_display_rows_masks_check_required_fields_and_hides_legacy(self): |
| from dashboard.financials import _display_rows |
|
|
| rows = [ |
| self._row( |
| "Q22025", "2025-08-01", "10-Q", "CHECK_REQUIRED", |
| ["free_cash_flow:fallback_non_sec"], |
| ), |
| self._row("Q12025", "2025-05-01", "10-Q"), |
| self._row("FY2024", "2024-11-01", "10-K", "LEGACY_UNVERIFIED"), |
| ] |
|
|
| display_rows = _display_rows(rows) |
|
|
| assert [row["period"] for row in display_rows] == ["Q22025", "Q12025"] |
| partial = display_rows[0] |
| assert partial["free_cash_flow"] is None |
| assert partial["revenue"] == 10_000_000_000.0 |
| assert partial["eps"] == 2.5 |
| assert partial["_masked_fields"] == ["free_cash_flow"] |
|
|
| def test_display_row_masks_revenue_and_derived_yoy(self): |
| from dashboard.financials import _display_row |
|
|
| row = self._row( |
| "Q22025", "2025-08-01", "10-Q", "CHECK_REQUIRED", |
| ["revenue:fallback_non_sec"], |
| ) |
|
|
| displayed = _display_row(row) |
|
|
| assert displayed is not None |
| assert displayed["revenue"] is None |
| assert displayed["revenue_yoy_pct"] is None |
| assert displayed["eps"] == 2.5 |
| assert displayed["_masked_fields"] == ["revenue", "revenue_yoy_pct"] |
|
|
| def test_display_row_hides_legacy_unverified(self): |
| from dashboard.financials import _display_row |
|
|
| legacy = self._row( |
| "FY2024", "2024-11-01", "10-K", "LEGACY_UNVERIFIED" |
| ) |
|
|
| assert _display_row(legacy) is None |
|
|
| def test_comparable_prior_does_not_compare_annual_with_quarter(self): |
| from dashboard.financials import _comparable_prior |
|
|
| latest = self._row("FY2025", "2025-11-01", "10-K") |
| recent_quarter = self._row("Q32025", "2025-08-01", "10-Q") |
| prior_annual = self._row("FY2024", "2024-11-01", "10-K") |
|
|
| assert _comparable_prior( |
| [latest, recent_quarter, prior_annual], latest |
| )["period"] == "FY2024" |
|
|
| def test_comparable_prior_accepts_sanitized_check_required_lineage(self): |
| from dashboard.financials import _comparable_prior, _display_rows |
|
|
| latest = self._row("Q22025", "2025-08-01", "10-Q") |
| partial_prior = self._row( |
| "Q12025", "2025-05-01", "10-Q", "CHECK_REQUIRED", |
| ["free_cash_flow:fallback_non_sec"], |
| ) |
| rows = _display_rows([latest, partial_prior]) |
|
|
| prior = _comparable_prior(rows, rows[0]) |
|
|
| assert prior["period"] == "Q12025" |
| assert prior["free_cash_flow"] is None |
| assert prior["revenue"] == 10_000_000_000.0 |
|
|
| def test_comparable_prior_requires_exact_adjacent_fiscal_period(self): |
| from dashboard.financials import _comparable_prior |
|
|
| latest = self._row("Q32025", "2025-11-01", "10-Q") |
| non_adjacent = self._row("Q12025", "2025-05-01", "10-Q") |
|
|
| assert _comparable_prior([latest, non_adjacent], latest) == {} |
|
|
| def test_masked_message_only_receives_fully_hidden_rows(self): |
| from dashboard.financials import _display_row, _masked_quality_message |
|
|
| partial = self._row( |
| "Q22025", "2025-08-01", "10-Q", "CHECK_REQUIRED", |
| ["revenue:duration_mismatch"], |
| ) |
| legacy = self._row( |
| "FY2024", "2024-11-01", "10-K", "LEGACY_UNVERIFIED" |
| ) |
| masked_rows = [row for row in [partial, legacy] if _display_row(row) is None] |
|
|
| message = _masked_quality_message(masked_rows) |
|
|
| assert "FY2024: LEGACY_UNVERIFIED" in message |
| assert "Q22025" not in message |
| assert "revenue:duration_mismatch" not in message |
| assert "no citable SEC lineage" in message |
| assert "not VERIFIED" not in message |
|
|
| @patch("dashboard.financials.st.plotly_chart") |
| def test_chart_margins_preserves_masked_gross_margin_as_none( |
| self, mock_plotly_chart |
| ): |
| from dashboard.financials import _chart_margins, _display_row |
|
|
| row = self._row( |
| "Q22025", "2025-08-01", "10-Q", "CHECK_REQUIRED", |
| ["gross_margin:fallback_non_sec"], |
| ) |
| displayed = _display_row(row) |
|
|
| assert displayed is not None |
| assert displayed["gross_margin"] is None |
| assert displayed["revenue"] == 10_000_000_000.0 |
| assert displayed["eps"] == 2.5 |
|
|
| _chart_margins(["Q2 FY25"], [displayed]) |
|
|
| margins_figure = mock_plotly_chart.call_args_list[0].args[0] |
| assert list(margins_figure.data[0].y) == [None] |
|
|
| @patch("dashboard.financials.st.plotly_chart") |
| def test_chart_cash_preserves_masked_free_cash_flow_as_none( |
| self, mock_plotly_chart |
| ): |
| from dashboard.financials import _chart_cash, _display_row |
|
|
| row = self._row( |
| "Q22025", "2025-08-01", "10-Q", "CHECK_REQUIRED", |
| ["free_cash_flow:fallback_non_sec"], |
| ) |
| displayed = _display_row(row) |
|
|
| assert displayed is not None |
| assert displayed["free_cash_flow"] is None |
| assert displayed["revenue"] == 10_000_000_000.0 |
| assert displayed["eps"] == 2.5 |
|
|
| _chart_cash(["Q2 FY25"], [displayed]) |
|
|
| cash_figure = mock_plotly_chart.call_args_list[0].args[0] |
| assert list(cash_figure.data[0].y) == [None] |
|
|