493 lines
20 KiB
Python
493 lines
20 KiB
Python
"""Tests for ALR, CCLF, and REACH filename classifiers.
|
|
|
|
Covers:
|
|
- aco.table.alr_filenames.classify
|
|
- aco.table.cclf_filenames.classify, identify_cclf_table, is_cclf
|
|
- aco.table.reach_filenames.classify
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from aco.table.alr_filenames import classify as alr_classify
|
|
from aco.table.cclf_filenames import (
|
|
CclfFilename,
|
|
identify_cclf_table,
|
|
is_cclf,
|
|
)
|
|
from aco.table.cclf_filenames import (
|
|
classify as cclf_classify,
|
|
)
|
|
from aco.table.reach_filenames import classify as reach_classify
|
|
|
|
# ── ALR filenames ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
class TestAlrClassify:
|
|
"""Tests for aco.table.alr_filenames.classify."""
|
|
|
|
# ── Annual ALR tables ────────────────────────────────────────────────────
|
|
|
|
@pytest.mark.parametrize(
|
|
"table_num, table_id",
|
|
[
|
|
("1", "1-1"),
|
|
("2", "1-2"),
|
|
("3", "1-3"),
|
|
("4", "1-4"),
|
|
("5", "1-5"),
|
|
("6", "1-6"),
|
|
("7", "1-7"),
|
|
("8", "1-8"),
|
|
("9", "1-9"),
|
|
],
|
|
)
|
|
def test_annual_alr_table(self, table_num: str, table_id: str) -> None:
|
|
fname = f"AALR_Table_1_{table_num}_A12345_2024.csv"
|
|
result = alr_classify(fname)
|
|
assert result is not None
|
|
assert result["type"] == "alr"
|
|
assert result["table_id"] == table_id
|
|
assert result["report_type"] == "annual"
|
|
assert result["aco_id"] == "A12345"
|
|
assert result["year"] == "2024"
|
|
assert "quarter" not in result
|
|
|
|
# ── Quarterly ALR tables ─────────────────────────────────────────────────
|
|
|
|
@pytest.mark.parametrize("quarter", ["Q1", "Q2", "Q3", "Q4"])
|
|
def test_quarterly_alr_table_1_2(self, quarter: str) -> None:
|
|
fname = f"QALR_Table_1_2_B67890_{2024}_{quarter}.csv"
|
|
result = alr_classify(fname)
|
|
assert result is not None
|
|
assert result["type"] == "alr"
|
|
assert result["table_id"] == "1-2"
|
|
assert result["report_type"] == "quarterly"
|
|
assert result["aco_id"] == "B67890"
|
|
assert result["year"] == "2024"
|
|
assert result["quarter"] == quarter
|
|
|
|
def test_quarterly_alr_table_1_5(self) -> None:
|
|
result = alr_classify("QALR_Table_1_5_XYZ99_2023_Q2.csv")
|
|
assert result is not None
|
|
assert result["table_id"] == "1-5"
|
|
assert result["report_type"] == "quarterly"
|
|
assert result["quarter"] == "Q2"
|
|
|
|
# ── ASR patterns ─────────────────────────────────────────────────────────
|
|
|
|
def test_annual_asr(self) -> None:
|
|
result = alr_classify("AASR_A12345_2024.xlsx")
|
|
assert result is not None
|
|
assert result["type"] == "asr"
|
|
assert result["report_type"] == "annual"
|
|
assert result["aco_id"] == "A12345"
|
|
assert result["year"] == "2024"
|
|
|
|
def test_quarterly_asr(self) -> None:
|
|
result = alr_classify("QASR_B67890_2024_Q3.xlsx")
|
|
assert result is not None
|
|
assert result["type"] == "asr"
|
|
assert result["report_type"] == "quarterly"
|
|
assert result["aco_id"] == "B67890"
|
|
assert result["year"] == "2024"
|
|
assert result["quarter"] == "Q3"
|
|
|
|
# ── Package ZIP files ────────────────────────────────────────────────────
|
|
|
|
def test_hassgn_package(self) -> None:
|
|
result = alr_classify("HASSGN_A12345_2024_01_15.zip")
|
|
assert result is not None
|
|
assert result["type"] == "package"
|
|
assert result["package_type"] == "initial_assignment"
|
|
assert result["aco_id"] == "A12345"
|
|
assert result["year"] == "2024"
|
|
assert result["month"] == "01"
|
|
assert result["day"] == "15"
|
|
|
|
def test_qexpu_package(self) -> None:
|
|
result = alr_classify("QEXPU_B67890_2024_06_30.zip")
|
|
assert result is not None
|
|
assert result["type"] == "package"
|
|
assert result["package_type"] == "quarterly"
|
|
|
|
def test_stlmt_package(self) -> None:
|
|
result = alr_classify("STLMT_C11111_2023_12_01.zip")
|
|
assert result is not None
|
|
assert result["type"] == "package"
|
|
assert result["package_type"] == "financial_reconciliation"
|
|
|
|
def test_bnmrk_package(self) -> None:
|
|
result = alr_classify("BNMRK_D22222_2024_03_20.zip")
|
|
assert result is not None
|
|
assert result["type"] == "package"
|
|
assert result["package_type"] == "historical_benchmark"
|
|
|
|
# ── Path objects ─────────────────────────────────────────────────────────
|
|
|
|
def test_accepts_path_object(self) -> None:
|
|
p = Path("/data/downloads/AALR_Table_1_1_A12345_2024.csv")
|
|
result = alr_classify(p)
|
|
assert result is not None
|
|
assert result["type"] == "alr"
|
|
assert result["aco_id"] == "A12345"
|
|
|
|
def test_accepts_path_with_directory(self) -> None:
|
|
result = alr_classify("/some/dir/AASR_X99_2025.xlsx")
|
|
assert result is not None
|
|
assert result["type"] == "asr"
|
|
|
|
# ── Unrecognized filenames ───────────────────────────────────────────────
|
|
|
|
def test_returns_none_for_unknown(self) -> None:
|
|
assert alr_classify("random_file.csv") is None
|
|
|
|
def test_returns_none_for_empty_string(self) -> None:
|
|
assert alr_classify("") is None
|
|
|
|
def test_returns_none_for_partial_match(self) -> None:
|
|
assert alr_classify("AALR_Table_1_1_A12345.csv") is None # missing year
|
|
|
|
def test_returns_none_for_wrong_extension(self) -> None:
|
|
assert alr_classify("AALR_Table_1_1_A12345_2024.xlsx") is None
|
|
|
|
|
|
# ── CCLF filenames ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
class TestCclfClassify:
|
|
"""Tests for aco.table.cclf_filenames.classify."""
|
|
|
|
# ── Individual CCLF files ────────────────────────────────────────────────
|
|
|
|
def test_sssp_file(self) -> None:
|
|
result = cclf_classify("P.A1234.ACO.ZC1Y25.D250716.T1234567")
|
|
assert result is not None
|
|
assert result.program == "sssp"
|
|
assert result.aco_id == "1234"
|
|
assert result.entity == "ACO"
|
|
assert result.file_id == "1"
|
|
assert result.cclf_table == "cclf1"
|
|
assert result.run_type == "Y"
|
|
assert result.performance_year == 2025
|
|
assert result.delivery_date == "250716"
|
|
assert result.delivery_time == "1234567"
|
|
assert result.is_zip is False
|
|
|
|
def test_reach_file(self) -> None:
|
|
result = cclf_classify("P.D5678.ACO.ZC5R24.D250101.T0000001")
|
|
assert result is not None
|
|
assert result.program == "reach"
|
|
assert result.file_id == "5"
|
|
assert result.cclf_table == "cclf5"
|
|
assert result.run_type == "R"
|
|
assert result.performance_year == 2024
|
|
|
|
def test_kcf_file(self) -> None:
|
|
result = cclf_classify("P.K9999.ACO.ZC8Y23.D230601.T0001234")
|
|
assert result is not None
|
|
assert result.program == "kcf"
|
|
assert result.cclf_table == "cclf8"
|
|
|
|
def test_ckcc_file(self) -> None:
|
|
result = cclf_classify("P.C1111.ACO.ZC0Y25.D250101.T0000001")
|
|
assert result is not None
|
|
assert result.program == "ckcc"
|
|
assert result.cclf_table == "cclf0"
|
|
|
|
def test_pcf_file(self) -> None:
|
|
result = cclf_classify("P.P2222.PRT.ZC7R24.D240601.T1111111")
|
|
assert result is not None
|
|
assert result.program == "pcf"
|
|
assert result.entity == "PRT"
|
|
assert result.cclf_table == "cclf7"
|
|
|
|
def test_iota_file(self) -> None:
|
|
result = cclf_classify("P.IOTA3333.ACO.ZC2Y25.D250101.T0000001")
|
|
assert result is not None
|
|
assert result.program == "iota"
|
|
assert result.aco_id == "3333"
|
|
assert result.cclf_table == "cclf2"
|
|
|
|
@pytest.mark.parametrize(
|
|
"file_id, expected_table",
|
|
[
|
|
("0", "cclf0"),
|
|
("1", "cclf1"),
|
|
("2", "cclf2"),
|
|
("3", "cclf3"),
|
|
("4", "cclf4"),
|
|
("5", "cclf5"),
|
|
("6", "cclf6"),
|
|
("7", "cclf7"),
|
|
("8", "cclf8"),
|
|
("9", "cclf9"),
|
|
("A", "cclfa"),
|
|
("B", "cclfb"),
|
|
],
|
|
)
|
|
def test_all_file_ids(self, file_id: str, expected_table: str) -> None:
|
|
fname = f"P.A1234.ACO.ZC{file_id}Y25.D250101.T0000001"
|
|
result = cclf_classify(fname)
|
|
assert result is not None
|
|
assert result.file_id == file_id
|
|
assert result.cclf_table == expected_table
|
|
|
|
# ── ZIP files ────────────────────────────────────────────────────────────
|
|
|
|
def test_zip_sssp(self) -> None:
|
|
result = cclf_classify("P.A1234.ACO.ZCY25.D250716.T1234567")
|
|
assert result is not None
|
|
assert result.program == "sssp"
|
|
assert result.file_id == ""
|
|
assert result.cclf_table == ""
|
|
assert result.is_zip is True
|
|
assert result.run_type == "Y"
|
|
assert result.performance_year == 2025
|
|
|
|
def test_zip_reach_runout(self) -> None:
|
|
result = cclf_classify("P.D5678.ACO.ZCR24.D250101.T0000001")
|
|
assert result is not None
|
|
assert result.program == "reach"
|
|
assert result.is_zip is True
|
|
assert result.run_type == "R"
|
|
assert result.performance_year == 2024
|
|
|
|
def test_zip_iota(self) -> None:
|
|
result = cclf_classify("P.IOTA4444.ACO.ZCY25.D250301.T5555555")
|
|
assert result is not None
|
|
assert result.program == "iota"
|
|
assert result.is_zip is True
|
|
|
|
# ── Unknown program prefix falls back to lower ───────────────────────────
|
|
|
|
def test_unknown_program_prefix_file(self) -> None:
|
|
result = cclf_classify("P.Z1234.ACO.ZC1Y25.D250101.T0000001")
|
|
assert result is not None
|
|
assert result.program == "z"
|
|
|
|
def test_unknown_program_prefix_zip(self) -> None:
|
|
result = cclf_classify("P.Z1234.ACO.ZCY25.D250101.T0000001")
|
|
assert result is not None
|
|
assert result.program == "z"
|
|
|
|
# ── Directory paths stripped ──────────────────────────────────────────────
|
|
|
|
def test_forward_slash_path_stripped(self) -> None:
|
|
result = cclf_classify("/data/cclf/P.A1234.ACO.ZC1Y25.D250716.T1234567")
|
|
assert result is not None
|
|
assert result.program == "sssp"
|
|
|
|
def test_backslash_path_stripped(self) -> None:
|
|
result = cclf_classify("C:\\Users\\data\\P.A1234.ACO.ZC1Y25.D250716.T1234567")
|
|
assert result is not None
|
|
assert result.program == "sssp"
|
|
|
|
# ── Unrecognized filenames ───────────────────────────────────────────────
|
|
|
|
def test_returns_none_for_unknown(self) -> None:
|
|
assert cclf_classify("random_file.csv") is None
|
|
|
|
def test_returns_none_for_empty_string(self) -> None:
|
|
assert cclf_classify("") is None
|
|
|
|
def test_returns_none_for_partial_cclf(self) -> None:
|
|
assert cclf_classify("P.A1234.ACO.ZC") is None
|
|
|
|
# ── NamedTuple unpacking ─────────────────────────────────────────────────
|
|
|
|
def test_result_is_namedtuple(self) -> None:
|
|
result = cclf_classify("P.A1234.ACO.ZC1Y25.D250716.T1234567")
|
|
assert isinstance(result, CclfFilename)
|
|
assert isinstance(result, tuple)
|
|
|
|
|
|
class TestIdentifyCclfTable:
|
|
"""Tests for aco.table.cclf_filenames.identify_cclf_table."""
|
|
|
|
def test_returns_table_name(self) -> None:
|
|
assert identify_cclf_table("P.A1234.ACO.ZC5Y25.D250101.T0000001") == "cclf5"
|
|
|
|
def test_returns_empty_for_zip(self) -> None:
|
|
assert identify_cclf_table("P.A1234.ACO.ZCY25.D250101.T0000001") == ""
|
|
|
|
def test_returns_empty_for_nonmatch(self) -> None:
|
|
assert identify_cclf_table("not_a_cclf_file.csv") == ""
|
|
|
|
|
|
class TestIsCclf:
|
|
"""Tests for aco.table.cclf_filenames.is_cclf."""
|
|
|
|
def test_true_for_cclf_file(self) -> None:
|
|
assert is_cclf("P.A1234.ACO.ZC1Y25.D250716.T1234567") is True
|
|
|
|
def test_true_for_cclf_zip(self) -> None:
|
|
assert is_cclf("P.A1234.ACO.ZCY25.D250716.T1234567") is True
|
|
|
|
def test_false_for_nonmatch(self) -> None:
|
|
assert is_cclf("random_file.csv") is False
|
|
|
|
|
|
# ── REACH filenames ────────────────────────────────────────────────────────────
|
|
|
|
|
|
class TestReachClassify:
|
|
"""Tests for aco.table.reach_filenames.classify."""
|
|
|
|
# ── QUALITY_PATTERN ──────────────────────────────────────────────────────
|
|
|
|
def test_annual_quality_report(self) -> None:
|
|
result = reach_classify("P.D1234.ANLQR.D230601.T120000.xlsx")
|
|
assert result is not None
|
|
assert result["file_type"] == "annual_quality_report"
|
|
assert result["entity_id"] == "1234"
|
|
assert result["date"] == "230601"
|
|
assert result["time"] == "120000"
|
|
assert "report_period" not in result
|
|
|
|
def test_quarterly_quality_report_with_quarter(self) -> None:
|
|
result = reach_classify("P.D5678.QTLQR.Q2.D230301.T093045.xlsx")
|
|
assert result is not None
|
|
assert result["file_type"] == "quarterly_quality_report"
|
|
assert result["report_period"] == "Q2"
|
|
|
|
def test_quality_report_with_version(self) -> None:
|
|
result = reach_classify("P.D1234.ANLQR.D230601.T1200001.xlsx")
|
|
assert result is not None
|
|
assert result["file_type"] == "annual_quality_report"
|
|
assert result["version"] == "1"
|
|
|
|
def test_quality_report_no_version(self) -> None:
|
|
result = reach_classify("P.D1234.QTLQR.D230101.T120000.xlsx")
|
|
assert result is not None
|
|
assert "version" not in result
|
|
|
|
# ── QUARTERLY_PATTERN ────────────────────────────────────────────────────
|
|
|
|
def test_quarterly_ppopr(self) -> None:
|
|
result = reach_classify("P.D1234.PPOPR.Q1.D230101.T1200001.xlsx")
|
|
assert result is not None
|
|
assert result["file_type"] == "prospective_plus_opportunity"
|
|
assert result["entity_id"] == "1234"
|
|
assert result["report_period"] == "Q1"
|
|
|
|
def test_quarterly_qbnmr(self) -> None:
|
|
result = reach_classify("P.D9999.QBNMR.Q4.D240101.T0000001.csv")
|
|
assert result is not None
|
|
assert result["file_type"] == "quarterly_benchmark_report"
|
|
assert result["report_period"] == "Q4"
|
|
|
|
def test_quarterly_with_version(self) -> None:
|
|
result = reach_classify("P.D1234.MEXPR.Q3.D230701.T1234567a.xlsx")
|
|
assert result is not None
|
|
assert result["version"] == "a"
|
|
|
|
def test_quarterly_without_version(self) -> None:
|
|
result = reach_classify("P.D1234.PPOPR.Q2.D230101.T1200001.csv")
|
|
assert result is not None
|
|
assert "version" not in result
|
|
|
|
# ── CCLF_PATTERN ─────────────────────────────────────────────────────────
|
|
|
|
def test_cclf_monthly(self) -> None:
|
|
result = reach_classify("P.A1234.ACO.ZCY23.D230101.T1200000.zip")
|
|
assert result is not None
|
|
assert result["file_type"] == "monthly_cclf"
|
|
assert result["entity_id"] == "1234"
|
|
assert result["date"] == "230101"
|
|
assert result["time"] == "1200000"
|
|
assert result["year"] == "23"
|
|
|
|
def test_cclf_runout(self) -> None:
|
|
result = reach_classify("P.A5678.ACO.ZCR24.D240601.T0000001.zip")
|
|
assert result is not None
|
|
assert result["file_type"] == "runout_cclf"
|
|
assert result["year"] == "24"
|
|
|
|
# ── RSR_PATTERN ──────────────────────────────────────────────────────────
|
|
|
|
def test_risk_score_report(self) -> None:
|
|
result = reach_classify("P.D1234.RAP1V2.D230101.T1200001.zip")
|
|
assert result is not None
|
|
assert result["file_type"] == "risk_score_report"
|
|
assert result["entity_id"] == "1234"
|
|
assert result["version"] == "PY1_V2"
|
|
|
|
def test_risk_score_report_different_py_version(self) -> None:
|
|
result = reach_classify("P.D9999.RAP3V1.D240601.T9999999.zip")
|
|
assert result is not None
|
|
assert result["file_type"] == "risk_score_report"
|
|
assert result["version"] == "PY3_V1"
|
|
|
|
# ── REACH_FILE_PATTERN (standard) ────────────────────────────────────────
|
|
|
|
def test_standard_palmr(self) -> None:
|
|
result = reach_classify("P.D1234.PALMR.D230101.T1200001.xlsx")
|
|
assert result is not None
|
|
assert result["file_type"] == "provider_alignment_report"
|
|
assert result["entity_id"] == "1234"
|
|
assert result["date"] == "230101"
|
|
assert result["time"] == "1200001"
|
|
|
|
def test_standard_ppopr(self) -> None:
|
|
result = reach_classify("P.D5678.PPOPR.D230601.T0000001.csv")
|
|
assert result is not None
|
|
assert result["file_type"] == "prospective_plus_opportunity"
|
|
|
|
def test_standard_altpr(self) -> None:
|
|
result = reach_classify("P.D1234.ALTPR.D230101.T1200001.xlsx")
|
|
assert result is not None
|
|
assert result["file_type"] == "alternative_payment_arrangement"
|
|
|
|
def test_standard_with_version_suffix(self) -> None:
|
|
result = reach_classify("P.D1234.PALMR.D230101.T12000012.xlsx")
|
|
assert result is not None
|
|
assert result["version"] == "2"
|
|
|
|
def test_standard_with_alpha_version(self) -> None:
|
|
result = reach_classify("P.D1234.PPOPR.D230101.T1200001a.xlsx")
|
|
assert result is not None
|
|
assert result["version"] == "a"
|
|
|
|
def test_standard_without_version(self) -> None:
|
|
result = reach_classify("P.D1234.PALMR.D230101.T1200001.txt")
|
|
assert result is not None
|
|
assert "version" not in result
|
|
|
|
def test_standard_unknown_file_code(self) -> None:
|
|
"""Unknown file codes fall back to lowercase base."""
|
|
result = reach_classify("P.D1234.ZZZZZ.D230101.T1200001.csv")
|
|
assert result is not None
|
|
assert result["file_type"] == "zzzzz"
|
|
|
|
def test_standard_code_with_trailing_digits(self) -> None:
|
|
"""File codes with trailing digits: base 5 chars looked up."""
|
|
result = reach_classify("P.D1234.PALMR1.D230101.T1200001.csv")
|
|
assert result is not None
|
|
# PALMR1 -> first 5 chars -> PALMR -> provider_alignment_report
|
|
assert result["file_type"] == "provider_alignment_report"
|
|
|
|
def test_standard_tparc(self) -> None:
|
|
result = reach_classify("P.D1234.TPARC.D230101.T1200001.zip")
|
|
assert result is not None
|
|
assert result["file_type"] == "weekly_claims_reduction"
|
|
|
|
def test_standard_mexpr(self) -> None:
|
|
result = reach_classify("P.D1234.MEXPR.D230101.T1200001.csv")
|
|
assert result is not None
|
|
assert result["file_type"] == "monthly_expenditure_report"
|
|
|
|
# ── Unrecognized filenames ───────────────────────────────────────────────
|
|
|
|
def test_returns_none_for_unknown(self) -> None:
|
|
assert reach_classify("random_file.csv") is None
|
|
|
|
def test_returns_none_for_empty_string(self) -> None:
|
|
assert reach_classify("") is None
|
|
|
|
def test_returns_none_for_partial_reach(self) -> None:
|
|
assert reach_classify("P.D1234") is None
|