3946 new tests covering: - cms.table: all 150 SQLTable models (schema, tablename, qualified_name, column_names, nullable fields, construction, uniqueness) - cms.log: JsonlHandler, setup() idempotency, custom attrs, exceptions - ccw.table: all 41 claim tables across 7 types (IP, SNF, Hospice, HHA, HOP, Carrier, DME) with structural validation - ccw.docs: all 308 variable documentation modules (importability, required attrs, type validation)
110 lines
3.8 KiB
Python
110 lines
3.8 KiB
Python
"""Tests for CCW FFS Claims Codebook variable documentation.
|
|
|
|
Validates that all 308 auto-generated variable modules have
|
|
consistent structure and required attributes.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
|
|
import pytest
|
|
|
|
from ccw.docs import VARIABLES
|
|
|
|
# ── VARIABLES registry ───────────────────────────────────────────
|
|
|
|
|
|
class TestVariablesRegistry:
|
|
def test_variables_is_list(self) -> None:
|
|
assert isinstance(VARIABLES, list)
|
|
|
|
def test_variables_count(self) -> None:
|
|
assert len(VARIABLES) == 308
|
|
|
|
def test_at_most_one_duplicate(self) -> None:
|
|
# rndrng_physn_upin appears twice in the PDF codebook
|
|
unique = set(VARIABLES)
|
|
assert len(VARIABLES) - len(unique) <= 1
|
|
|
|
def test_all_lowercase(self) -> None:
|
|
for v in VARIABLES:
|
|
assert v == v.lower(), f"{v} is not lowercase"
|
|
|
|
|
|
# ── Module importability ─────────────────────────────────────────
|
|
|
|
|
|
class TestImportability:
|
|
@pytest.fixture(params=VARIABLES)
|
|
def var_module(self, request):
|
|
return importlib.import_module(f"ccw.docs.{request.param}")
|
|
|
|
def test_importable(self, var_module) -> None:
|
|
assert var_module is not None
|
|
|
|
|
|
# ── Required attributes ──────────────────────────────────────────
|
|
|
|
|
|
REQUIRED_ATTRS = ["name", "label", "type", "length", "source"]
|
|
VALID_TYPES = {"CHAR", "NUM", "DATE"}
|
|
|
|
|
|
class TestAttributes:
|
|
@pytest.fixture(params=VARIABLES)
|
|
def var_module(self, request):
|
|
return importlib.import_module(f"ccw.docs.{request.param}")
|
|
|
|
@pytest.mark.parametrize("attr", REQUIRED_ATTRS)
|
|
def test_has_required_attr(self, var_module, attr) -> None:
|
|
assert hasattr(var_module, attr), f"{var_module.__name__} missing '{attr}'"
|
|
|
|
def test_name_starts_uppercase(self, var_module) -> None:
|
|
# Most names are fully uppercase; a few have mixed-case
|
|
# parenthetical qualifiers (e.g. "PRVDR_NUM (Institutional claim)")
|
|
base = var_module.name.split("(")[0].strip()
|
|
assert base == base.upper()
|
|
|
|
def test_type_is_valid(self, var_module) -> None:
|
|
assert var_module.type in VALID_TYPES, (
|
|
f"{var_module.__name__}: type={var_module.type!r} not in {VALID_TYPES}"
|
|
)
|
|
|
|
def test_label_non_empty(self, var_module) -> None:
|
|
assert len(var_module.label) > 0
|
|
|
|
def test_length_is_numeric_string(self, var_module) -> None:
|
|
assert var_module.length.isdigit(), (
|
|
f"{var_module.__name__}: length={var_module.length!r} not numeric"
|
|
)
|
|
|
|
|
|
# ── Spot checks ──────────────────────────────────────────────────
|
|
|
|
|
|
class TestSpotChecks:
|
|
def test_bene_id(self) -> None:
|
|
mod = importlib.import_module("ccw.docs.bene_id")
|
|
assert mod.name == "BENE_ID"
|
|
assert mod.label == "Encrypted CCW Beneficiary ID"
|
|
assert mod.type == "CHAR"
|
|
assert mod.length == "15"
|
|
assert mod.source == "CCW"
|
|
|
|
def test_numeric_variable(self) -> None:
|
|
mod = importlib.import_module("ccw.docs.nch_prmry_pyr_clm_pd_amt")
|
|
assert mod.name == "NCH_PRMRY_PYR_CLM_PD_AMT"
|
|
assert mod.type == "NUM"
|
|
assert mod.short_name == "PRPAYAMT"
|
|
|
|
def test_has_short_and_long_name(self) -> None:
|
|
mod = importlib.import_module("ccw.docs.bene_id")
|
|
assert hasattr(mod, "short_name")
|
|
assert hasattr(mod, "long_name")
|
|
|
|
def test_docstring_present(self) -> None:
|
|
mod = importlib.import_module("ccw.docs.bene_id")
|
|
assert mod.__doc__ is not None
|
|
assert "BENE_ID" in mod.__doc__
|