Some checks failed
CI / skinny-install (aco) (push) Successful in 1m7s
CI / skinny-install (api) (push) Successful in 33s
CI / skinny-install (bcda) (push) Successful in 39s
CI / skinny-install (bib) (push) Successful in 38s
CI / skinny-install (bls) (push) Successful in 34s
CI / skinny-install (ccw) (push) Successful in 38s
CI / skinny-install (cli) (push) Successful in 34s
CI / skinny-install (cms) (push) Successful in 38s
CI / skinny-install (conf) (push) Successful in 35s
CI / skinny-install (opps) (push) Successful in 33s
CI / skinny-install (perf) (push) Successful in 35s
CI / skinny-install (pfs) (push) Successful in 42s
CI / skinny-install (rex) (push) Successful in 40s
CI / lint-test (push) Failing after 11m44s
Deploy / build-scan-report (push) Failing after 6m13s
80 new tests covering federalregister, email_ingest, regulations_gov, prisma/fetch (incl. deterministic altcha PoW vector), and zot/ops. All external deps mocked (httpx, imaplib, subprocess, pydo). No network calls in CI.
236 lines
7.0 KiB
Python
236 lines
7.0 KiB
Python
"""Tests for bib.regulations_gov — reg.gov v4 API client."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from bib.regulations_gov import (
|
|
Client,
|
|
Comment,
|
|
_byline,
|
|
_docket_from_comment_id,
|
|
_filename_from,
|
|
_hash,
|
|
_parse_comment,
|
|
_reg_date,
|
|
_slug,
|
|
upsert_comment,
|
|
)
|
|
|
|
# ── Pure helpers ──────────────────────────────────────────────────
|
|
|
|
|
|
class TestRegDate:
|
|
def test_iso_to_space(self):
|
|
assert _reg_date("2017-08-30T18:35:48Z") == "2017-08-30 18:35:48"
|
|
|
|
def test_already_space(self):
|
|
assert _reg_date("2017-08-30 18:35:48") == "2017-08-30 18:35:48"
|
|
|
|
def test_empty(self):
|
|
assert _reg_date("") == ""
|
|
|
|
def test_none(self):
|
|
assert _reg_date("") == ""
|
|
|
|
|
|
class TestSlug:
|
|
def test_basic(self):
|
|
assert _slug("American Medical Association") == "american-medical-association"
|
|
|
|
def test_special(self):
|
|
assert _slug("Test & Co., Inc.") == "test-co-inc"
|
|
|
|
def test_truncate(self):
|
|
assert len(_slug("x" * 100)) <= 40
|
|
|
|
|
|
class TestFilenameFrom:
|
|
def test_url(self):
|
|
assert (
|
|
_filename_from(
|
|
"https://downloads.regulations.gov/CMS-2021-0119-26001/attachment_1.pdf"
|
|
)
|
|
== "attachment_1.pdf"
|
|
)
|
|
|
|
def test_with_query(self):
|
|
assert _filename_from("https://x.com/file.pdf?token=abc") == "file.pdf"
|
|
|
|
def test_empty(self):
|
|
assert _filename_from("") == ""
|
|
|
|
|
|
class TestHash:
|
|
def test_deterministic(self):
|
|
assert _hash("test") == _hash("test")
|
|
assert len(_hash("test")) == 10
|
|
|
|
|
|
class TestByline:
|
|
def test_org(self):
|
|
c = Comment(
|
|
id="1",
|
|
title="",
|
|
posted_date="",
|
|
received_date="",
|
|
docket_id="",
|
|
comment_on_id="",
|
|
organization="AMA",
|
|
)
|
|
assert _byline(c) == "AMA"
|
|
|
|
def test_name(self):
|
|
c = Comment(
|
|
id="1",
|
|
title="",
|
|
posted_date="",
|
|
received_date="",
|
|
docket_id="",
|
|
comment_on_id="",
|
|
first_name="John",
|
|
last_name="Doe",
|
|
)
|
|
assert _byline(c) == "John Doe"
|
|
|
|
def test_empty(self):
|
|
c = Comment(
|
|
id="1",
|
|
title="",
|
|
posted_date="",
|
|
received_date="",
|
|
docket_id="",
|
|
comment_on_id="",
|
|
)
|
|
assert _byline(c) == ""
|
|
|
|
|
|
class TestDocketFromCommentId:
|
|
def test_standard(self):
|
|
assert _docket_from_comment_id("CMS-2025-0304-14107") == "CMS-2025-0304"
|
|
|
|
def test_short(self):
|
|
assert _docket_from_comment_id("AB") == "AB"
|
|
|
|
|
|
# ── _parse_comment ────────────────────────────────────────────────
|
|
|
|
|
|
class TestParseComment:
|
|
def test_basic(self):
|
|
row = {
|
|
"id": "CMS-2017-0092-0002",
|
|
"attributes": {
|
|
"title": "Test Comment",
|
|
"postedDate": "2017-07-18T00:00:00Z",
|
|
"receivedDate": "2017-07-15T00:00:00Z",
|
|
"docketId": "CMS-2017-0092",
|
|
"commentOnId": "0900006482921ba1",
|
|
"firstName": "Jane",
|
|
"lastName": "Doe",
|
|
"organization": "AMA",
|
|
"comment": "This is the body.",
|
|
"attachmentCount": 2,
|
|
},
|
|
}
|
|
c = _parse_comment(row)
|
|
assert c.id == "CMS-2017-0092-0002"
|
|
assert c.title == "Test Comment"
|
|
assert c.posted_date == "2017-07-18"
|
|
assert c.organization == "AMA"
|
|
assert c.comment_text == "This is the body."
|
|
assert c.attachment_count == 2
|
|
|
|
def test_missing_attrs(self):
|
|
c = _parse_comment({"id": "X", "attributes": {}})
|
|
assert c.id == "X"
|
|
assert c.title == ""
|
|
assert c.attachment_count == 0
|
|
|
|
|
|
# ── upsert_comment ────────────────────────────────────────────────
|
|
|
|
|
|
class TestUpsertComment:
|
|
def test_creates_item(self):
|
|
store = MagicMock()
|
|
store.upsert.return_value = "KEY1"
|
|
c = Comment(
|
|
id="CMS-2017-0092-0002",
|
|
title="Test",
|
|
posted_date="2017-07-18",
|
|
received_date="2017-07-15",
|
|
docket_id="CMS-2017-0092",
|
|
comment_on_id="obj123",
|
|
organization="AMA",
|
|
)
|
|
key = upsert_comment(store, c, cms_id="CMS-1676-P")
|
|
assert key == "KEY1"
|
|
store.upsert.assert_called_once()
|
|
item = store.upsert.call_args[0][0]
|
|
assert "AMA" in item.title
|
|
assert item.url == "https://www.regulations.gov/comment/CMS-2017-0092-0002"
|
|
|
|
|
|
# ── Client (mocked httpx) ────────────────────────────────────────
|
|
|
|
|
|
class TestClient:
|
|
@patch.dict("os.environ", {"REGULATIONS_GOV_API_KEY": "test-key"})
|
|
def test_init(self):
|
|
c = Client(sleep=0)
|
|
assert c._key == "test-key"
|
|
c.close()
|
|
|
|
@patch.dict("os.environ", {"REGULATIONS_GOV_API_KEY": ""})
|
|
def test_init_no_key(self):
|
|
with pytest.raises(RuntimeError, match="REGULATIONS_GOV_API_KEY"):
|
|
Client()
|
|
|
|
@patch.dict("os.environ", {"REGULATIONS_GOV_API_KEY": "test-key"})
|
|
def test_get_retry_429(self):
|
|
mock_client = MagicMock()
|
|
resp_429 = MagicMock()
|
|
resp_429.status_code = 429
|
|
resp_ok = MagicMock()
|
|
resp_ok.status_code = 200
|
|
resp_ok.json.return_value = {"data": []}
|
|
resp_ok.raise_for_status = MagicMock()
|
|
mock_client.get.side_effect = [resp_429, resp_ok]
|
|
|
|
with patch("bib.regulations_gov.time.sleep"):
|
|
c = Client(sleep=0, client=mock_client)
|
|
result = c._get("/test")
|
|
assert result == {"data": []}
|
|
|
|
@patch.dict("os.environ", {"REGULATIONS_GOV_API_KEY": "test-key"})
|
|
def test_resolve_docket(self):
|
|
mock_client = MagicMock()
|
|
resp = MagicMock()
|
|
resp.status_code = 200
|
|
resp.json.return_value = {
|
|
"data": [{"attributes": {"docketId": "CMS-2017-0092"}}]
|
|
}
|
|
resp.raise_for_status = MagicMock()
|
|
mock_client.get.return_value = resp
|
|
|
|
with patch("bib.regulations_gov.time.sleep"):
|
|
c = Client(sleep=0, client=mock_client)
|
|
result = c.resolve_docket("CMS-1676-P")
|
|
assert result == "CMS-2017-0092"
|
|
|
|
@patch.dict("os.environ", {"REGULATIONS_GOV_API_KEY": "test-key"})
|
|
def test_resolve_docket_not_found(self):
|
|
mock_client = MagicMock()
|
|
resp = MagicMock()
|
|
resp.status_code = 200
|
|
resp.json.return_value = {"data": []}
|
|
resp.raise_for_status = MagicMock()
|
|
mock_client.get.return_value = resp
|
|
|
|
with patch("bib.regulations_gov.time.sleep"):
|
|
c = Client(sleep=0, client=mock_client)
|
|
assert c.resolve_docket("NONEXISTENT") is None
|