from __future__ import annotations import pytest import libs.intraday.catalyst as catalyst_mod from libs.intraday.catalyst import ( AttentionEventCache, FilingEventCache, PriorEventFeatureSnapshotCache, fetch_attention_features_bulk, fetch_filing_event_features_bulk, ) from libs.oracle_client.models import ( EntityInfo, EventAttentionResponse, FilingEventEntry, FilingEventsResponse, NewsFeatures, WikiFeatures, ) def test_filing_event_cache_roundtrip(tmp_path) -> None: cache = FilingEventCache(str(tmp_path)) cache.put( "ABC", "2026-01-01", "2026-03-31", [ { "ticker": "ABC", "filing_date": "2026-02-10", "accession_number": "1", "event_type": "other_material_event", "item_number": "8.01", "form_type": "8-K", "title": "Material event", } ], ) hit = cache.get("ABC", "2026-01-02", "2026-03-01") miss = cache.get("ABC", "2025-12-31", "2026-03-01") assert hit is not None and hit[0]["event_type"] == "other_material_event" assert miss is None @pytest.mark.asyncio async def test_fetch_filing_event_features_bulk_uses_cache_after_first_hit(tmp_path, monkeypatch) -> None: cache = FilingEventCache(str(tmp_path)) calls = {"count": 0} async def fake_get_events(self, ticker, start_date=None, end_date=None): calls["count"] += 1 return FilingEventsResponse( ticker=ticker, events=[ FilingEventEntry( id="evt-1", ticker=ticker, accession_number="acc-1", form_type="8-K", filing_date="2026-02-10", item_number="8.01", event_type="other_material_event", title="Material event", ) ], total_count=1, ) monkeypatch.setattr(catalyst_mod.FilingsService, "get_filing_events", fake_get_events) first = await fetch_filing_event_features_bulk( ["ABC"], "2026-01-01", "2026-03-31", client=object(), # patched method ignores the client cache=cache, concurrency=1, ) second = await fetch_filing_event_features_bulk( ["ABC"], "2026-01-01", "2026-03-31", client=object(), cache=cache, concurrency=1, ) assert first["ABC"]["2026-02-10"]["event_flag"] is True assert first["ABC"]["2026-02-10"]["event_score"] >= 1.0 assert second == first assert calls["count"] == 1 def test_attention_event_cache_roundtrip(tmp_path) -> None: cache = AttentionEventCache(str(tmp_path)) cache.put( "ABC", "2026-02-10", { "attention_wiki_spike_10d": 2.3, "attention_wiki_zscore_20d": 3.1, "attention_article_count_3d": 5, "attention_us_article_count_3d": 3, "attention_resolver_confidence": 0.95, }, ) hit = cache.get("ABC", "2026-02-10") miss = cache.get("ABC", "2026-02-11") assert hit is not None and hit["attention_article_count_3d"] == 5 assert miss is None def test_prior_event_feature_snapshot_cache_roundtrip_normalizes_request(tmp_path) -> None: cache = PriorEventFeatureSnapshotCache(str(tmp_path), snapshot_id="V46 Research 2026-04-23") features = { "ABC": { "2026-02-10": { "event_flag": True, "event_score": 1.0, } } } cache.put( ["abc", "ABC"], ["2026-02-10", "2026-02-11"], 10, ("guidance_update", "earnings_release"), features, ) hit = cache.get( ["ABC"], ["2026-02-10", "2026-02-11"], 10, ("earnings_release", "guidance_update"), ) miss = cache.get( ["ABC"], ["2026-02-10", "2026-02-11"], 7, ("earnings_release", "guidance_update"), ) assert hit == features assert miss is None assert cache.snapshot_id == "v46-research-2026-04-23" manifest = tmp_path / "v46-research-2026-04-23" / "manifest.json" assert manifest.exists() assert '"snapshot_id": "v46-research-2026-04-23"' in manifest.read_text() @pytest.mark.asyncio async def test_fetch_attention_features_bulk_uses_cache_after_first_hit(tmp_path, monkeypatch) -> None: cache = AttentionEventCache(str(tmp_path)) calls = {"count": 0} async def fake_get_attention(self, ticker, event_date): calls["count"] += 1 return EventAttentionResponse( ticker=ticker, event_date=event_date, entity=EntityInfo( ticker=ticker, canonical_name=ticker, resolver_confidence=0.91, ), wiki=WikiFeatures(spike_10d=2.8, zscore_20d=3.4), news=NewsFeatures(article_count_3d=6, us_article_count_3d=4), metadata={}, ) monkeypatch.setattr(catalyst_mod.AttentionService, "get_event_attention", fake_get_attention) first = await fetch_attention_features_bulk( [("ABC", "2026-02-10")], client=object(), cache=cache, concurrency=1, ) second = await fetch_attention_features_bulk( [("ABC", "2026-02-10")], client=object(), cache=cache, concurrency=1, ) assert first["ABC"]["2026-02-10"]["attention_wiki_spike_10d"] == 2.8 assert first["ABC"]["2026-02-10"]["attention_us_article_count_3d"] == 4 assert second == first assert calls["count"] == 1