TerraFin / tests /data /test_cusip_resolver.py
sk851's picture
refactor: Phase 6 data layer + portfolio + UI fixes + breadth label
085d910
"""Coverage for CUSIP -> ticker resolution + caching.
We don't hit the real OpenFIGI API in tests; the network call is monkeypatched
and only the response shape, parsing, ticker selection, and cache behavior
are verified.
"""
import json
import pytest
import requests
from TerraFin.data.providers.corporate import cusip_resolver
class _StubResponse:
def __init__(self, payload, status_code: int = 200) -> None:
self._payload = payload
self.status_code = status_code
def json(self):
if self._payload is _MalformedJsonSentinel:
raise ValueError("not json")
return self._payload
_MalformedJsonSentinel = object()
@pytest.fixture(autouse=True)
def _reset_cache(monkeypatch, tmp_path):
"""Redirect file cache to a temp dir so each test starts clean."""
from TerraFin.data.cache import manager as cache_module
monkeypatch.setattr(cache_module, "_FILE_CACHE_DIR", tmp_path)
yield
def test_resolve_cusip_returns_ticker_from_us_listing(monkeypatch) -> None:
captured = {}
def fake_post(url, json=None, timeout=None):
captured["body"] = json
return _StubResponse(
[
{
"data": [
{"ticker": "AAPL", "exchCode": "US"},
{"ticker": "AAPL.MX", "exchCode": "MM"},
]
}
]
)
monkeypatch.setattr(cusip_resolver.requests, "post", fake_post)
assert cusip_resolver.resolve_cusip_to_ticker("037833100") == "AAPL"
assert captured["body"] == [{"idType": "ID_CUSIP", "idValue": "037833100"}]
def test_resolve_cusip_caches_misses(monkeypatch) -> None:
calls = {"n": 0}
def fake_post(url, json=None, timeout=None):
calls["n"] += 1
return _StubResponse([{"warning": "No identifier found."}])
monkeypatch.setattr(cusip_resolver.requests, "post", fake_post)
assert cusip_resolver.resolve_cusip_to_ticker("999999999") is None
# Second call hits the on-disk cache, not OpenFIGI.
assert cusip_resolver.resolve_cusip_to_ticker("999999999") is None
assert calls["n"] == 1
def test_resolve_cusip_rejects_malformed_input(monkeypatch) -> None:
monkeypatch.setattr(
cusip_resolver.requests,
"post",
lambda *a, **k: pytest.fail("network must not be called for invalid CUSIP"),
)
assert cusip_resolver.resolve_cusip_to_ticker("not-a-cusip") is None
assert cusip_resolver.resolve_cusip_to_ticker("") is None
def test_resolve_cusip_swallows_request_exceptions(monkeypatch) -> None:
def boom(*a, **k):
raise requests.RequestException("network down")
monkeypatch.setattr(cusip_resolver.requests, "post", boom)
assert cusip_resolver.resolve_cusip_to_ticker("037833100") is None