test(opps): enforce CF correctness in CI via a real Addendum B fixture
All checks were successful
CI / lint (push) Successful in 35s
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 16s
CI / test (push) Successful in 13m3s
All checks were successful
CI / lint (push) Successful in 35s
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 16s
CI / test (push) Successful in 13m3s
The self-consistency check previously skipped in headless CI (no aco DuckDB), so CI only guarded that the CFs were *unchanged*, not that they were *correct*. Add a committed slice of 30 real Addendum B rows (fixtures/addendum_b_sample.csv — real published payment_rate, weights ~0.05 to ~700, CY2021-CY2026) and reconcile OppsPricer against it in an in-memory DuckDB. This runs in CI with no external data, and because the rows are real, a wrong conversion factor makes weight × CF diverge from the published rate and fails the build. Verified fail-closed: reverting the CY2026 CF to the old wrong value fails the golden test, this fixture test, and the full-DB check together.
This commit is contained in:
31
tests/opps/fixtures/addendum_b_sample.csv
Normal file
31
tests/opps/fixtures/addendum_b_sample.csv
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
hcpcs,status_indicator,relative_weight,payment_rate,year
|
||||||
|
C2645,U,0.0566,4.69,2021
|
||||||
|
37195,T,3.7532,310.75,2021
|
||||||
|
51040,J1,21.6552,1792.99,2021
|
||||||
|
58561,J1,53.2572,4409.54,2021
|
||||||
|
69930,J1,415.8068,34427.56,2021
|
||||||
|
C2645,U,0.0557,4.69,2022
|
||||||
|
11982,Q1,3.2928,277.18,2022
|
||||||
|
15946,T,20.7807,1749.26,2022
|
||||||
|
0430T,J1,39.7464,3345.73,2022
|
||||||
|
38240,J1,487.3894,41026.98,2022
|
||||||
|
C2645,U,0.0548,4.69,2023
|
||||||
|
95707,S,3.2723,280.06,2023
|
||||||
|
43242,J1,20.3492,1741.59,2023
|
||||||
|
19330,Q2,40.1682,3437.8,2023
|
||||||
|
38240,J1,493.4673,42233.4,2023
|
||||||
|
C2645,U,0.0537,4.69,2024
|
||||||
|
57156,T,3.5016,305.98,2024
|
||||||
|
15220,T,19.9049,1739.33,2024
|
||||||
|
15958,T,39.1593,3421.82,2024
|
||||||
|
38240,J1,596.4656,52120.36,2024
|
||||||
|
C2645,U,0.0526,4.69,2025
|
||||||
|
91132,S,3.4922,311.4,2025
|
||||||
|
15100,T,20.5142,1829.23,2025
|
||||||
|
0717T,T,41.0565,3660.97,2025
|
||||||
|
38240,J1,663.2611,59142.33,2025
|
||||||
|
C2645,U,0.0513,4.69,2026
|
||||||
|
28630,T,2.7568,252.01,2026
|
||||||
|
31238,J1,19.8922,1818.45,2026
|
||||||
|
19371,J1,43.7591,4000.24,2026
|
||||||
|
38240,J1,702.9866,64263.52,2026
|
||||||
|
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
The OPPS conversion factor per year is correctness-critical reference
|
The OPPS conversion factor per year is correctness-critical reference
|
||||||
data: OppsPricer computes national APC rates as ``weight × CF`` and
|
data: OppsPricer computes national APC rates as ``weight × CF`` and
|
||||||
reconciles them against CMS's published Addendum B. Two guards keep it
|
reconciles them against CMS's published Addendum B. Three guards keep
|
||||||
bulletproof:
|
it bulletproof:
|
||||||
|
|
||||||
1. ``TestGoldenConversionFactors`` pins every year's CF to the value
|
1. ``TestGoldenConversionFactors`` pins every year's CF to the value
|
||||||
taken from that year's Federal Register OPPS Final Rule (CY2014-
|
taken from that year's Federal Register OPPS Final Rule (CY2014-
|
||||||
@@ -12,19 +12,28 @@ bulletproof:
|
|||||||
CY2026). Runs everywhere (no data needed), so any accidental edit
|
CY2026). Runs everywhere (no data needed), so any accidental edit
|
||||||
fails CI. Changing a value requires updating the citation below too.
|
fails CI. Changing a value requires updating the citation below too.
|
||||||
|
|
||||||
2. ``TestAddendumBSelfConsistency`` (skipped when the aco DuckDB is
|
2. ``TestAddendumBFixtureConsistency`` reconciles OppsPricer against a
|
||||||
absent, e.g. headless CI) proves the CFs actually reproduce CMS's
|
committed slice of *real* Addendum B rows (fixtures/addendum_b_
|
||||||
published Addendum B rate to the cent — the independent check that
|
sample.csv — real published payment_rate, not computed). It runs in
|
||||||
the golden values above are *correct*, not merely *unchanged*.
|
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 __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from conf import path
|
from conf import path
|
||||||
from opps.rules import RULES
|
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
|
# Full OPPS conversion factor — the rate for hospitals that MEET quality
|
||||||
# reporting (not the reduced/OQR-penalty CF).
|
# reporting (not the reduced/OQR-penalty CF).
|
||||||
#
|
#
|
||||||
@@ -84,6 +93,44 @@ class TestGoldenConversionFactors:
|
|||||||
assert cur_cf > prev_cf
|
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 CY2021-
|
||||||
|
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 == [2021, 2022, 2023, 2024, 2025, 2026]
|
||||||
|
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")
|
@pytest.mark.skipif(not _HAS_ACO_DB, reason="data/aco.duckdb not present")
|
||||||
class TestAddendumBSelfConsistency:
|
class TestAddendumBSelfConsistency:
|
||||||
"""Prove weight × CF reproduces CMS's published Addendum B rate.
|
"""Prove weight × CF reproduces CMS's published Addendum B rate.
|
||||||
|
|||||||
Reference in New Issue
Block a user