diff --git a/ocr-service/main.py b/ocr-service/main.py index 1460a213..f7dc495d 100644 --- a/ocr-service/main.py +++ b/ocr-service/main.py @@ -69,6 +69,7 @@ async def lifespan(app: FastAPI): kraken_engine.load_models() load_spell_checker() _models_ready = True + metrics.ocr_models_ready.set(1) logger.info("Startup complete — ready to accept requests") yield diff --git a/ocr-service/test_metrics.py b/ocr-service/test_metrics.py index 7d154102..5c472dbe 100644 --- a/ocr-service/test_metrics.py +++ b/ocr-service/test_metrics.py @@ -422,3 +422,23 @@ async def test_ocr_model_accuracy_gauge_set_per_kind_after_successful_training(f assert fresh_metrics.ocr_model_accuracy.labels(kind="recognition")._value.get() == pytest.approx(recognition_accuracy) assert fresh_metrics.ocr_model_accuracy.labels(kind="segmentation")._value.get() == pytest.approx(segmentation_accuracy) + + +def test_ocr_models_ready_gauge_defaults_to_zero(): + """A freshly-built OcrMetrics has ocr_models_ready=0 before lifespan runs.""" + metrics = build_metrics(CollectorRegistry()) + assert metrics.ocr_models_ready._value.get() == 0.0 + + +@pytest.mark.asyncio +async def test_ocr_models_ready_gauge_is_one_after_lifespan_startup(fresh_metrics): + """The lifespan flips ocr_models_ready to 1 once load_models / load_spell_checker return. + + ASGITransport does not run lifespan by default, so the lifespan context + manager is driven directly to exercise the startup code path. + """ + assert fresh_metrics.ocr_models_ready._value.get() == 0.0 + with patch("main.kraken_engine.load_models"), \ + patch("main.load_spell_checker"): + async with app.router.lifespan_context(app): + assert fresh_metrics.ocr_models_ready._value.get() == 1.0