test(ocr): narrow training error test to subprocess.run seam

The asyncio.to_thread patch stubbed out the entire _run_training call,
hiding the real error path. Replacing it with a failing CompletedProcess
from subprocess.run exercises the actual ketos-failed branch and keeps
the test's intent — error counter bumps, 500 surfaces — intact.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-21 16:55:14 +02:00
parent 0fc0cbcffd
commit 0179e93a4b

View File

@@ -347,13 +347,21 @@ async def test_ocr_training_runs_total_incremented_with_recognition_success_labe
@pytest.mark.asyncio
async def test_ocr_training_runs_total_incremented_with_recognition_error_label(fresh_metrics):
"""When /train's inner runner raises, the error counter bumps and the exception propagates."""
async def fake_to_thread(func, *args, **kwargs):
raise RuntimeError("ketos train failed (exit 1): synthetic")
"""When ketos exits non-zero, the error counter bumps and the exception propagates.
Uses the narrowest available seam — `subprocess.run` returning a failing
CompletedProcess — instead of stubbing the asyncio.to_thread boundary,
so the test exercises the real _run_training error path.
"""
from subprocess import CompletedProcess
failing_proc = CompletedProcess(
args=["ketos"], returncode=1, stdout="", stderr="synthetic ketos failure"
)
with patch("main.TRAINING_TOKEN", "secret-token"), \
patch("main._models_ready", True), \
patch("main.asyncio.to_thread", side_effect=fake_to_thread):
patch("main.subprocess.run", return_value=failing_proc):
transport = ASGITransport(app=app, raise_app_exceptions=False)
async with AsyncClient(transport=transport, base_url="http://test") as client:
response = await client.post(