Files
stack/tests/rec/pricers/test_opps.py
kert 59272d715a feat(rec): OppsPricer — reconcile OPPS APC rates vs Addendum B
Adds the second pricer to the reconciliation engine (rec was open/closed
with only PfsPricer). OppsPricer validates that our OPPS conversion
factor reproduces CMS's published Addendum B national unadjusted rate:

    ground truth = opps.addendum_b.payment_rate  (published, rate > 0)
    calculated   = relative_weight × RULES[year].conversion_factor
                   (APC-priced rows, weight > 0)

Rows with a weight but no published rate (packaged, SI N) surface as
calculated_only; a published rate with no weight (non-APC method) as
ground_truth_only — mirroring how PfsPricer treats carrier-priced codes.
Wage-index adjustment is intentionally out of scope: Addendum B is the
national unadjusted rate, so the check is weight × CF only.

Also:
- stack rec opps CLI subcommand (mirrors rec pfs).
- Pricer-aware "no years available" hint (was hardcoded to PFS tables).

100% coverage on the new module, CLI paths, and registry.

Note: running this against loaded Addendum B data surfaces a systematic
CF drift in opps.rules for CY2021-CY2026 (stored CFs differ from the
empirical payment_rate/weight, which matches the real published CMS
values). Tracked for a follow-up fix — the pricer is doing its job.
2026-07-08 10:38:48 -04:00

165 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Tests for rec.pricers.opps.OppsPricer with a seeded in-memory DuckDB."""
from __future__ import annotations
import duckdb
import polars as pl
import pytest
from opps.rules import RULES
from rec.base import Pricer
from rec.engine import reconcile
from rec.pricers import PRICERS
from rec.pricers.opps import OppsPricer
# Pick a year present in opps.rules.RULES.
_TEST_YEAR = 2026
assert _TEST_YEAR in RULES, f"test seed year {_TEST_YEAR} missing from RULES"
_CF = RULES[_TEST_YEAR].conversion_factor
@pytest.fixture
def con() -> duckdb.DuckDBPyConnection:
"""DuckDB in-memory seeded with ``opps.addendum_b`` exercising the
full match/miss matrix:
10060 (T): exact match — payment_rate == weight × CF
10061 (S): 1¢ near-miss — published rate 1¢ below weight × CF
10062 (A): ground-truth only — published rate, NULL weight
(priced by a non-APC method → nothing to compute)
10063 (N): calculated only — positive weight, NULL rate
(packaged → computable but CMS publishes no rate)
"""
c = duckdb.connect(":memory:")
c.execute("CREATE SCHEMA opps")
c.execute(
"""
CREATE TABLE opps.addendum_b (
hcpcs VARCHAR,
short_description VARCHAR,
status_indicator VARCHAR,
apc VARCHAR,
apc_title VARCHAR,
relative_weight DOUBLE,
payment_rate DOUBLE,
minimum_unadjusted_copayment DOUBLE,
year INTEGER
)
"""
)
exact_rate = round(2.0 * _CF, 2)
near_rate = round(round(3.0 * _CF, 2) - 0.01, 2)
c.execute(
f"""
INSERT INTO opps.addendum_b
(hcpcs, status_indicator, apc, relative_weight, payment_rate, year)
VALUES
('10060', 'T', '5051', 2.0, {exact_rate}, {_TEST_YEAR}),
('10061', 'S', '5052', 3.0, {near_rate}, {_TEST_YEAR}),
('10062', 'A', NULL, NULL, 150.00, {_TEST_YEAR}),
('10063', 'N', '5053', 1.5, NULL, {_TEST_YEAR})
"""
)
return c
@pytest.fixture
def test_year() -> int:
return _TEST_YEAR
class TestRegistration:
def test_in_PRICERS(self) -> None:
assert "opps" in PRICERS
assert PRICERS["opps"] is OppsPricer
def test_conforms_to_protocol(self) -> None:
assert isinstance(OppsPricer(), Pricer)
def test_class_attributes(self) -> None:
assert OppsPricer.system == "opps"
assert OppsPricer.join_keys == ["year", "hcpcs"]
assert OppsPricer.compare_cols == ["payment_rate"]
class TestGroundTruth:
def test_row_count(self, con, test_year) -> None:
# Published rate > 0: 10060, 10061, 10062 = 3 (10063 has NULL rate).
gt = OppsPricer().ground_truth(con, test_year)
assert gt.height == 3
def test_columns(self, con, test_year) -> None:
gt = OppsPricer().ground_truth(con, test_year)
for col in ["year", "hcpcs", "payment_rate"]:
assert col in gt.columns
def test_excludes_null_rate(self, con, test_year) -> None:
"""10063 is packaged (NULL published rate) — not ground truth."""
gt = OppsPricer().ground_truth(con, test_year)
assert "10063" not in gt["hcpcs"].to_list()
class TestCalculated:
def test_row_count(self, con, test_year) -> None:
# weight > 0: 10060, 10061, 10063 = 3 (10062 has NULL weight).
calc = OppsPricer().calculated(con, test_year)
assert calc.height == 3
def test_excludes_null_weight(self, con, test_year) -> None:
"""10062 has no relative weight → nothing to compute."""
calc = OppsPricer().calculated(con, test_year)
assert "10062" not in calc["hcpcs"].to_list()
def test_weight_times_cf(self, con, test_year) -> None:
calc = OppsPricer().calculated(con, test_year)
rate = calc.filter(pl.col("hcpcs") == "10060")["payment_rate"][0]
assert rate == round(2.0 * _CF, 2)
def test_returns_cents_rounded(self, con, test_year) -> None:
calc = OppsPricer().calculated(con, test_year)
for rate in calc["payment_rate"].to_list():
assert rate == round(rate, 2)
class TestReconcileOpps:
def test_summary_counts(self, con, test_year) -> None:
r = reconcile(OppsPricer(), con, test_year)
assert r.ground_truth_rows == 3 # 10060, 10061, 10062
assert r.calculated_rows == 3 # 10060, 10061, 10063
assert r.matched_rows == 2 # 10060, 10061
assert r.ground_truth_only == 1 # 10062 (published, no weight)
assert r.calculated_only == 1 # 10063 (packaged, no rate)
def test_exact_and_near(self, con, test_year) -> None:
r = reconcile(OppsPricer(), con, test_year)
assert r.exact_matches == 1 # 10060
assert r.near_matches == 2 # 10060 + 10061 (1¢)
def test_tolerance_promotes(self, con, test_year) -> None:
r = reconcile(OppsPricer(), con, test_year, tolerance_cents=1)
assert r.exact_matches == 2
def test_delta_table_contains_all_hcpcs(self, con, test_year) -> None:
r = reconcile(OppsPricer(), con, test_year)
assert set(r.deltas["hcpcs"].to_list()) == {
"10060",
"10061",
"10062",
"10063",
}
def test_years_available(self, con, test_year) -> None:
assert OppsPricer().years_available(con) == [test_year]
def test_years_available_excludes_non_rule_years(self, con) -> None:
"""A year absent from opps.rules.RULES can't be priced."""
con.execute(
"INSERT INTO opps.addendum_b (hcpcs, relative_weight, payment_rate, year) "
"VALUES ('20000', 1.0, 10.0, 1999)"
)
assert 1999 not in OppsPricer().years_available(con)
def test_missing_year_raises(self, con) -> None:
with pytest.raises(KeyError, match="RuleYear missing"):
OppsPricer().calculated(con, 1999)