From ee260e05f526a9757a07330a7495864bac31fdd2 Mon Sep 17 00:00:00 2001 From: Research Assistant Date: Mon, 27 Apr 2026 20:41:49 +0800 Subject: [PATCH] fix: L2 diagnostic accepts 400 as reachable (PaddleOCR rejects bare POST without fileUrl) --- paperforge/ocr_diagnostics.py | 4 +++- tests/test_ocr_doctor.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/paperforge/ocr_diagnostics.py b/paperforge/ocr_diagnostics.py index 313f3989..ebaa5f82 100644 --- a/paperforge/ocr_diagnostics.py +++ b/paperforge/ocr_diagnostics.py @@ -62,7 +62,9 @@ def ocr_doctor(config: dict[str, str] | None, live: bool = False) -> dict: "error": f"URL returned {resp.status_code}", "fix": "OCR provider is experiencing issues. Retry later with `paperforge ocr --diagnose`", } - if resp.status_code != 200: + # 200 = happy path, 400 = reachable but request incomplete + # Both prove URL is live and token is valid (else 401) + if resp.status_code not in (200, 400): return { "level": 2, "passed": False, diff --git a/tests/test_ocr_doctor.py b/tests/test_ocr_doctor.py index 969ec441..e806847e 100644 --- a/tests/test_ocr_doctor.py +++ b/tests/test_ocr_doctor.py @@ -56,6 +56,20 @@ def test_l2_unauthorized(): assert "token is invalid" in result["fix"] +def test_l2_400_accepted(): + """Mock POST returning 400 → level 2 passes (reachable, incomplete request).""" + env = {"PADDLEOCR_API_TOKEN": "test-token"} + mock_resp = MagicMock() + mock_resp.status_code = 400 + + with patch.dict(os.environ, env, clear=True): + with patch("paperforge.ocr_diagnostics.requests.post", return_value=mock_resp): + result = ocr_doctor(config=None, live=False) + + # 400 must NOT be an L2 failure — it proves URL reachable + assert result["level"] != 2, f"Expected L2 to pass, got level {result['level']}" + + # --------------------------------------------------------------------------- # L3 — API response structure # ---------------------------------------------------------------------------