drive-and-save / test_core.py
apingali
fix(drive-and-save): consistent cheapest-bar color, graceful LLM-failure in build_ask, suppressed-row grounding test
482f642
Raw
History Blame Contribute Delete
16.6 kB
"""test_core.py β€” TDD tests for core.py (Drive & Save).
Run with: pytest test_core.py -v
"""
import h3
import pandas as pd
import pytest
import core
import data_source
# ---------------------------------------------------------------------------
# Part A β€” config loading
# ---------------------------------------------------------------------------
def test_load_config_reads_yaml():
cfg = core.load_config()
assert cfg["h3"]["resolution"] == 8
assert cfg["distance"]["avg_speed_kmh"] > 0
assert cfg["ranking"]["min_hospitals"] >= 1
assert cfg["funnel"]["cta_base_url"].startswith("https://")
# ---------------------------------------------------------------------------
# Part B β€” H3 distance with haversine fallback
# ---------------------------------------------------------------------------
CFG = core.load_config()
BOULDER = h3.latlng_to_cell(40.0150, -105.2705, 8)
DENVER = h3.latlng_to_cell(39.7392, -104.9903, 8)
def test_grid_distance_km_same_cell_is_zero():
assert core.grid_distance_km(DENVER, DENVER, CFG) == 0.0
def test_grid_distance_km_boulder_denver_is_realistic():
km = core.grid_distance_km(BOULDER, DENVER, CFG)
assert 30 <= km <= 50
def test_grid_distance_km_falls_back_on_h3_error(monkeypatch):
def boom(*args, **kwargs):
raise ValueError("cells too far apart")
monkeypatch.setattr(h3, "grid_distance", boom)
km = core.grid_distance_km(BOULDER, DENVER, CFG)
assert 30 <= km <= 50
# ---------------------------------------------------------------------------
# Part C β€” drive-time estimate
# ---------------------------------------------------------------------------
def test_drive_minutes_zero_distance():
assert core.drive_minutes(0.0, CFG) == 0
def test_drive_minutes_rounds_to_nearest_five():
m = core.drive_minutes(39.0, CFG)
assert m % 5 == 0
assert 30 <= m <= 50
def test_drive_minutes_monotonic():
assert core.drive_minutes(80.0, CFG) > core.drive_minutes(20.0, CFG)
# ---------------------------------------------------------------------------
# Part D β€” ranking, suppression, savings
# ---------------------------------------------------------------------------
SPRINGS = h3.latlng_to_cell(38.8339, -104.8214, 8)
def _fixtures():
medians = pd.DataFrame([
{"state": "CO", "procedure_code": "45378", "procedure_name": "Diagnostic colonoscopy",
"metro": "Denver", "median_price": 2267, "n_hospitals": 12},
{"state": "CO", "procedure_code": "45378", "procedure_name": "Diagnostic colonoscopy",
"metro": "Boulder", "median_price": 335, "n_hospitals": 5},
{"state": "CO", "procedure_code": "45378", "procedure_name": "Diagnostic colonoscopy",
"metro": "Colorado Springs", "median_price": 1122, "n_hospitals": 7},
{"state": "CO", "procedure_code": "45378", "procedure_name": "Diagnostic colonoscopy",
"metro": "Tiny Town", "median_price": 50, "n_hospitals": 1},
])
metros = pd.DataFrame([
{"state": "CO", "metro": "Denver", "h3_cell": DENVER},
{"state": "CO", "metro": "Boulder", "h3_cell": BOULDER},
{"state": "CO", "metro": "Colorado Springs", "h3_cell": SPRINGS},
{"state": "CO", "metro": "Tiny Town", "h3_cell": SPRINGS},
])
return medians, metros
def test_rank_metros_picks_cheapest_non_suppressed():
medians, metros = _fixtures()
r = core.rank_metros(medians, metros, "45378", "Denver", CFG)
assert r.home_metro == "Denver"
assert r.home_median == 2267
assert r.cheapest_metro == "Boulder"
assert r.cheapest_median == 335
assert r.savings == 2267 - 335
assert round(r.savings_pct) == 85
assert r.drive_min_to_cheapest >= 30
assert r.home_is_cheapest is False
def test_rank_metros_suppresses_low_n():
medians, metros = _fixtures()
r = core.rank_metros(medians, metros, "45378", "Denver", CFG)
visible = [row.metro for row in r.rows if not row.suppressed]
assert "Tiny Town" not in visible
assert {"Denver", "Boulder", "Colorado Springs"}.issubset(set(visible))
def test_rank_metros_home_is_cheapest_branch():
medians, metros = _fixtures()
r = core.rank_metros(medians, metros, "45378", "Boulder", CFG)
assert r.home_is_cheapest is True
assert r.cheapest_metro == "Colorado Springs"
assert r.savings == 0
# ---------------------------------------------------------------------------
# Part E β€” CTA URL builder
# ---------------------------------------------------------------------------
def test_build_cta_url_homepage_fallback_when_no_pattern():
url = core.build_cta_url("45378", "Boulder", CFG)
assert url.startswith("https://clearprice-health.com")
assert "utm_source=mile-hi-labs" in url
def test_rank_metros_home_suppressed_no_negative_savings():
medians, metros = _fixtures()
# "Tiny Town" has n_hospitals=1 (suppressed) and the lowest price ($50)
r = core.rank_metros(medians, metros, "45378", "Tiny Town", CFG)
assert r.savings >= 0 # never negative
assert r.savings == 0 # home price unreliable -> no claimed savings
assert r.home_is_cheapest is False
assert r.cheapest_metro == "Boulder" # cheapest non-suppressed shown informationally
def test_build_cta_url_uses_deep_link_pattern():
cfg = core.load_config()
cfg["funnel"]["deep_link_pattern"] = "/procedure/{code}/?metro={metro_slug}"
url = core.build_cta_url("45378", "Colorado Springs", cfg)
assert "/procedure/45378/" in url
assert "metro=colorado-springs" in url
assert "utm_source=mile-hi-labs" in url
# ---------------------------------------------------------------------------
# Part G β€” app.py: import contract + format_headline
# ---------------------------------------------------------------------------
import app as app_module
def test_app_imports_and_exposes_build_demo():
assert hasattr(app_module, "build_demo")
def test_format_headline_savings():
medians, metros = _fixtures()
r = core.rank_metros(medians, metros, "45378", "Denver", CFG)
text = app_module.format_headline(r, CFG)
assert "Denver" in text and "Boulder" in text
assert "$1,932" in text
assert "~" in text # approx drive time label
def test_format_headline_no_comparable_metro():
import pandas as pd
medians2 = pd.DataFrame([
{"state": "CO", "procedure_code": "99999", "procedure_name": "Solo proc",
"metro": "Denver", "median_price": 500, "n_hospitals": 12},
{"state": "CO", "procedure_code": "99999", "procedure_name": "Solo proc",
"metro": "Tiny Town", "median_price": 100, "n_hospitals": 1},
])
_, metros = _fixtures()
r = core.rank_metros(medians2, metros, "99999", "Denver", CFG)
text = app_module.format_headline(r, CFG) # must not raise
assert "Denver" in text
assert "$500" in text
assert "None" not in text
# ---------------------------------------------------------------------------
# Part F β€” data_source: load curated slice from private HF Dataset
# ---------------------------------------------------------------------------
def test_load_slices_uses_token_and_reads_csvs(monkeypatch, tmp_path):
medians_csv = tmp_path / "medians.csv"
metros_csv = tmp_path / "metros.csv"
medians_csv.write_text(
"state,procedure_code,procedure_name,metro,median_price,n_hospitals\n"
"CO,45378,Diagnostic colonoscopy,Denver,2267,12\n"
)
metros_csv.write_text("state,metro,h3_cell\nCO,Denver,8a2a1072b59ffff\n")
calls = {}
def fake_download(repo_id, filename, repo_type, token):
calls["repo_id"] = repo_id
calls["repo_type"] = repo_type
calls["token"] = token
return str(medians_csv if filename.endswith("medians.csv") else metros_csv)
monkeypatch.setattr(data_source, "hf_hub_download", fake_download)
cfg = core.load_config()
medians, metros = data_source.load_slices(cfg, token="hf_test")
assert calls["repo_type"] == "dataset"
assert calls["token"] == "hf_test"
assert calls["repo_id"] == cfg["data"]["dataset_repo"]
assert list(medians["metro"]) == ["Denver"]
assert list(metros["metro"]) == ["Denver"]
# ---------------------------------------------------------------------------
# Part H β€” app builds under gradio 5
# ---------------------------------------------------------------------------
import app as app_module
def test_build_demo_returns_blocks():
import gradio as gr
demo = app_module.build_demo()
assert isinstance(demo, gr.Blocks)
# ---------------------------------------------------------------------------
# Part I β€” llm config + module
# ---------------------------------------------------------------------------
def test_config_has_llm_block():
cfg = core.load_config()
assert cfg["llm"]["zerogpu_model_id"]
assert cfg["llm"]["recommendation_max_sentences"] >= 1
assert "not medical advice" in cfg["llm"]["medical_disclaimer"]
import llm
def _fake_gen(returns):
return lambda system, user: returns
CATALOG = {
"procedures": {"Diagnostic colonoscopy": "45378", "MRI brain": "70551"},
"metros": ["Denver Metro", "Boulder", "Colorado Springs"],
}
def test_parse_ok_maps_name_to_code():
gen = _fake_gen('{"procedure": "Diagnostic colonoscopy", "metro": "Denver Metro"}')
r = llm.parse_request("cheapest colonoscopy near denver", CATALOG, gen)
assert r.status == "ok"
assert r.procedure_code == "45378"
assert r.metro == "Denver Metro"
def test_parse_missing_metro():
gen = _fake_gen('{"procedure": "MRI brain", "metro": null}')
r = llm.parse_request("mri brain", CATALOG, gen)
assert r.status == "missing_metro"
assert r.procedure_code == "70551" and r.metro is None
def test_parse_missing_procedure():
gen = _fake_gen('{"procedure": null, "metro": "Boulder"}')
r = llm.parse_request("prices in boulder", CATALOG, gen)
assert r.status == "missing_procedure"
def test_parse_hallucinated_values_rejected():
gen = _fake_gen('{"procedure": "Brain transplant", "metro": "Atlantis"}')
r = llm.parse_request("brain transplant in atlantis", CATALOG, gen)
assert r.status == "no_match"
assert r.procedure_code is None and r.metro is None
def test_parse_bad_json():
gen = _fake_gen("I think you want a colonoscopy in Denver, friend!")
r = llm.parse_request("...", CATALOG, gen)
assert r.status == "bad_json"
def _denver_ranking():
medians, metros = _fixtures()
return core.rank_metros(medians, metros, "45378", "Denver", CFG)
def test_recommendation_passes_through_grounded_prose():
r = _denver_ranking()
gen = _fake_gen("Drive to Boulder and pay $335 instead of $2,267 β€” save $1,932 (85%).")
out = llm.write_recommendation(r, CFG, fallback_text="FALLBACK", generate=gen)
assert "Boulder" in out
assert "FALLBACK" not in out
assert CFG["llm"]["medical_disclaimer"] in out
def test_recommendation_with_ungrounded_number_falls_back():
r = _denver_ranking()
gen = _fake_gen("You could save up to $9,999 by driving to Boulder!")
out = llm.write_recommendation(r, CFG, fallback_text="FALLBACK", generate=gen)
assert out == "FALLBACK"
def test_recommendation_rejects_bare_ungrounded_number():
r = _denver_ranking()
gen = _fake_gen("You could save 9999 dollars driving to Boulder.")
out = llm.write_recommendation(r, CFG, fallback_text="FALLBACK", generate=gen)
assert out == "FALLBACK"
def test_import_safe_without_torch():
import importlib
importlib.reload(llm)
assert llm.zerogpu_available() in (True, False) # no ImportError raised
def test_detect_provider_prefers_explicit():
assert llm.detect_provider({"MODEL_PROVIDER": "huggingface"}) == "huggingface"
def test_detect_provider_space_with_deps_is_zerogpu(monkeypatch):
monkeypatch.setattr(llm, "zerogpu_available", lambda: True)
assert llm.detect_provider({"SPACE_ID": "x/y"}) == "zerogpu"
def test_detect_provider_space_without_deps_is_huggingface(monkeypatch):
monkeypatch.setattr(llm, "zerogpu_available", lambda: False)
assert llm.detect_provider({"SPACE_ID": "x/y"}) == "huggingface"
# ---------------------------------------------------------------------------
# Part J β€” app.py: result builders (Task 6)
# ---------------------------------------------------------------------------
def test_build_catalog_shape():
medians, metros = _fixtures()
cat = app_module.build_catalog(medians, metros)
names = {p["name"]: p["code"] for p in cat["procedures"]}
assert names.get("Diagnostic colonoscopy") == "45378"
assert any(m["label"] == "Denver" for m in cat["metros"])
def test_build_lookup_result():
medians, metros = _fixtures()
res = app_module.build_lookup(medians, metros, "45378", "Denver", CFG)
assert res["status"] == "ok"
assert res["recommendation"] is None
assert res["headline"] and "Boulder" in res["headline"]
metros_in_rows = {row["metro"] for row in res["rows"]}
assert "Tiny Town" not in metros_in_rows
boulder = next(r for r in res["rows"] if r["metro"] == "Boulder")
assert boulder["is_cheapest"] is True
assert res["cta_url"].startswith("https://")
def test_build_ask_ok_includes_recommendation():
medians, metros = _fixtures()
calls = {"n": 0}
def gen2(system, user):
calls["n"] += 1
if calls["n"] == 1:
return '{"procedure": "Diagnostic colonoscopy", "metro": "Denver"}'
return "Drive to Boulder for $335 instead of $2,267 β€” save $1,932 (85%)."
res = app_module.build_ask(medians, metros, "cheapest colonoscopy in denver", CFG, gen2)
assert res["status"] == "ok"
assert res["interpretation"]["metro"] == "Denver"
assert "Boulder" in res["recommendation"]
def test_build_ask_missing_metro_status():
medians, metros = _fixtures()
gen = _fake_gen('{"procedure": "Diagnostic colonoscopy", "metro": null}')
res = app_module.build_ask(medians, metros, "colonoscopy", CFG, gen)
assert res["status"] == "missing_metro"
assert res["rows"] == [] and res["recommendation"] is None
# ---------------------------------------------------------------------------
# Part J β€” API endpoint reachability (the API seam)
# ---------------------------------------------------------------------------
def test_named_endpoints_reachable_via_client():
import gradio as gr
assert gr.__version__.startswith("5"), f"need gradio 5.x for this smoke, got {gr.__version__}"
from gradio_client import Client
medians, metros = _fixtures()
app_module._DATA["medians"] = medians # seed cache: no token/network needed
app_module._DATA["metros"] = metros
app_module._DATA["error"] = None
demo = app_module.build_demo()
demo.launch(prevent_thread_lock=True, server_port=7899, quiet=True)
try:
client = Client("http://127.0.0.1:7899")
cat = client.predict(api_name="/catalog")
assert any(p["code"] == "45378" for p in cat["procedures"])
# named-kwarg payload with all params present β€” must NOT raise "missing parameter"
res = client.predict(procedure_code="45378", metro="Denver", api_name="/lookup")
assert res["status"] == "ok" and res["headline"]
# /ask shares the identical registration path; not invoked (no model in test env)
finally:
demo.close()
# ---------------------------------------------------------------------------
# Part K β€” M3: build_ask graceful LLM failure
# ---------------------------------------------------------------------------
def test_build_ask_llm_failure_is_graceful():
medians, metros = _fixtures()
def boom(system, user):
raise RuntimeError("model down")
res = app_module.build_ask(medians, metros, "cheapest colonoscopy in denver", CFG, boom)
assert res["status"] == "unavailable"
assert res["rows"] == [] and res["recommendation"] is None
# ---------------------------------------------------------------------------
# Part K β€” M1: suppressed-row exclusion from grounding set
# ---------------------------------------------------------------------------
def test_grounding_excludes_suppressed_row_prices():
# Tiny Town ($50, suppressed) must NOT legitimize an ungrounded "$50" claim.
r = _denver_ranking()
gen = _fake_gen("Skip the wait and pay just $50 somewhere.") # 50 = a suppressed row's price
out = llm.write_recommendation(r, CFG, fallback_text="FALLBACK", generate=gen)
assert out == "FALLBACK"