test(opps): lock down OPPS conversion factors with two guards
Some checks failed
CI / lint (push) Successful in 38s
Deploy / notebooks (push) Has been skipped
Deploy / zotero (push) Has been skipped
Deploy / docs (push) Has been skipped
Deploy / api (push) Has been skipped
Deploy / mc (push) Has been skipped
Deploy / report (push) Successful in 17s
CI / test (push) Failing after 13m16s
Some checks failed
CI / lint (push) Successful in 38s
Deploy / notebooks (push) Has been skipped
Deploy / zotero (push) Has been skipped
Deploy / docs (push) Has been skipped
Deploy / api (push) Has been skipped
Deploy / mc (push) Has been skipped
Deploy / report (push) Successful in 17s
CI / test (push) Failing after 13m16s
Make the CY2014-CY2026 conversion factors regression-proof: - TestGoldenConversionFactors pins every year's CF to its Federal- Register / Addendum-B-verified value (runs in CI, no data needed), so any accidental edit fails and forces a cited, deliberate change. - TestAddendumBSelfConsistency (skipped when the aco DuckDB is absent) reconciles OppsPricer against the loaded Addendum B and asserts every APC row matches weight × CF to the cent — the independent proof the golden values are correct, not merely unchanged. Verified: all six loaded years (2021-2026) reconcile 100% exact.
This commit is contained in:
113
tests/opps/test_rules.py
Normal file
113
tests/opps/test_rules.py
Normal file
@@ -0,0 +1,113 @@
|
||||
"""Golden + self-consistency guards for opps.rules conversion factors.
|
||||
|
||||
The OPPS conversion factor per year is correctness-critical reference
|
||||
data: OppsPricer computes national APC rates as ``weight × CF`` and
|
||||
reconciles them against CMS's published Addendum B. Two guards keep it
|
||||
bulletproof:
|
||||
|
||||
1. ``TestGoldenConversionFactors`` pins every year's CF to the value
|
||||
taken from that year's Federal Register OPPS Final Rule (CY2014-
|
||||
CY2020) and cross-checked against the empirical
|
||||
``payment_rate / relative_weight`` ratio in Addendum B (CY2021-
|
||||
CY2026). Runs everywhere (no data needed), so any accidental edit
|
||||
fails CI. Changing a value requires updating the citation below too.
|
||||
|
||||
2. ``TestAddendumBSelfConsistency`` (skipped when the aco DuckDB is
|
||||
absent, e.g. headless CI) proves the CFs actually reproduce CMS's
|
||||
published Addendum B rate to the cent — the independent check that
|
||||
the golden values above are *correct*, not merely *unchanged*.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from conf import path
|
||||
from opps.rules import RULES
|
||||
|
||||
# Full OPPS conversion factor — the rate for hospitals that MEET quality
|
||||
# reporting (not the reduced/OQR-penalty CF).
|
||||
#
|
||||
# Sources:
|
||||
# CY2014-CY2020 — each year's Federal Register OPPS/ASC Final Rule
|
||||
# preamble ("we are using a conversion factor of $X").
|
||||
# CY2021-CY2026 — empirical payment_rate / relative_weight in
|
||||
# opps.addendum_b (5,000+ APC-priced rows/year), which
|
||||
# reproduces the published national rate to the cent.
|
||||
# CY2020 = 80.793 is the corrected value; the final rule as published
|
||||
# stated 80.784 and CMS later corrected it (the CY2021 rule
|
||||
# builds off 80.793).
|
||||
# CY2016 is genuinely below CY2015 — a one-time -2.0pp packaged-lab
|
||||
# ("two-times") adjustment more than offset the market basket.
|
||||
_EXPECTED_CF: dict[int, float] = {
|
||||
2014: 72.672,
|
||||
2015: 74.144,
|
||||
2016: 73.725,
|
||||
2017: 75.001,
|
||||
2018: 78.636,
|
||||
2019: 79.490,
|
||||
2020: 80.793,
|
||||
2021: 82.797,
|
||||
2022: 84.177,
|
||||
2023: 85.585,
|
||||
2024: 87.382,
|
||||
2025: 89.169,
|
||||
2026: 91.415,
|
||||
}
|
||||
|
||||
_HAS_ACO_DB = path("db.aco").exists()
|
||||
|
||||
|
||||
class TestGoldenConversionFactors:
|
||||
"""CI-runnable pin — guards against accidental CF edits."""
|
||||
|
||||
def test_all_years_present(self) -> None:
|
||||
assert set(RULES) == set(_EXPECTED_CF)
|
||||
|
||||
@pytest.mark.parametrize("year", sorted(_EXPECTED_CF))
|
||||
def test_cf_matches_published_value(self, year: int) -> None:
|
||||
assert RULES[year].conversion_factor == _EXPECTED_CF[year], (
|
||||
f"CY{year} OPPS conversion factor changed. If this is a real "
|
||||
f"correction, update _EXPECTED_CF with a Federal Register / "
|
||||
f"Addendum B citation — do not silently override it."
|
||||
)
|
||||
|
||||
def test_monotonic_except_2016(self) -> None:
|
||||
"""CFs rise year over year, except the deliberate CY2016 dip."""
|
||||
years = sorted(_EXPECTED_CF)
|
||||
for prev, cur in zip(years, years[1:]):
|
||||
prev_cf = RULES[prev].conversion_factor
|
||||
cur_cf = RULES[cur].conversion_factor
|
||||
if cur == 2016:
|
||||
assert cur_cf < prev_cf
|
||||
else:
|
||||
assert cur_cf > prev_cf
|
||||
|
||||
|
||||
@pytest.mark.skipif(not _HAS_ACO_DB, reason="data/aco.duckdb not present")
|
||||
class TestAddendumBSelfConsistency:
|
||||
"""Prove weight × CF reproduces CMS's published Addendum B rate.
|
||||
|
||||
Runs only where the aco DuckDB (with ``opps.addendum_b``) is present.
|
||||
This is the independent check that the golden CFs are *correct*: if a
|
||||
CF is wrong, the calculated national rate diverges from the published
|
||||
``payment_rate`` and this fails.
|
||||
"""
|
||||
|
||||
def test_reconciles_to_the_cent_for_all_loaded_years(self) -> None:
|
||||
from conf import connect
|
||||
from rec.engine import reconcile
|
||||
from rec.pricers.opps import OppsPricer
|
||||
|
||||
con = connect.duckdb()
|
||||
pricer = OppsPricer()
|
||||
years = pricer.years_available(con)
|
||||
assert years, "opps.addendum_b has no years overlapping RULES"
|
||||
for year in years:
|
||||
r = reconcile(pricer, con, year)
|
||||
assert r.matched_rows > 0, f"CY{year}: no APC rows to reconcile"
|
||||
assert r.exact_matches == r.matched_rows, (
|
||||
f"CY{year}: {r.matched_rows - r.exact_matches} of "
|
||||
f"{r.matched_rows} APC rows do not match weight × CF to the "
|
||||
f"cent — the CY{year} conversion factor is wrong."
|
||||
)
|
||||
Reference in New Issue
Block a user