Fix Zotero table models for Zotero 9:
- Remove stale Annotations/Highlights/Transaction* models
- Add ItemAnnotations, RetractedItems, DeletedCollections,
DeletedSearches, DbDebug1
- Fix ItemAttachments, Libraries, Users column mismatches
New test files covering all major modules:
- cli/{bib,prisma,rec,zot,mail,run} deep exercising tests
- mail/{droplet,postmark,resend,cloudflare} lifecycle tests
- bib/{iom,oig,pincite,sync,regulations_gov,email_ingest,format,store}
- prisma/{vpn,fetch,export,llm,screen,eligibility,extract,project,ingest,flow}
- aco/lake/{unity,quality,deploy} + api/aco coverage gaps
- zot/{ops,db,extract,duck} + rec/{report,engine,base,pricers}
- pfs/{pipe,rules,eq,files}
Add pytest-xdist for parallel test execution.
Tracks #353
235 lines
8.1 KiB
Python
235 lines
8.1 KiB
Python
"""Tests for pfs.rules — RuleYear model and RULES dict with annual PFS parameters.
|
|
|
|
Covers every line of pfs/rules/__init__.py:
|
|
- RuleYear construction with required and default fields
|
|
- RULES dict: years 2014-2026 (13 entries), all RuleYear instances
|
|
- Convenience aliases: cy2024, cy2025, cy2026
|
|
- Specific parameter values for spot-checked years
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from pfs.rules import RULES, RuleYear, cy2024, cy2025, cy2026
|
|
|
|
# ── RuleYear model ───────────────────────────────────────────────
|
|
|
|
|
|
class TestRuleYear:
|
|
"""RuleYear Pydantic model construction and defaults."""
|
|
|
|
def test_required_fields(self):
|
|
ry = RuleYear(year=2026, conversion_factor=32.3465)
|
|
assert ry.year == 2026
|
|
assert ry.conversion_factor == 32.3465
|
|
|
|
def test_default_cf_qp(self):
|
|
ry = RuleYear(year=2020, conversion_factor=36.0)
|
|
assert ry.cf_qp is None
|
|
|
|
def test_default_budget_neutrality_adjustor(self):
|
|
ry = RuleYear(year=2020, conversion_factor=36.0)
|
|
assert ry.budget_neutrality_adjustor == 1.0
|
|
|
|
def test_default_work_gpci_floor(self):
|
|
ry = RuleYear(year=2020, conversion_factor=36.0)
|
|
assert ry.work_gpci_floor == 1.0
|
|
|
|
def test_default_pe_gpci_floor(self):
|
|
ry = RuleYear(year=2020, conversion_factor=36.0)
|
|
assert ry.pe_gpci_floor is False
|
|
|
|
def test_default_anesthesia_cf(self):
|
|
ry = RuleYear(year=2020, conversion_factor=36.0)
|
|
assert ry.anesthesia_cf is None
|
|
|
|
def test_default_mppr_diagnostic_imaging_pct(self):
|
|
ry = RuleYear(year=2020, conversion_factor=36.0)
|
|
assert ry.mppr_diagnostic_imaging_pct == 0.50
|
|
|
|
def test_default_mppr_therapy_pct(self):
|
|
ry = RuleYear(year=2020, conversion_factor=36.0)
|
|
assert ry.mppr_therapy_pct == 0.50
|
|
|
|
def test_default_telehealth_fee(self):
|
|
ry = RuleYear(year=2020, conversion_factor=36.0)
|
|
assert ry.telehealth_originating_site_fee is None
|
|
|
|
def test_default_clinical_labor_rate_update(self):
|
|
ry = RuleYear(year=2020, conversion_factor=36.0)
|
|
assert ry.clinical_labor_rate_update is False
|
|
|
|
def test_default_supply_equipment_price_update(self):
|
|
ry = RuleYear(year=2020, conversion_factor=36.0)
|
|
assert ry.supply_equipment_price_update is False
|
|
|
|
def test_default_federal_register_citation(self):
|
|
ry = RuleYear(year=2020, conversion_factor=36.0)
|
|
assert ry.federal_register_citation == ""
|
|
|
|
def test_all_fields_supplied(self):
|
|
ry = RuleYear(
|
|
year=2026,
|
|
conversion_factor=32.3465,
|
|
cf_qp=32.67,
|
|
budget_neutrality_adjustor=1.0,
|
|
work_gpci_floor=1.0,
|
|
pe_gpci_floor=True,
|
|
anesthesia_cf=22.0,
|
|
mppr_diagnostic_imaging_pct=0.50,
|
|
mppr_therapy_pct=0.50,
|
|
telehealth_originating_site_fee=30.38,
|
|
clinical_labor_rate_update=True,
|
|
supply_equipment_price_update=True,
|
|
federal_register_citation="90 FR 101174",
|
|
)
|
|
assert ry.year == 2026
|
|
assert ry.pe_gpci_floor is True
|
|
assert ry.anesthesia_cf == 22.0
|
|
assert ry.supply_equipment_price_update is True
|
|
|
|
|
|
# ── RULES dict structure ─────────────────────────────────────────
|
|
|
|
|
|
class TestRulesDict:
|
|
"""RULES maps calendar year -> RuleYear for 2014-2026."""
|
|
|
|
EXPECTED_YEARS = list(range(2014, 2027))
|
|
|
|
def test_count(self):
|
|
assert len(RULES) == 13
|
|
|
|
def test_keys(self):
|
|
assert sorted(RULES.keys()) == self.EXPECTED_YEARS
|
|
|
|
@pytest.mark.parametrize("year", list(range(2014, 2027)))
|
|
def test_values_are_ruleyear(self, year):
|
|
assert isinstance(RULES[year], RuleYear)
|
|
|
|
@pytest.mark.parametrize("year", list(range(2014, 2027)))
|
|
def test_year_field_matches_key(self, year):
|
|
assert RULES[year].year == year
|
|
|
|
|
|
# ── Convenience aliases ──────────────────────────────────────────
|
|
|
|
|
|
class TestAliases:
|
|
"""cy2024, cy2025, cy2026 point to the correct RULES entries."""
|
|
|
|
def test_cy2024_is_rules_2024(self):
|
|
assert cy2024 is RULES[2024]
|
|
|
|
def test_cy2025_is_rules_2025(self):
|
|
assert cy2025 is RULES[2025]
|
|
|
|
def test_cy2026_is_rules_2026(self):
|
|
assert cy2026 is RULES[2026]
|
|
|
|
|
|
# ── Spot-check specific year values ─────────────────────────────
|
|
|
|
|
|
class TestSpecificValues:
|
|
"""Verify key parameters for specific years match published CMS data."""
|
|
|
|
def test_cy2026_conversion_factor(self):
|
|
# CY2026 non-APM standard CF per AMA history :pincite:`VVBEVYLC`.
|
|
# Earlier values (32.3465, etc.) were stale copies from CY2025.
|
|
assert cy2026.conversion_factor == 33.4009
|
|
|
|
def test_cy2026_cf_qp(self):
|
|
# CY2026 APM standard CF — $0.1666 higher than non-APM under
|
|
# MACRA 2015 per :pincite:`VVBEVYLC`.
|
|
assert cy2026.cf_qp == 33.5675
|
|
|
|
def test_cy2026_telehealth_fee(self):
|
|
assert cy2026.telehealth_originating_site_fee == 30.38
|
|
|
|
def test_cy2026_federal_register(self):
|
|
assert cy2026.federal_register_citation == "90 FR 101174"
|
|
|
|
def test_cy2026_clinical_labor_update(self):
|
|
assert cy2026.clinical_labor_rate_update is True
|
|
|
|
def test_cy2025_conversion_factor(self):
|
|
assert cy2025.conversion_factor == 32.3465
|
|
|
|
def test_cy2025_telehealth_fee(self):
|
|
assert cy2025.telehealth_originating_site_fee == 29.96
|
|
|
|
def test_cy2024_conversion_factor(self):
|
|
assert cy2024.conversion_factor == 33.2875
|
|
|
|
def test_cy2024_federal_register(self):
|
|
assert cy2024.federal_register_citation == "88 FR 78818"
|
|
|
|
def test_2022_clinical_labor_rate_update(self):
|
|
assert RULES[2022].clinical_labor_rate_update is True
|
|
|
|
def test_2022_conversion_factor(self):
|
|
assert RULES[2022].conversion_factor == 34.6062
|
|
|
|
def test_2023_supply_equipment_price_update(self):
|
|
assert RULES[2023].supply_equipment_price_update is True
|
|
|
|
def test_2014_conversion_factor(self):
|
|
assert RULES[2014].conversion_factor == 35.8228
|
|
|
|
def test_2014_federal_register(self):
|
|
assert RULES[2014].federal_register_citation == "78 FR 74230"
|
|
|
|
def test_2021_conversion_factor(self):
|
|
assert RULES[2021].conversion_factor == 34.8931
|
|
|
|
|
|
# ── Gap coverage — missed lines ────────────────────────────────
|
|
|
|
|
|
class TestCfForDate:
|
|
"""Lines 176-181: cf_for_date with segments, no segments, and outside segments."""
|
|
|
|
def test_cf_for_date_with_segments(self):
|
|
"""Line 176-178: date falls within a segment."""
|
|
from datetime import date
|
|
|
|
from pfs.rules import CfSegment
|
|
|
|
ry = RuleYear(
|
|
year=2024,
|
|
conversion_factor=33.2875,
|
|
cf_segments=[
|
|
CfSegment(start=date(2024, 1, 1), end=date(2024, 3, 8), cf=32.7442),
|
|
CfSegment(start=date(2024, 3, 9), end=date(2024, 12, 31), cf=33.2875),
|
|
],
|
|
)
|
|
assert ry.cf_for_date(date(2024, 2, 1)) == 32.7442
|
|
assert ry.cf_for_date(date(2024, 6, 1)) == 33.2875
|
|
|
|
def test_cf_for_date_no_segments(self):
|
|
"""Lines 179-180: no segments → returns conversion_factor."""
|
|
from datetime import date
|
|
|
|
ry = RuleYear(year=2025, conversion_factor=32.3465)
|
|
assert ry.cf_for_date(date(2025, 6, 1)) == 32.3465
|
|
|
|
def test_cf_for_date_outside_segments(self):
|
|
"""Line 181: date outside all segments → ValueError."""
|
|
from datetime import date
|
|
|
|
import pytest
|
|
|
|
from pfs.rules import CfSegment
|
|
|
|
ry = RuleYear(
|
|
year=2024,
|
|
conversion_factor=33.2875,
|
|
cf_segments=[
|
|
CfSegment(start=date(2024, 3, 1), end=date(2024, 6, 30), cf=33.0),
|
|
],
|
|
)
|
|
with pytest.raises(ValueError, match="outside every CF segment"):
|
|
ry.cf_for_date(date(2024, 1, 15))
|