diff --git a/ocr-service/test_metrics.py b/ocr-service/test_metrics.py index 253cae14..d2bd9671 100644 --- a/ocr-service/test_metrics.py +++ b/ocr-service/test_metrics.py @@ -501,7 +501,17 @@ async def test_ocr_processing_seconds_histogram_observed_per_page_in_guided_stre @pytest.mark.asyncio async def test_ocr_processing_seconds_histogram_excludes_spell_check_time_in_guided_stream(fresh_metrics): - """The guided observation must time engine work only, not the spell-check pass.""" + """The guided observation must time engine work only, not the spell-check pass. + + Wall-clock bound rather than a structural `patch("main.time.monotonic")`: + the patched attribute is the *global* `time.monotonic`, which httpx and + asyncio also consume — they exhaust the deterministic sequence before the + request reaches the engine loop. Bound is sized against the failure mode, + not the noise floor: spell-check sleeps 0.05s × 2 regions = 0.1s, so a + timer that accidentally wrapped `correct_text` would observe >= 0.1s. The + 0.09s ceiling catches that bug while leaving ~90ms of slack for slow CI + runners (engine work is instantaneous under the mock). + """ mock_images = [Image.new("RGB", (100, 100))] regions = [ {"pageNumber": 1, "x": 0.0, "y": 0.0, "width": 0.5, "height": 0.5, "annotationId": "a1"}, @@ -532,10 +542,7 @@ async def test_ocr_processing_seconds_histogram_excludes_spell_check_time_in_gui sum_seconds, _ = _histogram_count_sum( fresh_metrics.ocr_processing_seconds, engine="kraken" ) - # Spell-check sleeps 0.05s per region × 2 regions = 0.1s; engine work is instantaneous. - # If timing included spell-check, sum_seconds would be >= 0.1s. Allow 30ms slack - # for scheduler overhead. - assert sum_seconds < 0.05, f"timing must exclude spell-check; got sum={sum_seconds}" + assert sum_seconds < 0.09, f"timing must exclude spell-check; got sum={sum_seconds}" @pytest.mark.asyncio