"""A progress checkpoint takes a FINITE position. The JSON parser under FastAPI accepts `Infinity` and `NaN`, and both progress handlers truncate the position with `int()` — so one hand-written checkpoint came back as a 500 with a traceback. Found in the 2026-09 bug hunt's second review round, as the sibling of the subtitle routes' non-finite `at`/`offset`. Pure: the body is refused before the handler touches the database. """ from types import SimpleNamespace import pytest from fastapi.testclient import TestClient import app.state as app_state from app.auth import current_user from app.main import app @pytest.fixture def client(monkeypatch, override_dep) -> TestClient: override_dep(current_user, lambda: SimpleNamespace(id=1)) monkeypatch.setattr(app_state, "_configured_cache", True) # pass the instance-setup gate, no DB return TestClient(app) @pytest.mark.parametrize("path", ["/api/videos/v1/progress", "/api/plex/item/98614/progress"]) @pytest.mark.parametrize("raw", ["Infinity", "-Infinity", "NaN"]) def test_a_non_finite_position_is_refused(client, path, raw): r = client.post( path, content=f'{{"position_seconds": {raw}, "duration_seconds": 3000}}', headers={"content-type": "application/json"}, ) assert r.status_code == 422, r.text @pytest.mark.parametrize("path", ["/api/videos/v1/progress", "/api/plex/item/98614/progress"]) @pytest.mark.parametrize("field", ["position_seconds", "duration_seconds"]) def test_a_position_past_the_int4_column_is_refused_not_a_500(client, path, field): """Finite but past the int4 column: the flush was a DataError, i.e. a 500 with a traceback.""" body = {"position_seconds": 10, "duration_seconds": 3000, field: 2**31} assert client.post(path, json=body).status_code == 422 def test_the_refusal_keeps_the_shape_the_ui_reads(client): """The handler replaces FastAPI's own for every validation error in the app, and the UI turns exactly this shape — a LIST of errors — into its localized "check your input" sentence. The refused value is echoed by its name, because JSON cannot carry NaN.""" r = client.post( "/api/videos/v1/progress", content='{"position_seconds": NaN, "duration_seconds": 3000}', headers={"content-type": "application/json"}, ) detail = r.json()["detail"] assert isinstance(detail, list) and detail[0]["loc"] == ["body", "position_seconds"] assert detail[0]["input"] == "nan" @pytest.mark.parametrize("q", ["start=inf", "start=nan", "aoff=inf", "aoff=-inf", "aoff=nan"]) def test_a_session_at_an_impossible_offset_is_refused(client, q): """`start=inf` reached `int()` in the session builder (a 500); `aoff=inf` spawned an ffmpeg with a non-finite offset.""" assert client.post(f"/api/plex/stream/98614/session?{q}").status_code == 422