- pyproject.toml: setuptools build, deps (requests), dev extra (pytest), pytest config with opt-in `live` marker - tests/: 95 offline tests locking every series-ID encoding against known-good IDs (regression guard for the JOLTS/OES/ECI/productivity/QCEW fixes), plus a live API smoke test behind `-m live` - README: install-as-package + test instructions; drop the now-resolved "no packaging"/"no tests" limitations - gitignore build/test artifacts Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
85 lines
3.3 KiB
Python
85 lines
3.3 KiB
Python
"""Unit tests for the pre-built query helpers (offline).
|
|
|
|
Asserts the high-value helpers emit the exact known-good IDs, and that every
|
|
zero-argument helper across all modules emits a structurally valid series ID
|
|
(correct survey prefix + length). No network.
|
|
"""
|
|
import inspect
|
|
|
|
import pytest
|
|
|
|
from bls_client.queries import employment as E, prices as P, wages as W, productivity as PR
|
|
|
|
GOLDEN = [
|
|
(E.nonfarm_payrolls, "CES0000000001"),
|
|
(E.qcew_national_private, "ENUUS00010510"),
|
|
(E.qcew_dc_private, "ENU1100010510"),
|
|
(P.ppi_final_demand, "WPUFD4"),
|
|
(P.ppi_finished_goods, "WPUFD4"), # back-compat alias
|
|
(W.eci_wages, "CIU1020000000000A"),
|
|
(W.eci_total_compensation, "CIU1010000000000A"),
|
|
(W.ecec_total_benefits, "CMU1036000000000D"),
|
|
(PR.business_output_per_hour, "PRS84006092"),
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize("fn,expected", GOLDEN, ids=[f.__name__ for f, _ in GOLDEN])
|
|
def test_helper_exact_id(fn, expected):
|
|
assert fn() == expected
|
|
|
|
|
|
def test_software_developers_uses_2018_soc():
|
|
# 15-1252 (2018 SOC); the old 15-1132 was retired and returns no data.
|
|
assert W.SOC_CODES["software_developers"] == "151252"
|
|
assert "151252" in W.occupation_annual_median_wage("151252")
|
|
|
|
|
|
# --- Structural sweep: every zero-arg helper -> well-formed ID -----------------
|
|
|
|
# Surveys with a single fixed-width series ID — these are the ones whose
|
|
# encodings broke before, so we enforce exact length. Other valid surveys
|
|
# (CPI, PPI, average prices, import/export, CPS) have variable-width IDs and
|
|
# are checked only for a well-formed prefix.
|
|
FIXED_LEN = {
|
|
"LA": 20, "CE": 13, "SM": 20, "OE": 25, "JT": 21,
|
|
"CI": 17, "CM": 17, "PR": 11, "EN": 13,
|
|
}
|
|
KNOWN_PREFIXES = set(FIXED_LEN) | {"CU", "CW", "SU", "WP", "LN", "LE", "AP", "EI"}
|
|
|
|
|
|
def _zero_arg_helpers():
|
|
out = []
|
|
for mod in (E, P, W, PR):
|
|
for name, fn in inspect.getmembers(mod, inspect.isfunction):
|
|
if fn.__module__ != mod.__name__:
|
|
continue
|
|
sig = inspect.signature(fn)
|
|
if any(p.default is inspect._empty for p in sig.parameters.values()):
|
|
continue
|
|
out.append((f"{mod.__name__.split('.')[-1]}.{name}", fn))
|
|
return out
|
|
|
|
|
|
def _ids_from(fn):
|
|
out = fn()
|
|
if isinstance(out, str):
|
|
return [out]
|
|
if isinstance(out, dict):
|
|
return [v for v in out.values() if isinstance(v, str)]
|
|
return []
|
|
|
|
|
|
@pytest.mark.parametrize("label,fn", _zero_arg_helpers(), ids=lambda v: v if isinstance(v, str) else "")
|
|
def test_helper_ids_well_formed(label, fn):
|
|
ids = _ids_from(fn)
|
|
assert ids, f"{label} produced no series IDs"
|
|
for sid in ids:
|
|
prefix = sid[:2]
|
|
assert sid.isalnum(), f"{label}: non-alphanumeric series ID {sid!r}"
|
|
assert prefix.isupper() and prefix.isalpha(), f"{label}: bad prefix in {sid!r}"
|
|
assert prefix in KNOWN_PREFIXES, f"{label}: unknown survey prefix in {sid!r}"
|
|
if prefix in FIXED_LEN:
|
|
assert len(sid) == FIXED_LEN[prefix], (
|
|
f"{label}: {sid!r} is {len(sid)} chars, expected {FIXED_LEN[prefix]} for {prefix}"
|
|
)
|