1from __future__ import annotations23import json4from datetime import UTC, date, datetime5from pathlib import Path67from findata_fundamentals import SecEdgarConnector, available_at_policy, facts_from_companyfacts, facts_from_financials89FIX = Path(__file__).parent / "fixtures" / "fundamentals"10NOW = datetime(2026, 11, 1, 12, 0, tzinfo=UTC)111213def test_available_at_policy_is_next_day_midnight_et():14 # filing 2026-07-31 → available 2026-08-01 00:00 EDT = 2026-08-01 04:00 UTC15 assert available_at_policy(date(2026, 7, 31), date(2026, 6, 27)) == datetime(2026, 8, 1, 4, 0, tzinfo=UTC)16 # winter: 2026-01-30 → 2026-01-31 00:00 EST = 05:00 UTC17 assert available_at_policy(date(2026, 1, 30), date(2025, 12, 27)) == datetime(2026, 1, 31, 5, 0, tzinfo=UTC)18 # missing filing date → period_end + 90 d + 119 assert available_at_policy(None, date(2026, 6, 30)).date() == date(2026, 9, 29)202122def test_facts_from_financials_long_format():23 payload = json.loads((FIX / "financials_all_AAPL_quarterly.json").read_text())24 facts = list(facts_from_financials(payload, NOW))25 assert len(facts) > 12 * 5026 latest = [f for f in facts if f["fiscal_period_end"] == date(2026, 6, 27)]27 rev = next(f for f in latest if f["metric"] == "rev")28 assert rev["value"] == 109417000000.0 and rev["unit"] == "USD" and rev["fiscal_period_type"] == "q"29 assert rev["filing_date"] == date(2026, 7, 31) and rev["available_at"] == datetime(2026, 8, 1, 4, 0, tzinfo=UTC)30 assert rev["entity_id"] == "frd:AAPL" and rev["source_id"] == "firstrate_fundamentals" and rev["pit_fallback"] is False31 assert all(f["available_at"] > datetime.combine(f["fiscal_period_end"], datetime.min.time(), tzinfo=UTC) for f in facts)323334def test_sec_edgar_pending_without_user_agent():35 c = SecEdgarConnector(user_agent="")36 assert c.status()["status"] == "pending_configuration"37 try:38 c.companyfacts(320193)39 raise AssertionError("must refuse without user agent")40 except RuntimeError as e:41 assert "pending configuration" in str(e)424344def test_companyfacts_parsing_keeps_each_filing_as_revision():45 payload = {"cik": 320193, "entityName": "Apple Inc.", "facts": {"us-gaap": {"Revenues": {"units": {"USD": [46 {"start": "2024-09-29", "end": "2024-12-28", "val": 124300000000, "accn": "0000320193-25-000008", "fy": 2025, "fp": "Q1", "form": "10-Q", "filed": "2025-01-31", "frame": "CY2024Q4"},47 {"start": "2024-09-29", "end": "2024-12-28", "val": 124300000001, "accn": "0000320193-25-000999", "fy": 2025, "fp": "Q2", "form": "10-Q", "filed": "2025-05-02"},48 {"start": "2023-10-01", "end": "2024-09-28", "val": 391035000000, "accn": "0000320193-24-000123", "fy": 2024, "fp": "FY", "form": "10-K", "filed": "2024-11-01"},49 ]}}}}}50 facts = list(facts_from_companyfacts(payload, NOW))51 assert len(facts) == 352 q = [f for f in facts if f["fiscal_period_end"] == date(2024, 12, 28)]53 assert sorted(f["filing_date"] for f in q) == [date(2025, 1, 31), date(2025, 5, 2)]54 assert q[0]["available_at"] == datetime(2025, 2, 1, 5, 0, tzinfo=UTC)55 fy = next(f for f in facts if f["fiscal_period_type"] == "a")56 assert fy["metric"] == "rev" and fy["entity_id"] == "sec:CIK0000320193"57