1from __future__ import annotations23import json4from datetime import UTC, date, datetime5from pathlib import Path67from findata_fundamentals import (8 SecEdgarConnector,9 available_at_policy,10 facts_from_companyfacts,11 facts_from_financials,12)1314FIX = Path(__file__).parent / "fixtures" / "fundamentals"15NOW = datetime(2026, 11, 1, 12, 0, tzinfo=UTC)161718def test_available_at_policy_is_next_day_midnight_et():19 # filing 2026-07-31 → available 2026-08-01 00:00 EDT = 2026-08-01 04:00 UTC20 assert available_at_policy(date(2026, 7, 31), date(2026, 6, 27)) == datetime(2026, 8, 1, 4, 0, tzinfo=UTC)21 # winter: 2026-01-30 → 2026-01-31 00:00 EST = 05:00 UTC22 assert available_at_policy(date(2026, 1, 30), date(2025, 12, 27)) == datetime(2026, 1, 31, 5, 0, tzinfo=UTC)23 # missing filing date → period_end + 90 d + 124 assert available_at_policy(None, date(2026, 6, 30)).date() == date(2026, 9, 29)252627def test_facts_from_financials_long_format():28 payload = json.loads((FIX / "financials_all_AAPL_quarterly.json").read_text())29 facts = list(facts_from_financials(payload, NOW))30 assert len(facts) > 12 * 5031 latest = [f for f in facts if f["fiscal_period_end"] == date(2026, 6, 27)]32 rev = next(f for f in latest if f["metric"] == "rev")33 assert rev["value"] == 109417000000.0 and rev["unit"] == "USD" and rev["fiscal_period_type"] == "q"34 assert rev["filing_date"] == date(2026, 7, 31) and rev["available_at"] == datetime(2026, 8, 1, 4, 0, tzinfo=UTC)35 assert rev["entity_id"] == "frd:AAPL" and rev["source_id"] == "firstrate_fundamentals" and rev["pit_fallback"] is False36 assert all(f["available_at"] > datetime.combine(f["fiscal_period_end"], datetime.min.time(), tzinfo=UTC) for f in facts)373839def test_sec_edgar_pending_without_user_agent():40 c = SecEdgarConnector(user_agent="")41 assert c.status()["status"] == "pending_configuration"42 try:43 c.companyfacts(320193)44 raise AssertionError("must refuse without user agent")45 except RuntimeError as e:46 assert "pending configuration" in str(e)474849def test_companyfacts_parsing_keeps_each_filing_as_revision():50 payload = {"cik": 320193, "entityName": "Apple Inc.", "facts": {"us-gaap": {"Revenues": {"units": {"USD": [51 {"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"},52 {"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"},53 {"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"},54 ]}}}}}55 facts = list(facts_from_companyfacts(payload, NOW))56 assert len(facts) == 357 q = [f for f in facts if f["fiscal_period_end"] == date(2024, 12, 28)]58 assert sorted(f["filing_date"] for f in q) == [date(2025, 1, 31), date(2025, 5, 2)]59 assert q[0]["available_at"] == datetime(2025, 2, 1, 5, 0, tzinfo=UTC)60 fy = next(f for f in facts if f["fiscal_period_type"] == "a")61 assert fy["metric"] == "rev" and fy["entity_id"] == "sec:CIK0000320193"62