- 5519 unit tests covering all modules (aco, bcda, bls, cms, pfs, rex, bib) - ruff lint + format enforcement across entire codebase (377 files reformatted) - pre-commit hook: ruff check, ruff format, pytest - Woodpecker CI split into ci.yml (quality gate) and deploy.yml (package + images) - ci.yml: lint → test → validate-compose, runs on every push/PR - deploy.yml: build + publish Python package to Gitea PyPI registry, then container image builds, Trivy scans, and registry push (main branch only) - Gitea branch protection on main: requires CI status checks to pass - .gitignore updated for .coverage, dist/, *.egg-info/ - grafana config moved to dev/grafana/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
480 lines
16 KiB
Python
480 lines
16 KiB
Python
"""Tests for aco.express.quality_measures.
|
|
|
|
Covers the seven decorated performance-period wrapper functions
|
|
and the nine staging functions that select columns from core tables.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import date
|
|
|
|
import polars as pl
|
|
import pytest
|
|
|
|
from aco.express.quality_measures import (
|
|
int_adh_diabetes__performance_period,
|
|
int_adh_statins__performance_period,
|
|
int_adhras__performance_period,
|
|
int_cqm130__performance_period,
|
|
int_cqm438__performance_period,
|
|
int_nqf0420__performance_period,
|
|
int_supd__performance_period,
|
|
stg_condition,
|
|
stg_encounter,
|
|
stg_lab_result,
|
|
stg_medical_claim,
|
|
stg_medication,
|
|
stg_observation,
|
|
stg_patient,
|
|
stg_pharmacy_claim,
|
|
stg_procedure,
|
|
)
|
|
|
|
# ── fixtures ────────────────────────────────────────────────────────
|
|
|
|
|
|
@pytest.fixture
|
|
def measures_df() -> pl.DataFrame:
|
|
"""Value-set measures with all seven measure IDs."""
|
|
return pl.DataFrame(
|
|
{
|
|
"id": [
|
|
"ADH-Diabetes",
|
|
"ADH-Statins",
|
|
"ADH-RAS",
|
|
"CQM130",
|
|
"CQM438",
|
|
"NQF0420",
|
|
"SUPD",
|
|
],
|
|
"name": [
|
|
"Adherence Diabetes",
|
|
"Adherence Statins",
|
|
"Adherence RAS",
|
|
"CQM 130",
|
|
"CQM 438",
|
|
"NQF 0420",
|
|
"SUPD Measure",
|
|
],
|
|
"version": [
|
|
"2024.1",
|
|
"2024.1",
|
|
"2024.1",
|
|
"2024.1",
|
|
"2024.1",
|
|
"2024.1",
|
|
"2024.1",
|
|
],
|
|
}
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def encounter_for_qm_df() -> pl.DataFrame:
|
|
"""Encounter with encounter_group for stg_encounter."""
|
|
return pl.DataFrame(
|
|
{
|
|
"encounter_id": ["ENC001"],
|
|
"person_id": ["P001"],
|
|
"encounter_type": ["acute inpatient"],
|
|
"encounter_group": ["claims"],
|
|
"encounter_start_date": [date(2024, 1, 1)],
|
|
"encounter_end_date": [date(2024, 1, 5)],
|
|
"length_of_stay": [4],
|
|
"extra_col": ["drop_me"],
|
|
}
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def medical_claim_for_qm_df() -> pl.DataFrame:
|
|
"""Medical claim with all columns needed by stg_medical_claim."""
|
|
return pl.DataFrame(
|
|
{
|
|
"person_id": ["P001"],
|
|
"claim_id": ["CLM001"],
|
|
"claim_start_date": [date(2024, 1, 1)],
|
|
"claim_end_date": [date(2024, 1, 5)],
|
|
"place_of_service_code": ["11"],
|
|
"hcpcs_code": ["99213"],
|
|
"hcpcs_modifier_1": [None],
|
|
"hcpcs_modifier_2": [None],
|
|
"hcpcs_modifier_3": [None],
|
|
"hcpcs_modifier_4": [None],
|
|
"hcpcs_modifier_5": [None],
|
|
"extra_col": ["drop_me"],
|
|
}
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def lab_result_df() -> pl.DataFrame:
|
|
"""Minimal lab result for stg_lab_result passthrough."""
|
|
return pl.DataFrame(
|
|
{
|
|
"lab_result_id": ["LAB001"],
|
|
"person_id": ["P001"],
|
|
"encounter_id": ["ENC001"],
|
|
"result": ["120"],
|
|
"result_date": [date(2024, 3, 1)],
|
|
}
|
|
)
|
|
|
|
|
|
# ── TestPerformancePeriod ───────────────────────────────────────────
|
|
|
|
|
|
class TestPerformancePeriod:
|
|
"""Tests for the seven performance-period wrapper functions."""
|
|
|
|
_WRAPPERS = [
|
|
(int_adh_diabetes__performance_period, "ADH-Diabetes"),
|
|
(int_adh_statins__performance_period, "ADH-Statins"),
|
|
(int_adhras__performance_period, "ADH-RAS"),
|
|
(int_cqm130__performance_period, "CQM130"),
|
|
(int_cqm438__performance_period, "CQM438"),
|
|
(int_nqf0420__performance_period, "NQF0420"),
|
|
(int_supd__performance_period, "SUPD"),
|
|
]
|
|
|
|
@pytest.mark.parametrize("fn, measure_id", _WRAPPERS, ids=[m for _, m in _WRAPPERS])
|
|
def test_returns_dataframe(self, fn, measure_id, measures_df) -> None:
|
|
result = fn(measures_df)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
@pytest.mark.parametrize("fn, measure_id", _WRAPPERS, ids=[m for _, m in _WRAPPERS])
|
|
def test_single_row(self, fn, measure_id, measures_df) -> None:
|
|
result = fn(measures_df)
|
|
assert len(result) == 1
|
|
|
|
@pytest.mark.parametrize("fn, measure_id", _WRAPPERS, ids=[m for _, m in _WRAPPERS])
|
|
def test_measure_id_matches(self, fn, measure_id, measures_df) -> None:
|
|
result = fn(measures_df)
|
|
assert result["measure_id"][0] == measure_id
|
|
|
|
@pytest.mark.parametrize("fn, measure_id", _WRAPPERS, ids=[m for _, m in _WRAPPERS])
|
|
def test_expected_columns(self, fn, measure_id, measures_df) -> None:
|
|
result = fn(measures_df)
|
|
for col in [
|
|
"measure_id",
|
|
"measure_name",
|
|
"measure_version",
|
|
"performance_period_begin",
|
|
"performance_period_end",
|
|
]:
|
|
assert col in result.columns
|
|
|
|
@pytest.mark.parametrize("fn, measure_id", _WRAPPERS, ids=[m for _, m in _WRAPPERS])
|
|
def test_performance_period_dates(self, fn, measure_id, measures_df) -> None:
|
|
result = fn(measures_df)
|
|
assert result["performance_period_begin"][0] == date(2018, 1, 1)
|
|
assert result["performance_period_end"][0] == date(2018, 12, 31)
|
|
|
|
def test_missing_measure_returns_empty(self, measures_df) -> None:
|
|
"""When the measure ID is not in the data,
|
|
the result is an empty DataFrame."""
|
|
partial = measures_df.filter(pl.col("id") != "ADH-Diabetes")
|
|
result = int_adh_diabetes__performance_period(partial)
|
|
assert len(result) == 0
|
|
|
|
def test_version_propagated(self, measures_df) -> None:
|
|
result = int_adh_diabetes__performance_period(measures_df)
|
|
assert result["measure_version"][0] == "2024.1"
|
|
|
|
|
|
# ── TestStgCondition ────────────────────────────────────────────────
|
|
|
|
|
|
class TestStgCondition:
|
|
"""Tests for stg_condition column selection."""
|
|
|
|
_EXPECTED = [
|
|
"person_id",
|
|
"claim_id",
|
|
"encounter_id",
|
|
"recorded_date",
|
|
"source_code_type",
|
|
"source_code",
|
|
"normalized_code_type",
|
|
"normalized_code",
|
|
]
|
|
|
|
def test_returns_dataframe(self, condition_df) -> None:
|
|
result = stg_condition(condition_df)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
def test_selects_expected_columns(self, condition_df) -> None:
|
|
result = stg_condition(condition_df)
|
|
assert result.columns == self._EXPECTED
|
|
|
|
def test_drops_extra_columns(self, condition_df) -> None:
|
|
result = stg_condition(condition_df)
|
|
assert "condition_id" not in result.columns
|
|
assert "payer" not in result.columns
|
|
|
|
def test_row_count_preserved(self, condition_df) -> None:
|
|
result = stg_condition(condition_df)
|
|
assert len(result) == len(condition_df)
|
|
|
|
|
|
# ── TestStgEncounter ────────────────────────────────────────────────
|
|
|
|
|
|
class TestStgEncounter:
|
|
"""Tests for stg_encounter column selection."""
|
|
|
|
_EXPECTED = [
|
|
"person_id",
|
|
"encounter_id",
|
|
"encounter_type",
|
|
"encounter_group",
|
|
"length_of_stay",
|
|
"encounter_start_date",
|
|
"encounter_end_date",
|
|
]
|
|
|
|
def test_returns_dataframe(self, encounter_for_qm_df) -> None:
|
|
result = stg_encounter(encounter_for_qm_df)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
def test_selects_expected_columns(self, encounter_for_qm_df) -> None:
|
|
result = stg_encounter(encounter_for_qm_df)
|
|
assert result.columns == self._EXPECTED
|
|
|
|
def test_drops_extra_columns(self, encounter_for_qm_df) -> None:
|
|
result = stg_encounter(encounter_for_qm_df)
|
|
assert "extra_col" not in result.columns
|
|
|
|
def test_row_count_preserved(self, encounter_for_qm_df) -> None:
|
|
result = stg_encounter(encounter_for_qm_df)
|
|
assert len(result) == len(encounter_for_qm_df)
|
|
|
|
|
|
# ── TestStgLabResult ────────────────────────────────────────────────
|
|
|
|
|
|
class TestStgLabResult:
|
|
"""Tests for stg_lab_result passthrough."""
|
|
|
|
def test_returns_dataframe(self, lab_result_df) -> None:
|
|
result = stg_lab_result(lab_result_df)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
def test_passthrough_preserves_all_columns(self, lab_result_df) -> None:
|
|
result = stg_lab_result(lab_result_df)
|
|
assert result.columns == lab_result_df.columns
|
|
|
|
def test_passthrough_preserves_data(self, lab_result_df) -> None:
|
|
result = stg_lab_result(lab_result_df)
|
|
assert result.equals(lab_result_df)
|
|
|
|
|
|
# ── TestStgMedicalClaim ─────────────────────────────────────────────
|
|
|
|
|
|
class TestStgMedicalClaim:
|
|
"""Tests for stg_medical_claim column selection."""
|
|
|
|
_EXPECTED = [
|
|
"person_id",
|
|
"claim_id",
|
|
"claim_start_date",
|
|
"claim_end_date",
|
|
"place_of_service_code",
|
|
"hcpcs_code",
|
|
"hcpcs_modifier_1",
|
|
"hcpcs_modifier_2",
|
|
"hcpcs_modifier_3",
|
|
"hcpcs_modifier_4",
|
|
"hcpcs_modifier_5",
|
|
]
|
|
|
|
def test_returns_dataframe(self, medical_claim_for_qm_df) -> None:
|
|
result = stg_medical_claim(medical_claim_for_qm_df)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
def test_selects_expected_columns(self, medical_claim_for_qm_df) -> None:
|
|
result = stg_medical_claim(medical_claim_for_qm_df)
|
|
assert result.columns == self._EXPECTED
|
|
|
|
def test_drops_extra_columns(self, medical_claim_for_qm_df) -> None:
|
|
result = stg_medical_claim(medical_claim_for_qm_df)
|
|
assert "extra_col" not in result.columns
|
|
|
|
def test_row_count_preserved(self, medical_claim_for_qm_df) -> None:
|
|
result = stg_medical_claim(medical_claim_for_qm_df)
|
|
assert len(result) == len(medical_claim_for_qm_df)
|
|
|
|
|
|
# ── TestStgMedication ───────────────────────────────────────────────
|
|
|
|
|
|
class TestStgMedication:
|
|
"""Tests for stg_medication column selection."""
|
|
|
|
_EXPECTED = [
|
|
"person_id",
|
|
"encounter_id",
|
|
"prescribing_date",
|
|
"dispensing_date",
|
|
"source_code_type",
|
|
"source_code",
|
|
"ndc_code",
|
|
"rxnorm_code",
|
|
]
|
|
|
|
def test_returns_dataframe(self, medication_df) -> None:
|
|
result = stg_medication(medication_df)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
def test_selects_expected_columns(self, medication_df) -> None:
|
|
result = stg_medication(medication_df)
|
|
assert result.columns == self._EXPECTED
|
|
|
|
def test_drops_extra_columns(self, medication_df) -> None:
|
|
result = stg_medication(medication_df)
|
|
assert "medication_id" not in result.columns
|
|
assert "route" not in result.columns
|
|
|
|
def test_row_count_preserved(self, medication_df) -> None:
|
|
result = stg_medication(medication_df)
|
|
assert len(result) == len(medication_df)
|
|
|
|
|
|
# ── TestStgObservation ──────────────────────────────────────────────
|
|
|
|
|
|
class TestStgObservation:
|
|
"""Tests for stg_observation column selection."""
|
|
|
|
_EXPECTED = [
|
|
"person_id",
|
|
"encounter_id",
|
|
"observation_date",
|
|
"result",
|
|
"source_code_type",
|
|
"source_code",
|
|
"normalized_code_type",
|
|
"normalized_code",
|
|
"normalized_description",
|
|
]
|
|
|
|
def test_returns_dataframe(self, observation_df) -> None:
|
|
result = stg_observation(observation_df)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
def test_selects_expected_columns(self, observation_df) -> None:
|
|
result = stg_observation(observation_df)
|
|
assert result.columns == self._EXPECTED
|
|
|
|
def test_drops_extra_columns(self, observation_df) -> None:
|
|
result = stg_observation(observation_df)
|
|
assert "observation_id" not in result.columns
|
|
assert "panel_id" not in result.columns
|
|
|
|
def test_row_count_preserved(self, observation_df) -> None:
|
|
result = stg_observation(observation_df)
|
|
assert len(result) == len(observation_df)
|
|
|
|
|
|
# ── TestStgPatient ──────────────────────────────────────────────────
|
|
|
|
|
|
class TestStgPatient:
|
|
"""Tests for stg_patient column selection."""
|
|
|
|
_EXPECTED = [
|
|
"person_id",
|
|
"sex",
|
|
"birth_date",
|
|
"death_date",
|
|
]
|
|
|
|
def test_returns_dataframe(self, patient_df) -> None:
|
|
result = stg_patient(patient_df)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
def test_selects_expected_columns(self, patient_df) -> None:
|
|
result = stg_patient(patient_df)
|
|
assert result.columns == self._EXPECTED
|
|
|
|
def test_drops_extra_columns(self, patient_df) -> None:
|
|
result = stg_patient(patient_df)
|
|
assert "first_name" not in result.columns
|
|
assert "address" not in result.columns
|
|
|
|
def test_row_count_preserved(self, patient_df) -> None:
|
|
result = stg_patient(patient_df)
|
|
assert len(result) == len(patient_df)
|
|
|
|
|
|
# ── TestStgPharmacyClaim ────────────────────────────────────────────
|
|
|
|
|
|
class TestStgPharmacyClaim:
|
|
"""Tests for stg_pharmacy_claim column selection."""
|
|
|
|
_EXPECTED = [
|
|
"person_id",
|
|
"dispensing_date",
|
|
"ndc_code",
|
|
"days_supply",
|
|
"paid_date",
|
|
]
|
|
|
|
def test_returns_dataframe(self, pharmacy_claim_df) -> None:
|
|
result = stg_pharmacy_claim(pharmacy_claim_df)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
def test_selects_expected_columns(self, pharmacy_claim_df) -> None:
|
|
result = stg_pharmacy_claim(pharmacy_claim_df)
|
|
assert result.columns == self._EXPECTED
|
|
|
|
def test_drops_extra_columns(self, pharmacy_claim_df) -> None:
|
|
result = stg_pharmacy_claim(pharmacy_claim_df)
|
|
assert "claim_id" not in result.columns
|
|
assert "quantity" not in result.columns
|
|
|
|
def test_row_count_preserved(self, pharmacy_claim_df) -> None:
|
|
result = stg_pharmacy_claim(pharmacy_claim_df)
|
|
assert len(result) == len(pharmacy_claim_df)
|
|
|
|
|
|
# ── TestStgProcedure ────────────────────────────────────────────────
|
|
|
|
|
|
class TestStgProcedure:
|
|
"""Tests for stg_procedure column selection."""
|
|
|
|
_EXPECTED = [
|
|
"person_id",
|
|
"encounter_id",
|
|
"procedure_date",
|
|
"source_code_type",
|
|
"source_code",
|
|
"normalized_code_type",
|
|
"normalized_code",
|
|
"modifier_1",
|
|
"modifier_2",
|
|
"modifier_3",
|
|
"modifier_4",
|
|
"modifier_5",
|
|
]
|
|
|
|
def test_returns_dataframe(self, procedure_df) -> None:
|
|
result = stg_procedure(procedure_df)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
def test_selects_expected_columns(self, procedure_df) -> None:
|
|
result = stg_procedure(procedure_df)
|
|
assert result.columns == self._EXPECTED
|
|
|
|
def test_drops_extra_columns(self, procedure_df) -> None:
|
|
result = stg_procedure(procedure_df)
|
|
assert "procedure_id" not in result.columns
|
|
assert "practitioner_id" not in result.columns
|
|
|
|
def test_row_count_preserved(self, procedure_df) -> None:
|
|
result = stg_procedure(procedure_df)
|
|
assert len(result) == len(procedure_df)
|