Files
stack/tests/cli/test_pfs_cli.py

1614 lines
60 KiB
Python

"""stack pfs — elements / lineage / families / guidance / reaction / review."""
from __future__ import annotations
from pathlib import Path
from unittest.mock import MagicMock
import duckdb
import pytest
from typer.testing import CliRunner
import cli.pfs as pfs_cli
from cli import app
from pfs.codetables import (
ElementRow,
EventRow,
GuidanceRow,
ReactionRow,
ensure_tables,
read_elements,
read_events,
read_families,
read_guidance,
read_reaction,
read_rounds,
write_cpt_edition,
write_elements,
write_events,
write_guidance,
)
from pfs.cpt_model import CptCode, CptEdition, CptSection
from pfs.extract import Extraction
runner = CliRunner()
@pytest.fixture
def restore_families():
"""Snapshot ``pfs.families.FAMILIES`` and restore it in teardown, even
if the test body raises — a test that reaches ``refresh_from`` must
not leave the live registry clobbered for the rest of the session."""
from pfs import families as mod
before = dict(mod.FAMILIES)
try:
yield mod
finally:
mod.FAMILIES.clear()
mod.FAMILIES.update(before)
@pytest.fixture
def con(monkeypatch, restore_families):
c = duckdb.connect(":memory:")
ensure_tables(c)
c.execute(
"CREATE TABLE pfs.rvu (hcpcs VARCHAR, mod VARCHAR, description VARCHAR, status_code VARCHAR, non_fac_total DOUBLE, year INTEGER)"
)
c.executemany(
"INSERT INTO pfs.rvu VALUES (?,?,?,?,?,?)",
[
("99490", None, "Chrnc care mgmt staff 1st 20", "A", 1.0, 2026),
("G2058", None, "Ccm add 20min", "A", 1.0, 2020),
("99999", None, "bundled thing", "B", 0.0, 2026),
],
)
class _Batch:
def __enter__(self):
return c
def __exit__(self, *a):
return False
monkeypatch.setattr(pfs_cli, "_batch", lambda: _Batch())
# A DuckDB cursor, not `c` itself: `stack pfs reaction` (unlike the
# other commands here) calls `_read()` and then, on `--write`,
# `_batch()` in the same invocation — closing a `.close()`d `c`
# would break the batch. A cursor shares `c`'s catalog/data and
# closes independently of it, matching how `_read()`/`_batch()` are
# genuinely separate connections in production.
monkeypatch.setattr(pfs_cli, "_read", lambda: c.cursor())
monkeypatch.setattr(pfs_cli, "_store", lambda: object())
published = []
monkeypatch.setattr(pfs_cli, "_publish", lambda: published.append(True))
class _ConProxy:
"""``DuckDBPyConnection`` has no ``__dict__`` (C extension type), so
a plain ``con.published = []`` fails; wrap it to attach the
publish-tracking list the tests assert on while forwarding
everything else (execute, executemany, ...) to the real connection."""
def __init__(self, real, published):
object.__setattr__(self, "_real", real)
object.__setattr__(self, "published", published)
def __getattr__(self, name):
return getattr(object.__getattribute__(self, "_real"), name)
try:
yield _ConProxy(c, published)
finally:
c.close()
def _empty_extractions(store, con, codes, *, classify=None):
"""``pfs.extract.extract_codes``'s shape with nothing extracted — the
stand-in for tests that only care which codes the CLI targeted."""
return {code: Extraction(code, (), ()) for code in codes}
class TestElements:
def test_writes_rows_and_publishes(self, con, monkeypatch):
def fake_extract(store, con_, codes, *, classify=None):
return {
code: Extraction(
code,
(
ElementRow(
code,
2026,
"activity",
"consent",
"",
"Consent;",
"K",
1,
2,
"fr",
),
),
(),
)
for code in codes
}
monkeypatch.setattr(pfs_cli, "extract_codes", fake_extract)
res = runner.invoke(app, ["pfs", "elements", "--code", "g0556", "--no-llm"])
assert res.exit_code == 0, res.output
assert "G0556: 1 elements, 0 for review" in res.output
assert [r.value for r in read_elements(con, "G0556")] == ["consent"]
assert con.published == [True]
def test_summary_line_reports_confirmed_count_when_nonzero(self, con, monkeypatch):
# #703: the elements summary surfaces confirmed_by, but only when
# there's something to report.
def fake_extract(store, con_, codes, *, classify=None):
return {
code: Extraction(
code,
(
ElementRow(
code,
2026,
"activity",
"consent",
"",
"Consent;",
"K",
1,
2,
"fr",
"cpt:GQGTPGYV",
),
ElementRow(
code,
2026,
"activity",
"24-7-access",
"",
"24/7;",
"K",
3,
2,
"fr",
),
),
(),
)
for code in codes
}
monkeypatch.setattr(pfs_cli, "extract_codes", fake_extract)
res = runner.invoke(app, ["pfs", "elements", "--code", "g0556", "--no-llm"])
assert res.exit_code == 0, res.output
assert "G0556: 2 elements, 0 for review, 1 confirmed by a second source" in (
res.output
)
def test_summary_line_omits_confirmed_when_none(self, con, monkeypatch):
monkeypatch.setattr(pfs_cli, "extract_codes", _empty_extractions)
res = runner.invoke(app, ["pfs", "elements", "--code", "g0556", "--no-llm"])
assert res.exit_code == 0, res.output
assert "confirmed" not in res.output
def test_dry_run_does_not_write(self, con, monkeypatch):
monkeypatch.setattr(pfs_cli, "extract_codes", _empty_extractions)
def fail_batch():
raise AssertionError("--dry-run must not open a RW duckdb_batch connection")
monkeypatch.setattr(pfs_cli, "_batch", fail_batch)
read_calls = []
orig_read = pfs_cli._read
def spy_read():
read_calls.append(True)
return orig_read()
monkeypatch.setattr(pfs_cli, "_read", spy_read)
res = runner.invoke(
app, ["pfs", "elements", "--code", "G0556", "--no-llm", "--dry-run"]
)
assert res.exit_code == 0, res.output
assert con.published == []
assert read_calls == [True]
def test_all_payable_selects_art_status_in_newest_year(self, con, monkeypatch):
seen = []
monkeypatch.setattr(
pfs_cli,
"extract_codes",
lambda s, c, codes, *, classify=None: (
seen.extend(codes) or _empty_extractions(s, c, codes)
),
)
res = runner.invoke(
app, ["pfs", "elements", "--all-payable", "--no-llm", "--dry-run"]
)
assert res.exit_code == 0 and seen == ["99490"]
def test_repeated_family_option_expands_every_family(self, con, monkeypatch):
# #684 regression: --family used to be a single str, so a second
# --family silently discarded the first. It must now be repeatable
# and expand every family named.
seen = []
monkeypatch.setattr(
pfs_cli,
"extract_codes",
lambda s, c, codes, *, classify=None: (
seen.extend(codes) or _empty_extractions(s, c, codes)
),
)
res = runner.invoke(
app,
[
"pfs",
"elements",
"--family",
"CCM",
"--family",
"APCM",
"--no-llm",
"--dry-run",
],
)
assert res.exit_code == 0, res.output
from pfs.families import HAND_FAMILIES
expected = set(HAND_FAMILIES["CCM"].codes) | set(HAND_FAMILIES["APCM"].codes)
assert set(seen) == expected
class TestCodesForWarnSlow:
"""``_codes_for``'s target-count note is for ``elements``, whose
``--all-payable`` run makes one model call per unplaced line — not for
``lineage --all-payable``, which is pure SQL/regex."""
def test_warns_by_default(self, con, caplog):
pfs_cli._codes_for(con, [], [], True)
assert "targeting 1 codes" in caplog.text
def test_silent_when_warn_slow_is_false(self, con, caplog):
pfs_cli._codes_for(con, [], [], True, warn_slow=False)
assert "targeting" not in caplog.text
class TestLineage:
def test_prints_and_writes(self, con, monkeypatch):
ev = EventRow(
"G2058",
2021,
"replaced_by",
"",
"99439",
"YBM4IZUS",
1578,
84639,
"fr",
True,
"",
)
monkeypatch.setattr(pfs_cli, "lineage", lambda c, s, code: [ev])
res = runner.invoke(app, ["pfs", "lineage", "--code", "G2058", "--write"])
assert res.exit_code == 0, res.output
assert "2021 replaced_by" in res.output and "YBM4IZUS ¶1578" in res.output
assert read_events(con, "G2058")[0].to_codes == "99439"
assert con.published == [True]
def test_prints_cpt_event_with_cpt_anchor_text(self, con, monkeypatch):
ev = EventRow(
"99490",
2015,
"cpt_changed",
"",
"",
"GQGTPGYV",
0,
0,
"cpt",
True,
"CPT Changes 2015",
)
monkeypatch.setattr(pfs_cli, "lineage", lambda c, s, code: [ev])
res = runner.invoke(app, ["pfs", "lineage", "--code", "99490", "--write"])
assert res.exit_code == 0, res.output
assert "2015 cpt_changed" in res.output
assert "cpt 2015 GQGTPGYV" in res.output
def test_without_write_reads_and_never_takes_the_write_lock(self, con, monkeypatch):
# I4: `stack pfs lineage` without --write must read the replica,
# not open a RW duckdb_batch connection (the DuckDB single-writer
# lock a notebook may be holding, #508-#514).
def fail_batch():
raise AssertionError(
"lineage without --write must not open a RW duckdb_batch connection"
)
monkeypatch.setattr(pfs_cli, "_batch", fail_batch)
monkeypatch.setattr(pfs_cli, "lineage", lambda c, s, code: [])
read_calls = []
orig_read = pfs_cli._read
def spy_read():
read_calls.append(True)
return orig_read()
monkeypatch.setattr(pfs_cli, "_read", spy_read)
res = runner.invoke(app, ["pfs", "lineage", "--code", "G2058"])
assert res.exit_code == 0, res.output
assert read_calls == [True]
assert con.published == []
def test_requires_code_or_all_payable(self, con):
res = runner.invoke(app, ["pfs", "lineage"])
assert res.exit_code != 0
def test_all_payable_limit_write_calls_writer_once_per_code_after_build(
self, con, monkeypatch
):
# Extra A/R/T, newest-year codes so --limit has something to
# trim: sorted, the newest-year A/R/T targets are
# 99490 < A0001 < B0002 ('9' sorts before letters); 99999 is
# status B (excluded) and G2058 is a stale year (excluded).
con.execute(
"INSERT INTO pfs.rvu VALUES (?,?,?,?,?,?)",
["A0001", None, "extra a", "A", 1.0, 2026],
)
con.execute(
"INSERT INTO pfs.rvu VALUES (?,?,?,?,?,?)",
["B0002", None, "extra b", "R", 1.0, 2026],
)
order: list[tuple] = []
def fake_lineage_all(con_, store_, codes):
order.append(("lineage_all", tuple(codes)))
return {c: [] for c in codes}
monkeypatch.setattr(pfs_cli, "lineage_all", fake_lineage_all)
written: list[str] = []
def fake_write_events(con_, code, rows):
order.append(("write", code))
written.append(code)
return 0
monkeypatch.setattr(pfs_cli, "write_events", fake_write_events)
real_batch = pfs_cli._batch
def recording_batch():
order.append(("batch",))
return real_batch()
monkeypatch.setattr(pfs_cli, "_batch", recording_batch)
res = runner.invoke(
app, ["pfs", "lineage", "--all-payable", "--limit", "2", "--write"]
)
assert res.exit_code == 0, res.output
assert order[0] == ("lineage_all", ("99490", "A0001"))
assert order[1] == ("batch",)
assert order[2:] == [("write", "99490"), ("write", "A0001")]
assert written == ["99490", "A0001"]
assert con.published == [True]
def test_all_payable_without_write_never_opens_the_batch(self, con, monkeypatch):
monkeypatch.setattr(
pfs_cli, "lineage_all", lambda c, s, codes: {c: [] for c in codes}
)
def fail_batch():
raise AssertionError(
"lineage --all-payable without --write must not open a RW "
"duckdb_batch connection"
)
monkeypatch.setattr(pfs_cli, "_batch", fail_batch)
res = runner.invoke(app, ["pfs", "lineage", "--all-payable"])
assert res.exit_code == 0, res.output
assert con.published == []
assert "lineage: 1 codes targeted" in res.output
def test_lineage_all_seen_targets_deleted_codes(self, con, monkeypatch):
con.execute(
"INSERT INTO pfs.rvu VALUES (?,?,?,?,?,?)",
["99343", None, "old home visit", "A", 1.0, 2022],
)
order: list[tuple] = []
def fake_lineage_all(con_, store_, codes):
order.append(("lineage_all", tuple(codes)))
return {c: [] for c in codes}
monkeypatch.setattr(pfs_cli, "lineage_all", fake_lineage_all)
monkeypatch.setattr(pfs_cli, "write_events", lambda c, code, rows: 0)
real_batch = pfs_cli._batch
monkeypatch.setattr(
pfs_cli, "_batch", lambda: (order.append(("batch",)), real_batch())[1]
)
res = runner.invoke(app, ["pfs", "lineage", "--all-seen", "--write"])
assert res.exit_code == 0, res.output
targets = order[0][1]
assert "99343" in targets and "G2058" in targets and "99999" in targets
assert order[1] == ("batch",)
assert con.published == [True]
def test_lineage_all_seen_limit_and_code_union(self, con, monkeypatch):
seen: list[tuple] = []
def fake(con_, store_, codes):
seen.append(tuple(codes))
return {c: [] for c in codes}
monkeypatch.setattr(pfs_cli, "lineage_all", fake)
res = runner.invoke(
app, ["pfs", "lineage", "--all-seen", "--limit", "1", "--code", "zzzzz"]
)
assert res.exit_code == 0, res.output
assert len(seen[0]) == 2 and "ZZZZZ" in seen[0]
def test_lineage_family_targets_curated_members(self, con, monkeypatch):
seen: list[tuple] = []
def fake(con_, store_, codes):
seen.append(tuple(codes))
return {c: [] for c in codes}
monkeypatch.setattr(pfs_cli, "lineage_all", fake)
res = runner.invoke(app, ["pfs", "lineage", "--family", "HOME-VISITS"])
assert res.exit_code == 0, res.output
assert len(seen[0]) == 20
assert "lineage: 20 codes targeted" in res.output
def test_lineage_unknown_family_is_rejected(self, con):
res = runner.invoke(app, ["pfs", "lineage", "--family", "NOPE-NOPE"])
assert res.exit_code != 0
def test_codes_for_with_no_targets_names_every_flag(self, con):
with pytest.raises(Exception, match="--all-seen"):
pfs_cli._codes_for(con, [], [], False)
def test_codes_for_all_seen_warns_unless_quiet(self, con, caplog):
import logging
with caplog.at_level(logging.WARNING):
out = pfs_cli._codes_for(con, [], [], False, all_seen=True)
assert "99490" in out and "--all-seen: targeting" in caplog.text
class TestFamilies:
def test_derives_from_tables_and_writes(self, con):
write_elements(
con,
"99439",
[
ElementRow(
"99439", 2021, "relation", "addon-of", "99490", "", "K", 1, 1, "fr"
)
],
[],
)
write_events(
con,
"G2058",
[
EventRow(
"G2058", 2021, "replaced_by", "", "99439", "K", 2, 1, "fr", True, ""
)
],
)
res = runner.invoke(app, ["pfs", "families", "--write"])
assert res.exit_code == 0, res.output
fams = read_families(con)
assert {r.code for r in fams if r.key == "CCM"} >= {"99490", "99439", "G2058"}
# Minor: --write prints the summary line by default; the
# per-family dump ("CCM" among them) is --verbose only.
assert "CCM" not in res.output
# No pfs.cpt_* rows seeded in this test — the summary line must
# still print, with cpt-named at 0 (I4: read-only-tolerant path
# also applies to the write path when the CPT tables are empty).
assert (
"families: total 3, multi-code 2, cpt-named 0, hand 1, curated 1, other 1"
in (res.output)
)
def test_verbose_prints_the_per_family_dump(self, con):
write_elements(
con,
"99439",
[
ElementRow(
"99439", 2021, "relation", "addon-of", "99490", "", "K", 1, 1, "fr"
)
],
[],
)
res = runner.invoke(app, ["pfs", "families", "--write", "--verbose"])
assert res.exit_code == 0, res.output
assert "CCM" in res.output
assert "families: total" in res.output
def _ccm_cpt_edition(self, year=2024):
section = CptSection(
sec_id="sec_ccm",
level=3,
title="Chronic Care Management Services",
path=(
"Evaluation and Management",
"Care Management Services",
"Chronic Care Management Services",
),
code_lo="99490",
code_hi="99439",
guideline="",
)
codes = (
CptCode(
code="99490",
sec_id="sec_ccm",
descriptor="Chronic care management services, first 20 minutes.",
stem="Chronic care management services",
elements=(),
tail="first 20 minutes.",
addon=False,
resequenced=False,
new=False,
revised=False,
telemedicine=False,
parent="",
mod51_exempt=False,
audio_only=False,
fda_pending=False,
pla=False,
category="I",
),
CptCode(
code="99439",
sec_id="sec_ccm",
descriptor="each additional 20 minutes.",
stem="Chronic care management services",
elements=(),
tail="each additional 20 minutes.",
addon=True,
resequenced=False,
new=False,
revised=False,
telemedicine=False,
parent="99490",
mod51_exempt=False,
audio_only=False,
fda_pending=False,
pla=False,
category="I",
),
)
return CptEdition(
year=year,
sections=(section,),
codes=codes,
instructions=(),
references=(),
crosswalks=(),
lists=(),
)
def test_feeds_cpt_inputs_when_present_and_prints_summary_line(self, con):
write_cpt_edition(con, self._ccm_cpt_edition(), "ITEM0001")
res = runner.invoke(app, ["pfs", "families", "--write"])
assert res.exit_code == 0, res.output
fams = read_families(con)
ccm = {r.code: r for r in fams if r.key == "CCM"}
assert {"99490", "99439"} <= set(ccm)
assert ccm["99490"].note == (
"Evaluation and Management > Care Management Services > "
"Chronic Care Management Services"
)
assert "families: total" in res.output
assert "cpt-named" in res.output
def test_since_spans_all_ingested_editions_not_just_the_newest(self, con):
# Ruling C12: _cpt_inputs' cpt_presence comes from a full
# SELECT code, edition_year FROM pfs.cpt_code (every edition
# write_cpt_edition has ever written), so `since` is the
# earliest edition, not clamped to whichever one is newest.
write_cpt_edition(con, self._ccm_cpt_edition(year=2019), "ITEM_2019")
write_cpt_edition(con, self._ccm_cpt_edition(year=2024), "ITEM_2024")
res = runner.invoke(app, ["pfs", "families", "--write"])
assert res.exit_code == 0, res.output
fams = read_families(con)
ccm = {r.code: r for r in fams if r.key == "CCM"}
assert ccm["99490"].since == 2019
assert ccm["99490"].until is None
def test_write_tolerates_null_rvu_description(self, con):
# #687 regression: a real pfs.rvu row can carry a NULL description
# (a corpus parsing artifact) at the current max year with a
# payable status — that row lands in the derivation scope and used
# to crash `families --write` with an AttributeError.
con.execute(
"INSERT INTO pfs.rvu VALUES (?,?,?,?,?,?)",
("Z9999", None, None, "A", 1.0, 2026),
)
res = runner.invoke(app, ["pfs", "families", "--write"])
assert res.exit_code == 0, res.output
def test_without_write_reads_and_never_takes_the_write_lock(self, con, monkeypatch):
# I4: `stack pfs families` without --write must read the replica,
# not open a RW duckdb_batch connection.
def fail_batch():
raise AssertionError(
"families without --write must not open a RW duckdb_batch connection"
)
monkeypatch.setattr(pfs_cli, "_batch", fail_batch)
read_calls = []
orig_read = pfs_cli._read
def spy_read():
read_calls.append(True)
return orig_read()
monkeypatch.setattr(pfs_cli, "_read", spy_read)
res = runner.invoke(app, ["pfs", "families"])
assert res.exit_code == 0, res.output
assert read_calls == [True]
assert con.published == []
def test_no_derived_tables_yet_is_a_friendly_no_op(self, monkeypatch):
# I4: a fresh replica with no pfs.code_element/code_event/code_family
# (nothing has run --write yet) is not a bug — print a friendly
# message and exit 0, without ever taking the write lock.
bare = duckdb.connect(":memory:")
monkeypatch.setattr(pfs_cli, "_read", lambda: bare)
def fail_batch():
raise AssertionError("must not open a RW duckdb_batch connection")
monkeypatch.setattr(pfs_cli, "_batch", fail_batch)
try:
res = runner.invoke(app, ["pfs", "families"])
assert res.exit_code == 0, res.output
assert "no derived tables yet" in res.output
finally:
bare.close()
def test_unknown_family_error_lists_hand_families_only(self, con, monkeypatch):
# M3: FAMILIES can hold ~15k derived keys once `refresh_from` has
# run — the error text must stay short and human-scannable.
pfs_cli.FAMILIES["ZZZZ-99999"] = pfs_cli.FAMILIES["CCM"]
res = runner.invoke(
app, ["pfs", "elements", "--family", "NOPE", "--no-llm", "--dry-run"]
)
assert res.exit_code != 0
assert "ZZZZ-99999" not in res.output
for key in pfs_cli.HAND_FAMILIES:
assert key in res.output
class TestCptIngest:
def test_dry_run_never_opens_batch(self, con, monkeypatch):
seen = {}
def fake_ingest(store, con_, *, years=None, dry_run=False):
seen["store"] = store
seen["con"] = con_
seen["years"] = years
seen["dry_run"] = dry_run
return {2024: {"sections": 3, "codes": 5}}
monkeypatch.setattr(pfs_cli, "cpt_ingest", fake_ingest)
def fail_batch():
raise AssertionError("--dry-run must not open a RW duckdb_batch connection")
monkeypatch.setattr(pfs_cli, "_batch", fail_batch)
res = runner.invoke(
app, ["pfs", "cpt-ingest", "--edition", "2024", "--dry-run"]
)
assert res.exit_code == 0, res.output
assert seen["dry_run"] is True
assert seen["con"] is None
assert seen["years"] == [2024]
assert "2024: sections=3, codes=5" in res.output
assert con.published == []
def test_all_flag_passes_years_none(self, con, monkeypatch):
seen = {}
monkeypatch.setattr(
pfs_cli,
"cpt_ingest",
lambda store, con_, *, years=None, dry_run=False: (
seen.update(years=years) or {}
),
)
res = runner.invoke(app, ["pfs", "cpt-ingest", "--all", "--dry-run"])
assert res.exit_code == 0, res.output
assert seen["years"] is None
def test_requires_edition_or_all(self, con):
res = runner.invoke(app, ["pfs", "cpt-ingest"])
assert res.exit_code != 0
def test_write_path_opens_batch_ensures_tables_and_publishes(
self, con, monkeypatch
):
seen = {}
def fake_ingest(store, con_, *, years=None, dry_run=False):
seen["con"] = con_
seen["dry_run"] = dry_run
return {2024: {"sections": 1, "codes": 2}}
monkeypatch.setattr(pfs_cli, "cpt_ingest", fake_ingest)
res = runner.invoke(app, ["pfs", "cpt-ingest", "--edition", "2024"])
assert res.exit_code == 0, res.output
assert seen["dry_run"] is False
assert seen["con"] is not None
assert "2024: sections=1, codes=2" in res.output
assert con.published == [True]
class TestGuidance:
def _row(self, family="CCM", code="99490"):
return GuidanceRow(
family, code, "cfr", "42 CFR 410.78(a)(3)", "SEC", "K", 398, 32389
)
def test_write_builds_writes_and_publishes(self, con, monkeypatch):
seen = {}
def fake_build(con_, store_, codes, *, family):
seen["con"] = con_
seen["store"] = store_
seen["codes"] = codes
seen["family"] = family
return [self._row(family=family)]
monkeypatch.setattr(pfs_cli, "build_guidance", fake_build)
res = runner.invoke(app, ["pfs", "guidance", "--family", "ccm", "--write"])
assert res.exit_code == 0, res.output
assert seen["family"] == "CCM"
assert seen["con"] is not None
assert seen["store"] is not None
assert set(seen["codes"]) == set(pfs_cli.HAND_FAMILIES["CCM"].codes)
got = read_guidance(con, "CCM")
assert [r.code for r in got] == ["99490"]
assert con.published == [True]
assert "CCM cfr 42 CFR 410.78(a)(3) SEC ← K ¶398" in res.output
def test_write_scopes_per_family(self, con, monkeypatch):
monkeypatch.setattr(
pfs_cli,
"build_guidance",
lambda con_, store_, codes, *, family: [self._row(family=family)],
)
res = runner.invoke(
app, ["pfs", "guidance", "--family", "CCM", "--family", "APCM", "--write"]
)
assert res.exit_code == 0, res.output
assert [r.family for r in read_guidance(con, "CCM")] == ["CCM"]
assert [r.family for r in read_guidance(con, "APCM")] == ["APCM"]
def test_without_write_reads_and_never_takes_the_write_lock(self, con, monkeypatch):
write_guidance(con, "CCM", [self._row()])
def fail_batch():
raise AssertionError("must not open a RW duckdb_batch connection")
monkeypatch.setattr(pfs_cli, "_batch", fail_batch)
res = runner.invoke(app, ["pfs", "guidance", "--family", "CCM"])
assert res.exit_code == 0, res.output
assert "CCM cfr 42 CFR 410.78(a)(3) SEC ← K ¶398" in res.output
assert con.published == []
def test_no_derived_guidance_yet_is_a_friendly_no_op(self, monkeypatch):
# I4: a fresh replica with no pfs.code_guidance (guidance --write
# hasn't run yet) is not a bug — print a friendly message per
# family and exit 0, without ever taking the write lock.
bare = duckdb.connect(":memory:")
monkeypatch.setattr(pfs_cli, "_read", lambda: bare)
def fail_batch():
raise AssertionError("must not open a RW duckdb_batch connection")
monkeypatch.setattr(pfs_cli, "_batch", fail_batch)
try:
res = runner.invoke(app, ["pfs", "guidance", "--family", "CCM"])
assert res.exit_code == 0, res.output
assert "no derived guidance yet" in res.output
finally:
bare.close()
def test_unknown_family_error_lists_hand_families_only(self, con, monkeypatch):
pfs_cli.FAMILIES["ZZZZ-99999"] = pfs_cli.FAMILIES["CCM"]
res = runner.invoke(app, ["pfs", "guidance", "--family", "NOPE", "--write"])
assert res.exit_code != 0
assert "ZZZZ-99999" not in res.output
for key in pfs_cli.HAND_FAMILIES:
assert key in res.output
def test_unknown_key_among_several_errors_before_any_batch(self, con, monkeypatch):
# Every --family key must be resolved and validated up front — an
# unknown key later in the list must fail before build_guidance
# runs for any family and before the write lock is ever opened.
built: list[str] = []
monkeypatch.setattr(
pfs_cli,
"build_guidance",
lambda con_, store_, codes, *, family: built.append(family) or [],
)
def fail_batch():
raise AssertionError("must not open a RW duckdb_batch connection")
monkeypatch.setattr(pfs_cli, "_batch", fail_batch)
res = runner.invoke(
app, ["pfs", "guidance", "--family", "CCM", "--family", "NOPE", "--write"]
)
assert res.exit_code != 0
assert built == []
def test_write_builds_every_family_before_opening_the_batch(self, con, monkeypatch):
order: list[str] = []
def fake_build(con_, store_, codes, *, family):
order.append("build")
return [self._row(family=family)]
real_batch = pfs_cli._batch
def recording_batch():
order.append("batch")
return real_batch()
monkeypatch.setattr(pfs_cli, "build_guidance", fake_build)
monkeypatch.setattr(pfs_cli, "_batch", recording_batch)
res = runner.invoke(
app, ["pfs", "guidance", "--family", "CCM", "--family", "APCM", "--write"]
)
assert res.exit_code == 0, res.output
assert order == ["build", "build", "batch"]
def test_requires_family(self, con):
res = runner.invoke(app, ["pfs", "guidance"])
assert res.exit_code != 0
class TestReaction:
def _row(
self, family="CCM", period="CMS-2023-0121", period_kind="docket", year=2023
):
return ReactionRow(family, period, period_kind, year, 5, 20, 1, 0, 0, 1, "[]")
def test_write_builds_writes_and_publishes(self, con, monkeypatch):
seen = {}
def fake_series(
engine,
store_,
family,
codes,
*,
classify=None,
stance_sample_n=0,
name=None,
):
seen["engine"] = engine
seen["store"] = store_
seen["family"] = family
seen["codes"] = codes
seen["classify"] = classify
seen["n"] = stance_sample_n
seen["name"] = name
return [self._row(family=family)]
monkeypatch.setattr(pfs_cli, "reaction_series", fake_series)
monkeypatch.setattr(pfs_cli, "_engine", lambda: "ENGINE")
res = runner.invoke(app, ["pfs", "reaction", "--family", "ccm", "--write"])
assert res.exit_code == 0, res.output
assert seen["family"] == "CCM"
assert seen["engine"] == "ENGINE"
assert seen["classify"] is None # no --stance-sample: no LLM at all
assert seen["n"] == 0
assert seen["name"] == pfs_cli.HAND_FAMILIES["CCM"].name
assert set(seen["codes"]) == set(pfs_cli.HAND_FAMILIES["CCM"].codes)
got = read_reaction(con, "CCM")
assert [r.period for r in got] == ["CMS-2023-0121"]
assert con.published == [True]
assert "CCM CMS-2023-0121 (2023) 5/20 commenters" in res.output
def test_code_makes_a_single_code_pseudo_family_with_no_display_name(
self, con, monkeypatch
):
seen = {}
def fake_series(
engine,
store_,
family,
codes,
*,
classify=None,
stance_sample_n=0,
name=None,
):
seen["family"] = family
seen["codes"] = codes
seen["name"] = name
return [self._row(family=family)]
monkeypatch.setattr(pfs_cli, "reaction_series", fake_series)
monkeypatch.setattr(pfs_cli, "_engine", lambda: "ENGINE")
res = runner.invoke(app, ["pfs", "reaction", "--code", "g2211", "--write"])
assert res.exit_code == 0, res.output
assert seen["family"] == "G2211"
assert seen["codes"] == ("G2211",)
assert seen["name"] is None
assert [r.family for r in read_reaction(con, "G2211")] == ["G2211"]
def test_stance_sample_builds_the_classifier(self, con, monkeypatch):
seen = {}
monkeypatch.setattr(
pfs_cli,
"reaction_series",
lambda engine, store_, family, codes, *, classify=None, stance_sample_n=0, name=None: ( # noqa: E501
seen.update(classify=classify, n=stance_sample_n) or []
),
)
monkeypatch.setattr(pfs_cli, "_engine", lambda: "ENGINE")
monkeypatch.setattr(pfs_cli, "_classifier", lambda: "CLASSIFIER")
res = runner.invoke(
app, ["pfs", "reaction", "--family", "CCM", "--stance-sample", "5"]
)
assert res.exit_code == 0, res.output
assert seen["classify"] == "CLASSIFIER"
assert seen["n"] == 5
def test_write_computes_classifier_and_series_before_opening_the_batch(
self, con, monkeypatch
):
# Ruling A13: the classifier and every family's series must be
# fully computed BEFORE the DuckDB write lock is taken — the
# batch block only writes and publishes.
order: list[str] = []
def fake_classifier():
order.append("classifier")
return "CLASSIFIER"
def fake_series(
engine,
store_,
family,
codes,
*,
classify=None,
stance_sample_n=0,
name=None,
):
order.append("series")
assert classify == "CLASSIFIER" # already built by the time series runs
return [self._row(family=family)]
real_batch = pfs_cli._batch
def recording_batch():
order.append("batch")
return real_batch()
monkeypatch.setattr(pfs_cli, "_classifier", fake_classifier)
monkeypatch.setattr(pfs_cli, "reaction_series", fake_series)
monkeypatch.setattr(pfs_cli, "_engine", lambda: "ENGINE")
monkeypatch.setattr(pfs_cli, "_batch", recording_batch)
res = runner.invoke(
app,
["pfs", "reaction", "--family", "CCM", "--stance-sample", "5", "--write"],
)
assert res.exit_code == 0, res.output
assert order == ["classifier", "series", "batch"]
def test_without_write_never_opens_a_batch_connection(self, con, monkeypatch):
monkeypatch.setattr(
pfs_cli,
"reaction_series",
lambda engine, store_, family, codes, *, classify=None, stance_sample_n=0, name=None: [ # noqa: E501
self._row(family=family)
],
)
monkeypatch.setattr(pfs_cli, "_engine", lambda: "ENGINE")
def fail_batch():
raise AssertionError("must not open a RW duckdb_batch connection")
monkeypatch.setattr(pfs_cli, "_batch", fail_batch)
res = runner.invoke(app, ["pfs", "reaction", "--family", "CCM"])
assert res.exit_code == 0, res.output
assert "CCM CMS-2023-0121 (2023) 5/20 commenters" in res.output
assert con.published == [] # nothing persisted, batch never opened
def test_unknown_family_error_lists_hand_families_only(self, con, monkeypatch):
pfs_cli.FAMILIES["ZZZZ-99999"] = pfs_cli.FAMILIES["CCM"]
res = runner.invoke(app, ["pfs", "reaction", "--family", "NOPE", "--write"])
assert res.exit_code != 0
assert "ZZZZ-99999" not in res.output
for key in pfs_cli.HAND_FAMILIES:
assert key in res.output
def test_requires_family_or_code(self, con):
res = runner.invoke(app, ["pfs", "reaction"])
assert res.exit_code != 0
class TestReview:
def test_lists_queue(self, con):
from pfs.codetables import ReviewRow
write_elements(
con, "G0556", [], [ReviewRow("G0556", "Odd line;", "", "", "K", 9)]
)
res = runner.invoke(app, ["pfs", "review"])
assert res.exit_code == 0 and "G0556" in res.output and "Odd line" in res.output
def test_source_shown_when_present(self, con):
# #703: CPT-sourced review lines are distinguishable from FR ones.
from pfs.codetables import ReviewRow
write_elements(
con,
"G0556",
[],
[
ReviewRow("G0556", "Odd fr line;", "", "", "K", 9, "fr"),
ReviewRow("G0556", "Odd cpt line;", "", "", "GQGTPGYV", 0, "cpt"),
],
)
res = runner.invoke(app, ["pfs", "review"])
assert res.exit_code == 0, res.output
assert "(fr)" in res.output and "(cpt)" in res.output
def test_code_filter_scopes_the_query(self, con):
from pfs.codetables import ReviewRow
write_elements(
con, "G0556", [], [ReviewRow("G0556", "Odd line;", "", "", "K", 9, "fr")]
)
write_elements(
con, "99490", [], [ReviewRow("99490", "Other line;", "", "", "K", 3, "fr")]
)
res = runner.invoke(app, ["pfs", "review", "--code", "g0556"])
assert res.exit_code == 0, res.output
assert "Odd line" in res.output and "Other line" not in res.output
class TestExposure:
def test_write_builds_whole_calendar_and_publishes(self, con, monkeypatch):
from pfs.codetables import read_exposures
res = runner.invoke(app, ["pfs", "exposure", "--write"])
assert res.exit_code == 0, res.output
rows = read_exposures(con)
got = {(r.code, r.year, r.kind) for r in rows}
# 99490 appears (payable) in 2026 after being absent; G2058's only year is
# 2020 so it ends 2021; 99999 is B-status → never payable → no rows.
assert ("99490", 2026, "becomes-payable") in got
assert ("G2058", 2021, "ends") in got
assert not any(r.code == "99999" for r in rows)
assert con.published == [True]
assert "99490 2026-01-01 becomes-payable" in res.output
assert "exposures:" in res.output
def test_filters_only_affect_printing(self, con, monkeypatch):
from pfs.codetables import read_exposures
res = runner.invoke(app, ["pfs", "exposure", "--write", "--code", "g2058"])
assert res.exit_code == 0, res.output
assert len({r.code for r in read_exposures(con)}) == 2 # whole calendar written
assert "G2058" in res.output and "99490" not in res.output
def test_without_write_reads_and_never_takes_the_write_lock(self, con, monkeypatch):
runner.invoke(app, ["pfs", "exposure", "--write"])
con.published.clear()
def fail_batch():
raise AssertionError("must not open a RW duckdb_batch connection")
monkeypatch.setattr(pfs_cli, "_batch", fail_batch)
res = runner.invoke(app, ["pfs", "exposure", "--code", "99490"])
assert res.exit_code == 0, res.output
assert "99490 2026-01-01 becomes-payable" in res.output
assert con.published == []
def test_controls_lists_quiet_codes_in_the_same_band(self, con, monkeypatch):
# two more A-status codes in 2026 in the same (single) band, one of
# which is itself exposed that year → only the quiet one is a control
con.executemany(
"INSERT INTO pfs.rvu VALUES (?,?,?,?,?,?)",
[
# 11111 is payable across the whole file span → never treated
*[("11111", None, "quiet", "A", 1.0, y) for y in range(2020, 2027)],
("22222", None, "new", "A", 1.0, 2026), # becomes payable 2026 itself
],
)
runner.invoke(app, ["pfs", "exposure", "--write"])
res = runner.invoke(
app,
["pfs", "exposure", "--code", "99490", "--year", "2026", "--controls", "5"],
)
assert res.exit_code == 0, res.output
assert "11111 A band" in res.output
assert "22222" not in res.output
assert "controls for 99490 @ 2026" in res.output
def test_controls_requires_one_code_and_year(self, con):
res = runner.invoke(app, ["pfs", "exposure", "--controls", "5"])
assert res.exit_code != 0
def test_no_calendar_yet_is_a_friendly_no_op(self, monkeypatch):
bare = duckdb.connect(":memory:")
monkeypatch.setattr(pfs_cli, "_read", lambda: bare)
def fail_batch():
raise AssertionError("must not open a RW duckdb_batch connection")
monkeypatch.setattr(pfs_cli, "_batch", fail_batch)
try:
res = runner.invoke(app, ["pfs", "exposure"])
assert res.exit_code == 0, res.output
assert "no exposure calendar yet" in res.output
finally:
bare.close()
class TestUtilization:
_REC = {
"Rndrng_Prvdr_Geo_Lvl": "National",
"HCPCS_Cd": "99490",
"HCPCS_Desc": "CCM first 20 min",
"HCPCS_Drug_Ind": "N",
"Place_Of_Srvc": "O",
"Tot_Rndrng_Prvdrs": "10",
"Tot_Benes": "100",
"Tot_Srvcs": "1000",
"Tot_Bene_Day_Srvcs": "990",
"Avg_Sbmtd_Chrg": "100",
"Avg_Mdcr_Alowd_Amt": "60",
"Avg_Mdcr_Pymt_Amt": "45",
"Avg_Mdcr_Stdzd_Amt": "44",
}
@pytest.fixture
def env(self, con, monkeypatch, tmp_path):
from bib.store import Store
store = Store(":memory:", storage_dir=tmp_path / "storage")
monkeypatch.setattr(pfs_cli, "_store", lambda: store)
monkeypatch.setattr(
pfs_cli,
"utilization_cache_path",
lambda y, root=None: tmp_path / f"mup-phy-geo-{y}.json",
)
fetched: list[int] = []
def fake_fetch(client, y, **kw):
fetched.append(y)
return [self._REC, dict(self._REC, Place_Of_Srvc="F", Tot_Srvcs="5")]
monkeypatch.setattr(pfs_cli, "fetch_utilization_year", fake_fetch)
monkeypatch.setattr(pfs_cli, "_http", lambda: MagicMock())
try:
yield store, fetched, tmp_path
finally:
store.close()
def test_write_loads_cites_logs_and_publishes(self, con, env):
store, fetched, tmp_path = env
res = runner.invoke(app, ["pfs", "utilization", "--write", "--year", "2023"])
assert res.exit_code == 0, res.output
assert fetched == [2023]
assert con.execute(
"SELECT count(*), sum(n_services) FROM pfs.utilization WHERE year = 2023"
).fetchone() == (2, 1005.0)
key = con.execute("SELECT DISTINCT item_key FROM pfs.utilization").fetchone()[0]
item = store.get(key)
assert "CY2023" in item.title and "year:2023" in item.tags
log = con.execute(
"SELECT module, table_name, rows, pincite_key, source_file FROM cms.ingest_log"
).fetchone()
assert log[:4] == ("pfs.utilization", "pfs.utilization", 2, key)
assert log[4].endswith("mup-phy-geo-2023.json")
assert (tmp_path / "mup-phy-geo-2023.json").exists()
assert con.published == [True]
assert "2023: 2 records → 2 national rows" in res.output
def test_write_is_idempotent_and_offline_reads_the_cache(self, con, env):
store, fetched, tmp_path = env
runner.invoke(app, ["pfs", "utilization", "--write", "--year", "2023"])
res = runner.invoke(
app, ["pfs", "utilization", "--write", "--year", "2023", "--offline"]
)
assert res.exit_code == 0, res.output
assert fetched == [2023] # the offline run never fetched
assert con.execute("SELECT count(*) FROM pfs.utilization").fetchone()[0] == 2
def test_unknown_year_is_rejected_before_any_fetch(self, con, env):
store, fetched, _ = env
res = runner.invoke(app, ["pfs", "utilization", "--write", "--year", "1999"])
assert res.exit_code != 0
assert fetched == []
def test_code_reads_series_without_the_write_lock(self, con, env, monkeypatch):
runner.invoke(app, ["pfs", "utilization", "--write", "--year", "2023"])
con.published.clear()
def fail_batch():
raise AssertionError("must not open a RW duckdb_batch connection")
monkeypatch.setattr(pfs_cli, "_batch", fail_batch)
res = runner.invoke(app, ["pfs", "utilization", "--code", "99490"])
assert res.exit_code == 0, res.output
assert "99490 2023 services 1,005" in res.output
assert con.published == []
def test_no_table_yet_is_a_friendly_no_op(self, monkeypatch):
bare = duckdb.connect(":memory:")
monkeypatch.setattr(pfs_cli, "_read", lambda: bare)
try:
res = runner.invoke(app, ["pfs", "utilization", "--code", "99490"])
assert res.exit_code == 0, res.output
assert "no utilization table yet" in res.output
finally:
bare.close()
class TestFamiliesCurated:
def test_families_write_applies_curated(self, con, monkeypatch):
from pfs.codetables import FamilyRow
derived = [
FamilyRow(
"NEW-PATIENT", "New Patient", "99341", "base", 2015, None, "", 0, ""
)
]
monkeypatch.setattr(pfs_cli, "_derive_rows", lambda c: derived)
res = runner.invoke(app, ["pfs", "families", "--write"])
assert res.exit_code == 0, res.output
fams = read_families(con)
assert [r.key for r in fams if r.code == "99341"] == ["HOME-VISITS"]
assert len([r for r in fams if r.key == "HOME-VISITS"]) == 20
assert "curated 1" in res.output
def test_asker_builds_json_extractor_on_instruct_model(monkeypatch):
from llm import config as llm_config
from llm import pool as llm_pool
from llm.structured import JsonExtractor
class Cfg:
instruct_model = "qwen-test"
chat_num_ctx = 1024
class FakePool:
def check(self, model):
return ["http://h"]
monkeypatch.setattr(llm_config, "load", lambda: Cfg())
monkeypatch.setattr(
llm_pool.HostPool, "from_config", classmethod(lambda c, cfg: FakePool())
)
asker = pfs_cli._asker()
assert isinstance(asker, JsonExtractor) and asker.model == "qwen-test"
class TestRounds:
def _row(self, **kw):
from pfs.codetables import RoundRow
base = dict(
family="HOME-VISITS",
code="99341",
rule_year=2023,
item_key="K",
rule_kind="final",
p_id=1,
page=2,
source="fr-para",
table_title="",
ruc_work_rvu=1.0,
ruc_percentile="25th",
survey_intra_min=None,
survey_total_min=None,
comparators="",
surveying_societies="",
requested_work_rvu=None,
proposed_work_rvu=1.0,
final_work_rvu=1.0,
decision="accept",
rationale_p_id=0,
rationale_quote="",
pe_note="",
extractor="regex",
)
return RoundRow(**{**base, **kw})
def _patch(self, monkeypatch, order, counts=None):
rows = [
self._row(),
self._row(rule_year=2008, rule_kind="final"),
self._row(code="99342", rule_year=2023, field_cites='{"decision": 1}'),
]
seen = {}
def fake(con, store, fam, **kw):
order.append("family_rounds")
seen.update(kw, key=fam.key)
return rows, counts or {
"done": 2,
"skipped": 1,
"failed": 0,
"failed_pending": 3,
}
monkeypatch.setattr(pfs_cli, "family_rounds", fake)
monkeypatch.setattr(pfs_cli, "_asker", lambda: "ASK")
monkeypatch.setattr(
pfs_cli, "ledger_path", lambda t, f: Path("/nonexistent") / f"{t}-{f}"
)
return seen
def test_rounds_cli_writes_after_extraction(self, con, monkeypatch):
order = []
seen = self._patch(monkeypatch, order)
orig_write = pfs_cli.write_rounds
def write(c, fam, rows):
order.append("write_rounds")
return orig_write(c, fam, rows)
class _B:
def __enter__(self):
order.append("_batch")
return con._real
def __exit__(self, *a):
return False
monkeypatch.setattr(pfs_cli, "write_rounds", write)
monkeypatch.setattr(pfs_cli, "_batch", lambda: _B())
monkeypatch.setattr(pfs_cli, "_publish", lambda: order.append("_publish"))
res = runner.invoke(
app,
[
"pfs",
"rounds",
"--family",
"home-visits",
"--limit",
"5",
"--retry-failed",
"--write",
],
)
assert res.exit_code == 0, res.output
assert order == ["family_rounds", "_batch", "write_rounds", "_publish"]
assert seen["ask"] == "ASK" and seen["limit"] == 5 and seen["retry_failed"]
assert seen["key"] == "HOME-VISITS" and not seen["tables_only"]
assert "CY2008 final: 1 rounds" in res.output
assert "CY2023 final: 2 rounds" in res.output
assert "done 2, skipped 1, failed 0, failed_pending 3" in res.output
assert "wrote 3 rounds" in res.output
got = read_rounds(con._real, "HOME-VISITS")
assert len(got) == 3
assert {r.code: r.field_cites for r in got}["99342"] == '{"decision": 1}'
def test_rounds_without_write_writes_nothing(self, con, monkeypatch):
order = []
self._patch(monkeypatch, order)
monkeypatch.setattr(pfs_cli, "_batch", lambda: 1 / 0)
res = runner.invoke(app, ["pfs", "rounds", "--family", "HOME-VISITS"])
assert res.exit_code == 0, res.output
assert order == ["family_rounds"] and "wrote" not in res.output
@pytest.mark.parametrize("flag", ["--no-llm", "--tables-only"])
def test_no_model_flags_build_no_asker(self, con, monkeypatch, flag):
seen = self._patch(monkeypatch, [])
monkeypatch.setattr(pfs_cli, "_asker", lambda: 1 / 0)
monkeypatch.setattr(pfs_cli, "_model_name", lambda: "qwen-x")
res = runner.invoke(app, ["pfs", "rounds", "--family", "HOME-VISITS", flag])
assert res.exit_code == 0, res.output
assert seen["ask"] is None and seen["tables_only"] == (flag == "--tables-only")
# the finished ledger is replayed, so a --write cannot delete model rows
assert seen["replay_model"] == "qwen-x"
def test_model_run_does_not_replay(self, con, monkeypatch):
seen = self._patch(monkeypatch, [])
monkeypatch.setattr(pfs_cli, "_model_name", lambda: 1 / 0)
res = runner.invoke(app, ["pfs", "rounds", "--family", "HOME-VISITS"])
assert res.exit_code == 0, res.output
assert seen["replay_model"] is None
def test_model_name_reads_config_without_a_pool(self, monkeypatch):
from llm import config as llm_config
class Cfg:
instruct_model = "qwen-test"
monkeypatch.setattr(llm_config, "load", lambda: Cfg())
assert pfs_cli._model_name() == "qwen-test"
def test_rounds_unknown_family(self, con):
res = runner.invoke(app, ["pfs", "rounds", "--family", "NOPE"])
assert res.exit_code != 0 and "unknown curated family" in res.output
def test_rounds_dry_run_makes_no_model_calls(self, con, monkeypatch):
monkeypatch.setattr(
pfs_cli, "rounds_inventory", lambda store, fam: ([{}, {}, {}], [1, 2])
)
monkeypatch.setattr(pfs_cli, "_asker", lambda: 1 / 0)
monkeypatch.setattr(pfs_cli, "family_rounds", lambda *a, **k: 1 / 0)
monkeypatch.setattr(pfs_cli, "_batch", lambda: 1 / 0)
res = runner.invoke(
app, ["pfs", "rounds", "--family", "HOME-VISITS", "--dry-run"]
)
assert res.exit_code == 0, res.output
assert "3 candidate paragraphs, 2 table rows; no model calls made" in res.output
class TestOrigin:
def _row(self, **kw):
from pfs.codetables import OriginRow
base = dict(
family="HOME-VISITS",
code="99341",
kind="statute",
reference="Section 1861(m) of the Act",
year=None,
summary="A paraphrase.",
cite_kind="fr_para",
item_key="K",
p_id=2,
page_label="100",
locator="FR p. 100, ¶2",
extractor="m",
)
return OriginRow(**{**base, **kw})
def _patch(self, monkeypatch, order):
rows = [
self._row(),
self._row(code="99350", kind="regulation", cite_kind="cfr"),
self._row(code="99342", cite_kind="cpt_book", item_key="B", p_id=0),
]
seen = {}
def fake(con, store, fam, **kw):
order.append("family_origin")
seen.update(kw, key=fam.key)
return rows, {"done": 2, "skipped": 1, "failed": 0, "failed_pending": 3}
monkeypatch.setattr(pfs_cli, "family_origin", fake)
monkeypatch.setattr(pfs_cli, "_asker", lambda: "ASK")
monkeypatch.setattr(
pfs_cli,
"resolve_book",
lambda store, key: ("Title " + key, Path("/x") / f"{key}.epub"),
)
monkeypatch.setattr(
pfs_cli, "ledger_path", lambda t, f: Path("/nonexistent") / f"{t}-{f}"
)
return seen
def test_origin_cli_write_after_extraction(self, con, monkeypatch):
from pfs.codetables import read_origin, write_origin
order = []
seen = self._patch(monkeypatch, order)
orig_write = write_origin
def write(c, fam, rows):
order.append("write_origin")
return orig_write(c, fam, rows)
class _B:
def __enter__(self):
order.append("_batch")
return con._real
def __exit__(self, *a):
return False
monkeypatch.setattr(pfs_cli, "write_origin", write)
monkeypatch.setattr(pfs_cli, "_batch", lambda: _B())
monkeypatch.setattr(pfs_cli, "_publish", lambda: order.append("_publish"))
res = runner.invoke(
app,
["pfs", "origin", "--family", "home-visits", "--limit", "4", "--write"],
)
assert res.exit_code == 0, res.output
assert order == ["family_origin", "_batch", "write_origin", "_publish"]
assert (
seen["ask"] == "ASK" and seen["limit"] == 4 and seen["key"] == "HOME-VISITS"
)
assert seen["books"] == [
("3ANRT854", "Title 3ANRT854", Path("/x/3ANRT854.epub"))
]
assert seen["replay_model"] is None
assert "statute: 2" in res.output and "regulation: 1" in res.output
assert "cpt_book: 1" in res.output and "fr_para: 1" in res.output
assert "done 2, skipped 1, failed 0, failed_pending 3" in res.output
assert "wrote 3 origins" in res.output
assert len(read_origin(con._real, "HOME-VISITS")) == 3
def test_origin_without_write_writes_nothing(self, con, monkeypatch):
order = []
seen = self._patch(monkeypatch, order)
monkeypatch.setattr(pfs_cli, "_batch", lambda: 1 / 0)
res = runner.invoke(
app,
["pfs", "origin", "--family", "HOME-VISITS", "--book", "A", "--book", "B"],
)
assert res.exit_code == 0, res.output
assert order == ["family_origin"] and "wrote" not in res.output
assert [b[0] for b in seen["books"]] == ["A", "B"]
def test_no_llm_replays_the_ledger(self, con, monkeypatch):
seen = self._patch(monkeypatch, [])
monkeypatch.setattr(pfs_cli, "_asker", lambda: 1 / 0)
monkeypatch.setattr(pfs_cli, "_model_name", lambda: "qwen-x")
res = runner.invoke(
app, ["pfs", "origin", "--family", "HOME-VISITS", "--no-llm"]
)
assert res.exit_code == 0, res.output
assert seen["ask"] is None and seen["replay_model"] == "qwen-x"
def test_no_model_host_falls_back_to_replay(self, con, monkeypatch):
seen = self._patch(monkeypatch, [])
def down():
raise RuntimeError("no Ollama host serves qwen")
monkeypatch.setattr(pfs_cli, "_asker", down)
monkeypatch.setattr(pfs_cli, "_model_name", lambda: "qwen-x")
res = runner.invoke(app, ["pfs", "origin", "--family", "HOME-VISITS"])
assert res.exit_code == 0, res.output
assert seen["ask"] is None and seen["replay_model"] == "qwen-x"
assert "replaying the ledger" in res.output
def test_origin_unknown_family_and_missing_book(self, con, monkeypatch):
res = runner.invoke(app, ["pfs", "origin", "--family", "NOPE"])
assert res.exit_code != 0 and "unknown curated family" in res.output
self._patch(monkeypatch, [])
monkeypatch.setattr(pfs_cli, "resolve_book", lambda store, key: None)
res = runner.invoke(app, ["pfs", "origin", "--family", "HOME-VISITS"])
assert res.exit_code != 0 and "no EPUB attachment" in res.output
def test_origin_dry_run_makes_no_model_calls(self, con, monkeypatch):
self._patch(monkeypatch, [])
monkeypatch.setattr(
pfs_cli,
"origin_inventory",
lambda store, fam, books: (
[{"unit": "a"}, {"unit": "b"}],
{"3ANRT854": ["12", "13", "20"]},
),
)
monkeypatch.setattr(pfs_cli, "_asker", lambda: 1 / 0)
monkeypatch.setattr(pfs_cli, "family_origin", lambda *a, **k: 1 / 0)
monkeypatch.setattr(pfs_cli, "_batch", lambda: 1 / 0)
res = runner.invoke(
app, ["pfs", "origin", "--family", "HOME-VISITS", "--dry-run"]
)
assert res.exit_code == 0, res.output
assert "2 candidate Federal Register paragraphs" in res.output
assert "3ANRT854: 3 pages" in res.output and "no model calls made" in res.output