Files
stack/tests/aco/test_express_provider_attribution.py
kert 8f99eeb154
Some checks failed
ci/woodpecker/push/deploy Pipeline failed
ci/woodpecker/push/ci Pipeline failed
add comprehensive test suite, CI/CD quality gates, and package publishing
- 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>
2026-02-28 14:58:48 -05:00

431 lines
14 KiB
Python

"""Tests for aco.express.provider_attribution — staging and
int_person_years functions.
Tests verify column selection, renames, and the person-year
grouping/casting logic.
"""
from __future__ import annotations
import polars as pl
import pytest
from aco.express.provider_attribution import (
int_person_years,
stg_core__claims_medical_claim,
stg_core__medical_claim,
stg_core__member_months,
stg_reference_data__calendar,
stg_terminology__provider,
)
# ── helpers ──────────────────────────────────────────────────────
def _cols(result: pl.DataFrame, *expected: str) -> None:
missing = [c for c in expected if c not in result.columns]
assert not missing, f"Missing columns: {missing}"
# ── stg_core__claims_medical_claim ───────────────────────────────
class TestStgCoreClaimsMedicalClaim:
"""Selects claim_id, claim_line_number, data_source,
encounter_id."""
@pytest.fixture
def input_df(self) -> pl.DataFrame:
return pl.DataFrame(
{
"claim_id": ["C1", "C2"],
"claim_line_number": [1, 2],
"data_source": ["src_a", "src_b"],
"encounter_id": ["E1", "E2"],
"extra_col": ["x", "y"],
}
)
def test_returns_dataframe(self, input_df) -> None:
result = stg_core__claims_medical_claim(input_df)
assert isinstance(result, pl.DataFrame)
def test_selects_four_columns(self, input_df) -> None:
result = stg_core__claims_medical_claim(input_df)
assert set(result.columns) == {
"claim_id",
"claim_line_number",
"data_source",
"encounter_id",
}
def test_excludes_extra_columns(self, input_df) -> None:
result = stg_core__claims_medical_claim(input_df)
assert "extra_col" not in result.columns
def test_preserves_row_count(self, input_df) -> None:
result = stg_core__claims_medical_claim(input_df)
assert len(result) == 2
def test_values_passed_through(self, input_df) -> None:
result = stg_core__claims_medical_claim(input_df)
assert result["claim_id"].to_list() == [
"C1",
"C2",
]
# ── stg_core__medical_claim ──────────────────────────────────────
class TestStgCoreMedicalClaim:
"""Selects 11 columns, renames rendering_id to
rendering_npi."""
@pytest.fixture
def input_df(self) -> pl.DataFrame:
return pl.DataFrame(
{
"claim_id": ["C1"],
"claim_line_number": [1],
"person_id": ["P1"],
"claim_start_date": ["2024-01-15"],
"claim_end_date": ["2024-01-20"],
"allowed_amount": [100.0],
"paid_amount": [80.0],
"rendering_id": ["NPI123"],
"hcpcs_code": ["99213"],
"data_source": ["test"],
"encounter_id": ["E1"],
"extra": ["drop_me"],
}
)
def test_returns_dataframe(self, input_df) -> None:
result = stg_core__medical_claim(input_df)
assert isinstance(result, pl.DataFrame)
def test_renames_rendering_id(self, input_df) -> None:
result = stg_core__medical_claim(input_df)
assert "rendering_npi" in result.columns
assert "rendering_id" not in result.columns
assert result["rendering_npi"][0] == "NPI123"
def test_selects_11_columns(self, input_df) -> None:
result = stg_core__medical_claim(input_df)
expected = {
"claim_id",
"claim_line_number",
"person_id",
"claim_start_date",
"claim_end_date",
"allowed_amount",
"paid_amount",
"rendering_npi",
"hcpcs_code",
"data_source",
"encounter_id",
}
assert set(result.columns) == expected
def test_excludes_extra(self, input_df) -> None:
result = stg_core__medical_claim(input_df)
assert "extra" not in result.columns
def test_preserves_values(self, input_df) -> None:
result = stg_core__medical_claim(input_df)
assert result["claim_id"][0] == "C1"
assert result["person_id"][0] == "P1"
assert result["allowed_amount"][0] == pytest.approx(100.0)
# ── stg_core__member_months ──────────────────────────────────────
class TestStgCoreMemberMonths:
"""Selects person_id and year_month."""
@pytest.fixture
def input_df(self) -> pl.DataFrame:
return pl.DataFrame(
{
"person_id": ["P1", "P2", "P3"],
"year_month": [
"202401",
"202402",
"202403",
],
"payer": ["MCR", "MCR", "MCR"],
}
)
def test_returns_dataframe(self, input_df) -> None:
result = stg_core__member_months(input_df)
assert isinstance(result, pl.DataFrame)
def test_selects_two_columns(self, input_df) -> None:
result = stg_core__member_months(input_df)
assert set(result.columns) == {
"person_id",
"year_month",
}
def test_excludes_payer(self, input_df) -> None:
result = stg_core__member_months(input_df)
assert "payer" not in result.columns
def test_preserves_row_count(self, input_df) -> None:
result = stg_core__member_months(input_df)
assert len(result) == 3
def test_values_passed_through(self, input_df) -> None:
result = stg_core__member_months(input_df)
assert result["person_id"].to_list() == [
"P1",
"P2",
"P3",
]
assert result["year_month"].to_list() == [
"202401",
"202402",
"202403",
]
# ── stg_reference_data__calendar ─────────────────────────────────
class TestStgReferenceDataCalendar:
"""Selects 7 date-related columns."""
@pytest.fixture
def input_df(self) -> pl.DataFrame:
from datetime import date
return pl.DataFrame(
{
"full_date": [date(2024, 1, 15)],
"year": [2024],
"month": [1],
"year_month": ["202401"],
"first_day_of_month": [date(2024, 1, 1)],
"last_day_of_month": [date(2024, 1, 31)],
"year_month_int": [202401],
"day_of_week": ["Monday"],
"quarter": [1],
}
)
def test_returns_dataframe(self, input_df) -> None:
result = stg_reference_data__calendar(input_df)
assert isinstance(result, pl.DataFrame)
def test_selects_seven_columns(self, input_df) -> None:
result = stg_reference_data__calendar(input_df)
expected = {
"full_date",
"year",
"month",
"year_month",
"first_day_of_month",
"last_day_of_month",
"year_month_int",
}
assert set(result.columns) == expected
def test_excludes_extra(self, input_df) -> None:
result = stg_reference_data__calendar(input_df)
assert "day_of_week" not in result.columns
assert "quarter" not in result.columns
def test_values_passed_through(self, input_df) -> None:
from datetime import date
result = stg_reference_data__calendar(input_df)
assert result["full_date"][0] == date(2024, 1, 15)
assert result["year"][0] == 2024
assert result["year_month_int"][0] == 202401
# ── stg_terminology__provider ────────────────────────────────────
class TestStgTerminologyProvider:
"""Selects npi, primary_taxonomy_code,
primary_specialty_description, entity_type_description.
"""
@pytest.fixture
def input_df(self) -> pl.DataFrame:
return pl.DataFrame(
{
"npi": ["1234567890", "9876543210"],
"primary_taxonomy_code": [
"207Q00000X",
"208D00000X",
],
"primary_specialty_description": [
"Family Medicine",
"General Practice",
],
"entity_type_description": [
"Individual",
"Organization",
],
"provider_first_name": [
"Alice",
"Bob",
],
"provider_last_name": [
"Smith",
"Jones",
],
}
)
def test_returns_dataframe(self, input_df) -> None:
result = stg_terminology__provider(input_df)
assert isinstance(result, pl.DataFrame)
def test_selects_four_columns(self, input_df) -> None:
result = stg_terminology__provider(input_df)
expected = {
"npi",
"primary_taxonomy_code",
"primary_specialty_description",
"entity_type_description",
}
assert set(result.columns) == expected
def test_excludes_name_columns(self, input_df) -> None:
result = stg_terminology__provider(input_df)
assert "provider_first_name" not in result.columns
assert "provider_last_name" not in result.columns
def test_preserves_row_count(self, input_df) -> None:
result = stg_terminology__provider(input_df)
assert len(result) == 2
def test_values_passed_through(self, input_df) -> None:
result = stg_terminology__provider(input_df)
assert result["npi"].to_list() == [
"1234567890",
"9876543210",
]
assert result["entity_type_description"].to_list() == [
"Individual",
"Organization",
]
# ── int_person_years ─────────────────────────────────────────────
class TestIntPersonYears:
"""int_person_years groups member_months by person_id +
first 4 chars of year_month (performance_year), casts
performance_year to Int32."""
@pytest.fixture
def member_months_df(self) -> pl.DataFrame:
return pl.DataFrame(
{
"person_id": [
"P1",
"P1",
"P1",
"P2",
"P2",
],
"year_month": [
"202401",
"202402",
"202403",
"202401",
"202501",
],
}
)
def test_returns_dataframe(self, member_months_df) -> None:
result = int_person_years(member_months_df)
assert isinstance(result, pl.DataFrame)
def test_output_columns(self, member_months_df) -> None:
result = int_person_years(member_months_df)
assert set(result.columns) == {
"person_id",
"performance_year",
}
def test_groups_by_person_and_year(self, member_months_df) -> None:
result = int_person_years(member_months_df)
# P1 has 3 months all in 2024 -> 1 row
# P2 has months in 2024 and 2025 -> 2 rows
assert len(result) == 3
def test_performance_year_is_int32(self, member_months_df) -> None:
result = int_person_years(member_months_df)
assert result["performance_year"].dtype == pl.Int32
def test_person_id_is_string(self, member_months_df) -> None:
result = int_person_years(member_months_df)
assert result["person_id"].dtype == pl.Utf8
def test_performance_year_values(self, member_months_df) -> None:
result = int_person_years(member_months_df)
years = sorted(result["performance_year"].to_list())
assert years == [2024, 2024, 2025]
def test_single_person_single_year(self) -> None:
df = pl.DataFrame(
{
"person_id": ["P1"],
"year_month": ["202406"],
}
)
result = int_person_years(df)
assert len(result) == 1
assert result["person_id"][0] == "P1"
assert result["performance_year"][0] == 2024
def test_multiple_months_same_year_one_row(
self,
) -> None:
df = pl.DataFrame(
{
"person_id": ["P1"] * 12,
"year_month": [f"2024{m:02d}" for m in range(1, 13)],
}
)
result = int_person_years(df)
assert len(result) == 1
def test_integer_year_month_input(self) -> None:
"""year_month is cast to String before slicing,
so integer inputs should also work."""
df = pl.DataFrame(
{
"person_id": ["P1", "P1"],
"year_month": [202401, 202502],
}
)
result = int_person_years(df)
assert len(result) == 2
years = sorted(result["performance_year"].to_list())
assert years == [2024, 2025]
def test_empty_input(self) -> None:
df = pl.DataFrame(
{
"person_id": pl.Series([], dtype=pl.Utf8),
"year_month": pl.Series([], dtype=pl.Utf8),
}
)
result = int_person_years(df)
assert len(result) == 0
assert set(result.columns) == {
"person_id",
"performance_year",
}