With CY2014-2020 Addendum B now ingested, every year's conversion factor is cross-checked against CMS's own published rates (weight × CF vs payment_rate) — all 13 reconcile 100% exact, independently confirming the Federal-Register CFs for 2014-2020. One correction fell out: CY2020 = 80.784 (not 80.793). The archived addenda are the NFRM (as-published) version, whose national rates were computed with the as-published 80.784; CMS later cited a corrected 80.793, but our source data uses 80.784, so that is the value that reproduces it to the cent. Update the rule, the golden pin, and the note. Extend the CI fixture (tests/opps/fixtures/addendum_b_sample.csv) from CY2021-2026 to all 13 years — 5 real rows per year spanning APC weights ~0.05 to ~700 — so the in-CI correctness proof now covers every year.
162 lines
6.4 KiB
Python
162 lines
6.4 KiB
Python
"""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. Three 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. ``TestAddendumBFixtureConsistency`` reconciles OppsPricer against a
|
||
committed slice of *real* Addendum B rows (fixtures/addendum_b_
|
||
sample.csv — real published payment_rate, not computed). It runs in
|
||
CI with no external data, and because the rows are real a wrong CF
|
||
fails here — the independent correctness proof, enforced in CI.
|
||
|
||
3. ``TestAddendumBSelfConsistency`` (skipped when the aco DuckDB is
|
||
absent, e.g. headless CI) does the same against the full loaded
|
||
Addendum B — broader coverage where the data is present.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
from conf import path
|
||
from opps.rules import RULES
|
||
|
||
_FIXTURE = Path(__file__).parent / "fixtures" / "addendum_b_sample.csv"
|
||
|
||
# Full OPPS conversion factor — the rate for hospitals that MEET quality
|
||
# reporting (not the reduced/OQR-penalty CF).
|
||
#
|
||
# Sources (every year cross-checked BOTH ways):
|
||
# Federal Register — each year's OPPS/ASC Final Rule preamble ("we are
|
||
# using a conversion factor of $X"), primary for CY2014-2020.
|
||
# Empirical — payment_rate / relative_weight in opps.addendum_b (all 13
|
||
# years now ingested, 5,000+ APC-priced rows/year), which
|
||
# reproduces the published national rate to the cent.
|
||
# CY2020 = 80.784, the as-published NFRM value that reproduces the CY2020
|
||
# Addendum B we ingest (the archived NFRM addenda are our source).
|
||
# CMS later cited a corrected 80.793; we track the value that
|
||
# matches our data.
|
||
# 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.784,
|
||
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
|
||
|
||
|
||
class TestAddendumBFixtureConsistency:
|
||
"""CI-runnable correctness proof against a committed slice of real
|
||
Addendum B rows.
|
||
|
||
The fixture holds real published ``payment_rate`` values (never
|
||
computed), spanning APC weights from ~0.05 to ~700 across every year
|
||
CY2014-CY2026. If any of those years' conversion factors is wrong, the
|
||
calculated ``weight × CF`` diverges from the published rate and this
|
||
fails — no external dataset required, so the correctness check runs
|
||
in headless CI.
|
||
"""
|
||
|
||
def test_fixture_reconciles_to_the_cent(self) -> None:
|
||
import duckdb
|
||
|
||
from rec.engine import reconcile
|
||
from rec.pricers.opps import OppsPricer
|
||
|
||
con = duckdb.connect(":memory:")
|
||
con.execute("CREATE SCHEMA opps")
|
||
con.execute(
|
||
"CREATE TABLE opps.addendum_b AS SELECT * FROM read_csv_auto(?)",
|
||
[str(_FIXTURE)],
|
||
)
|
||
pricer = OppsPricer()
|
||
years = pricer.years_available(con)
|
||
assert years == list(range(2014, 2027))
|
||
for year in years:
|
||
r = reconcile(pricer, con, year)
|
||
assert r.matched_rows > 0, f"CY{year}: no fixture rows"
|
||
assert r.exact_matches == r.matched_rows, (
|
||
f"CY{year}: {r.matched_rows - r.exact_matches} of "
|
||
f"{r.matched_rows} real Addendum B rows do not match "
|
||
f"weight × CF to the cent — the CY{year} conversion "
|
||
f"factor is wrong."
|
||
)
|
||
|
||
|
||
@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."
|
||
)
|