mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-02 23:41:27 +00:00
* refactor bls for v5, add 100 widgets * some CI fixes * no python 3.15 * fix missing dev group in pyproject.toml * more pyproject fallout * action
146 lines
4.8 KiB
Python
146 lines
4.8 KiB
Python
"""Tests for the ``BlsMetadata`` singleton."""
|
|
|
|
import copy
|
|
|
|
import pytest
|
|
|
|
from openbb_bls.utils.metadata import BlsMetadata, BlsMetadataDependency
|
|
|
|
|
|
def test_singleton_identity(stub_cache_path):
|
|
"""``__new__`` returns the same instance on subsequent calls."""
|
|
a = BlsMetadata()
|
|
b = BlsMetadata()
|
|
assert a is b
|
|
|
|
|
|
def test_deepcopy_returns_self(stub_cache_path):
|
|
"""``__deepcopy__`` returns ``self`` to short-circuit deepcopy walkers."""
|
|
meta = BlsMetadata()
|
|
assert copy.deepcopy(meta) is meta
|
|
|
|
|
|
def test_copy_returns_self(stub_cache_path):
|
|
"""``__copy__`` returns ``self`` so ``copy.copy`` also stops."""
|
|
meta = BlsMetadata()
|
|
assert copy.copy(meta) is meta
|
|
|
|
|
|
def test_call_returns_self(stub_cache_path):
|
|
"""The singleton is callable so it can stand in as a FastAPI dependency."""
|
|
meta = BlsMetadata()
|
|
assert meta() is meta
|
|
|
|
|
|
def test_reset_clears_state(stub_cache_path):
|
|
"""``_reset`` zeroes both the instance and the initialised flag."""
|
|
BlsMetadata()
|
|
assert BlsMetadata._instance is not None
|
|
assert BlsMetadata._initialized is True
|
|
BlsMetadata._reset()
|
|
assert BlsMetadata._instance is None
|
|
assert BlsMetadata._initialized is False
|
|
|
|
|
|
def test_categories_populated_from_index(stub_cache_path):
|
|
"""``categories`` reflects the keys parsed from ``index.json``."""
|
|
meta = BlsMetadata()
|
|
assert meta.categories == ["cpi", "ppi"]
|
|
assert meta.category_info("cpi") == {
|
|
"name": "Consumer Price Index",
|
|
"surveys": ["cu"],
|
|
"series_count": 2,
|
|
}
|
|
assert meta.category_info("missing") == {}
|
|
|
|
|
|
def test_get_series_lazy_and_memoised(stub_cache_path):
|
|
"""``get_series`` reads the CSV once and caches the result."""
|
|
meta = BlsMetadata()
|
|
df1 = meta.get_series("cpi")
|
|
df2 = meta.get_series("cpi")
|
|
assert df1 is df2
|
|
assert list(df1["series_id"]) == ["CUUR0000SA0", "CUUR0000SAF1"]
|
|
|
|
|
|
def test_get_codes_lazy_and_memoised(stub_cache_path):
|
|
"""``get_codes`` reads JSON once and caches the result."""
|
|
meta = BlsMetadata()
|
|
a = meta.get_codes("cpi")
|
|
b = meta.get_codes("cpi")
|
|
assert a is b
|
|
assert a["cu"]["area_code"]["0000"] == "U.S. city average"
|
|
|
|
|
|
def test_get_codes_missing_member_returns_empty(stub_cache_path):
|
|
"""``get_codes`` for a category lacking ``codes.json`` returns ``{}``."""
|
|
meta = BlsMetadata()
|
|
assert meta.get_codes("ppi") == {}
|
|
|
|
|
|
def test_unknown_category_raises_keyerror(stub_cache_path):
|
|
"""``get_series`` and ``get_codes`` raise ``KeyError`` for unknown keys."""
|
|
meta = BlsMetadata()
|
|
with pytest.raises(KeyError, match="Unknown BLS category"):
|
|
meta.get_series("not-a-real-category")
|
|
with pytest.raises(KeyError, match="Unknown BLS category"):
|
|
meta.get_codes("not-a-real-category")
|
|
|
|
|
|
def test_missing_archive_skips_load(tmp_path, monkeypatch):
|
|
"""Absent archive leaves the singleton with empty state."""
|
|
missing = tmp_path / "nope.zip"
|
|
monkeypatch.setattr(
|
|
"openbb_bls.utils.metadata._core._SHIPPED_CACHE_FILE",
|
|
missing,
|
|
)
|
|
meta = BlsMetadata()
|
|
assert meta.categories == []
|
|
assert meta._archive_bytes is None
|
|
|
|
|
|
def test_open_without_archive_raises(tmp_path, monkeypatch):
|
|
"""``_open`` raises ``FileNotFoundError`` when there is no archive in memory."""
|
|
missing = tmp_path / "nope.zip"
|
|
monkeypatch.setattr(
|
|
"openbb_bls.utils.metadata._core._SHIPPED_CACHE_FILE",
|
|
missing,
|
|
)
|
|
meta = BlsMetadata()
|
|
# Force the path through ``get_series`` -> ``_open`` after seeding the
|
|
# index manually so the category lookup passes the gate.
|
|
meta._index = {"cpi": {"surveys": ["cu"]}}
|
|
with pytest.raises(FileNotFoundError, match="BLS cache archive not found"):
|
|
meta.get_series("cpi")
|
|
|
|
|
|
def test_archive_without_index_json(tmp_path, make_stub_zip, monkeypatch):
|
|
"""An archive lacking ``index.json`` yields an empty category list."""
|
|
archive = tmp_path / "no_index.zip"
|
|
payload = make_stub_zip(include_index=False)
|
|
archive.write_bytes(payload)
|
|
monkeypatch.setattr(
|
|
"openbb_bls.utils.metadata._core._SHIPPED_CACHE_FILE",
|
|
archive,
|
|
)
|
|
meta = BlsMetadata()
|
|
assert meta.categories == []
|
|
assert meta._archive_bytes is not None
|
|
|
|
|
|
def test_dependency_wrapper_resolves_to_class(stub_cache_path):
|
|
"""``BlsMetadataDependency`` is an Annotated alias on ``BlsMetadata``."""
|
|
args = getattr(BlsMetadataDependency, "__metadata__", ())
|
|
assert args, "Annotated metadata should not be empty"
|
|
origin = getattr(BlsMetadataDependency, "__origin__", None)
|
|
assert origin is BlsMetadata
|
|
|
|
|
|
def test_init_no_op_when_already_initialised(stub_cache_path):
|
|
"""Calling ``__init__`` again is a no-op once the singleton is ready."""
|
|
meta = BlsMetadata()
|
|
sentinel = object()
|
|
meta._index = sentinel # type: ignore[assignment]
|
|
BlsMetadata().__init__()
|
|
assert meta._index is sentinel
|