fix(sp41): trailing newlines + edge-case tests for rebill admin endpoints
This commit is contained in:
@@ -38,6 +38,7 @@ from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
from cyclone.auth.deps import matrix_gate
|
||||
from cyclone.rebill.run import run_rebill
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -133,8 +134,6 @@ def post_rebill(req: RebillRequest) -> dict[str, Any]:
|
||||
envelope with CORS headers. Per-file failures land in the summary
|
||||
CSV (and the ``counts`` dict) rather than raising here.
|
||||
"""
|
||||
from cyclone.rebill.run import run_rebill # local import — keeps --help fast
|
||||
|
||||
start_str, end_str = req.window.split("..", 1)
|
||||
# Validator already proved these parse cleanly.
|
||||
window_start = date.fromisoformat(start_str)
|
||||
@@ -212,7 +211,7 @@ def get_rebill_status(
|
||||
counts: dict[str, int] = {}
|
||||
with summary_path.open(newline="") as f:
|
||||
for row in csv.DictReader(f):
|
||||
disp = (row.get("disposition") or "UNKNOWN").strip() or "UNKNOWN"
|
||||
disp = (row.get("disposition", "UNKNOWN") or "").strip() or "UNKNOWN"
|
||||
counts[disp] = counts.get(disp, 0) + 1
|
||||
recent.append({
|
||||
"as_of": d.name,
|
||||
|
||||
@@ -191,3 +191,36 @@ def test_get_rebill_status_returns_recent_runs(client, tmp_path, monkeypatch):
|
||||
# Each disposition surfaced exactly once — matches the 2-row CSV.
|
||||
assert counts.get("REBILLED_B") == 1, counts
|
||||
assert counts.get("EXCLUDED_TIMELY_FILING") == 1, counts
|
||||
|
||||
|
||||
def test_post_rebill_rejects_malformed_window(client):
|
||||
"""Pydantic window validator should 422 on bad shapes (no `..`, non-dates, reversed)."""
|
||||
bad_windows = [
|
||||
"2026-01-01", # no `..` separator
|
||||
"foo..bar", # non-ISO dates
|
||||
"2026-12-01..2026-01-01", # start after end
|
||||
]
|
||||
for w in bad_windows:
|
||||
resp = client.post(
|
||||
"/api/admin/rebill-from-835",
|
||||
json={"window": w},
|
||||
)
|
||||
assert resp.status_code == 422, (
|
||||
f"expected 422 for window={w!r}, got {resp.status_code}: {resp.text}"
|
||||
)
|
||||
|
||||
|
||||
def test_get_rebill_status_returns_empty_when_no_runs(client, tmp_path, monkeypatch):
|
||||
"""GET /status on an empty rebills dir must return {"recent_runs": []}, not 500.
|
||||
|
||||
Monkeypatches REBILLS_DIR to a fresh tmp_path subdir so the test
|
||||
doesn't depend on (or pollute) any pre-existing dev/rebills/ tree.
|
||||
"""
|
||||
from cyclone.api_routers import rebill
|
||||
|
||||
monkeypatch.setattr(rebill, "REBILLS_DIR", tmp_path / "rebills_empty")
|
||||
|
||||
resp = client.get("/api/admin/rebill-from-835/status")
|
||||
assert resp.status_code == 200, resp.text
|
||||
assert resp.json() == {"recent_runs": []}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user