"""Live smoke test — hits the real BLS API. Opt-in only: deselected by default (see addopts in pyproject.toml). Run with: pytest -m live Requires network and a BLS API key (BLS_API_KEY env var, or config.py). Guards against silent series-ID drift: BLS occasionally retires codes (e.g. the 2010->2018 SOC change), which unit tests alone can't catch. """ import os import pytest from bls_client import BLSClient from bls_client.queries import employment as E, prices as P, wages as W, productivity as PR def _api_key(): key = os.environ.get("BLS_API_KEY") if key: return key try: from config import BLS_API_KEY # local, gitignored if BLS_API_KEY and BLS_API_KEY != "YOUR_KEY_HERE": return BLS_API_KEY except Exception: pass return None pytestmark = pytest.mark.live @pytest.fixture(scope="module") def client(): key = _api_key() if not key: pytest.skip("no BLS_API_KEY available") return BLSClient(key) # One representative, previously-broken helper per repaired survey. REPAIRED = { "JOLTS": E.jolts_dashboard(), # dict of 5 "OES wage": W.occupation_annual_median_wage("151252"), "OES employ": W.all_occupations_employment(), "ECI": W.eci_total_compensation(), "Productivity": PR.business_output_per_hour(), "QCEW": E.qcew_national_private(), "PPI final": P.ppi_final_demand(), "ECEC health": W.ecec_health_insurance(), "ECEC retire": W.ecec_retirement(), } def _flatten(v): return list(v.values()) if isinstance(v, dict) else [v] def test_repaired_surveys_return_data(client): """Every repaired survey must return at least one real observation.""" all_ids = [] for ids in REPAIRED.values(): all_ids.extend(_flatten(ids)) res = client.fetch(all_ids, 2024, 2025) missing = [sid for sid in all_ids if not res.get(sid, {}).get("data")] assert not missing, f"no data returned for: {missing}" def test_headline_payrolls_plausible(client): res = client.fetch_latest(E.nonfarm_payrolls(), years=1) obs = client.latest_obs(next(iter(res.values()))) assert obs is not None # US nonfarm payrolls are ~150,000 (thousands); sanity bound, not exact. assert 100_000 < float(obs["value"]) < 200_000