Files
cali_scraper/tests/test_dashboard_bill_status.py

111 lines
4.8 KiB
Python

"""Tests for ``scraper.dashboard.bill_status`` — the "why is this bill here?"
classifier (API_CONTRACT §5 "upcoming" item 2: a bill should not be a vague
"pending" when its floor history already shows a terminal / current state).
Pure, no DB: ``derive_status(description)`` -> ``{status, reason,
possibly_closed}``. The *latest* matching action in the chronological log wins;
the contract's "default to including, flag possibly_closed" applies when the
source can't disambiguate.
"""
from __future__ import annotations
from scraper.dashboard import bill_status
from scraper.dashboard.bill_status import derive_status, is_closed
# ---------------------------------------------------------------------------
# the motivating case: a passed bill sitting in "unfinished business"
# ---------------------------------------------------------------------------
def test_awaiting_concurrence_in_senate_amendments_is_open():
# AB1099: passed the Assembly, now awaiting Senate-amendment concurrence.
r = derive_status(
"Vote required: 41 2026 Aug. 30 - Read third time. Passed. "
"Ordered to the Assembly. (Ayes 39. Noes 0.). Aug. 30 - In Assembly. "
"Concurrence in Senate amendments pending. View Details",
"AB 1099")
assert r["status"] == "open"
assert "concurrence" in r["reason"].lower()
assert r["possibly_closed"] is False
assert not is_closed(r)
def test_passed_is_not_terminal_but_closed_terms_are():
# "Passed" (one chamber) is an in-progress step -> open, not dropped.
passed = derive_status("Read third time. Passed. Ordered to the Assembly.",
"AB 1")
assert passed["status"] == "open"
# "To inactive file" is a terminal/acted state -> closed, possibly_closed.
inact = derive_status("2026 Aug. 25 - To inactive file on motion of "
"Assembly Member Jackson. View Details", "AB 2")
assert inact["status"] == "closed"
assert inact["possibly_closed"] is True
assert is_closed(inact)
assert "inactive" in inact["reason"].lower()
# ---------------------------------------------------------------------------
# latest-action-wins + priority
# ---------------------------------------------------------------------------
def test_latest_action_in_log_wins():
# A bill that passed long ago but now has "Motion to reconsider
# continued" surfaces the *current* reconsideration state, not the old pass.
r = derive_status(
"2025 Sep. 13 - Read third time. Passed. Ordered to the Assembly. "
"Sep. 13 - In Assembly. Assembly refused to concur in Senate "
"amendments. (Ayes 34. Noes 27.) Motion to reconsider on the next "
"legislative day made. ... 2026 Aug. 30 - Motion to reconsider "
"continued. View Details",
"AB 1231")
assert "reconsider" in r["reason"].lower()
assert r["status"] == "open"
def test_priority_breates_ties_on_same_span():
# "Motion to reconsider" must win over a bare "passed" even though both
# match; the specific/current fact dominates.
r = derive_status("Passed. Motion to reconsider made.", "AB 1")
assert "reconsider" in r["reason"].lower()
# ---------------------------------------------------------------------------
# unknown / content-free -> default to including + flag (API_CONTRACT §5)
# ---------------------------------------------------------------------------
def test_content_free_defaults_to_unknown_and_flags():
# A bare dated line with no action text -> "On file", never a false pass.
r = derive_status("Vote required: 41 2026 May 27 - View Details", "AB 3")
assert r["status"] == "unknown"
assert r["possibly_closed"] is None # can't confirm -> not asserted open
assert "on file" in r["reason"].lower()
def test_empty_description_is_unknown_not_crash():
for text in (None, "", " "):
r = derive_status(text, "AB 4")
assert r["status"] == "unknown"
assert r["possibly_closed"] is None
# ---------------------------------------------------------------------------
# a few more real-world terminal / open signals
# ---------------------------------------------------------------------------
def test_engrossed_and_withdrawn_are_closed():
assert is_closed(derive_status("Engrossed. View Details", "AB 5"))
assert is_closed(derive_status("Withdrawn from committee. View Details",
"AB 6"))
def test_in_committee_and_scheduled_hearing_are_open():
assert derive_status("Referred to Com. on Health. View Details", "AB 7")["status"] == "open"
assert derive_status("June 17 - Set for first hearing. View Details",
"AB 8")["status"] == "open"
def test_return_shape_is_stable():
r = derive_status("Pending. View Details", "AB 9")
assert set(r.keys()) == {"status", "reason", "possibly_closed"}