from __future__ import annotations import pytest import libs.intraday.catalyst as catalyst_mod from libs.intraday.catalyst import ( AttentionEventCache, FilingEventCache, 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 @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