Address claude-review on #922: Blocking — test coverage for the new tier2 rung: - test_registry_tiering.py: inline empty-text routes to ocr-incluster before ocr-upstream; only-incluster-enabled routes to incluster; disabled-incluster skips to upstream; evaluate_escalation empty_text hops to ocr-incluster (and falls through to upstream when incluster off); next_available_tier walks the full fast→structured→ocr-incluster→ocr-upstream ladder + ignore_ocr_enabled ideal-target. - test_escalation_signature.py: enabling document_ocr_incluster_enabled changes the dead-letter signature (independent of the upstream rung). - test_tiered_escalation_strategy.py: structured→ocr-incluster hops to INGEST_QUEUE_OCR_INCLUSTER; tier_for_queue covers the in-cluster queue. Important — real fixes: - registry.py: run scan detection (image_coverage_per_page) when EITHER OCR rung is enabled, not just the upstream one — a tenant with only in-cluster OCR on was missing image-coverage scan signals. - ocr.py: the gateway_only (in-cluster) processor never enters batch mode — the GPU is synchronous/low-latency; batch OCR is the upstream Mistral async path. _get_batch_client short-circuits to None. Covered by a new test. Nit: - cli.py: worker --tier help lists ocr-incluster/ocr-upstream as separate fleets. Left as-is: the lazy anyio.Lock init in OcrProcessor — instances ARE created at module import (document_processors/__init__.py), so deferring lock creation off import time is still required; moving it into __init__ would reintroduce the import-time-primitive issue the comment guards against. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
57 lines
1.9 KiB
Python
57 lines
1.9 KiB
Python
"""Unit tests for the escalation-tier signature used by dead-letter keying.
|
|
|
|
``escalation_tiers_signature`` fingerprints the runtime escalation config so a
|
|
dead-lettered document becomes retryable when a new tier appears (e.g. an
|
|
operator enables OCR). It must be settings-derived (role-independent) and must
|
|
change when OCR is toggled.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from nextcloud_mcp_server.document_processors.escalation import (
|
|
escalation_tiers_signature,
|
|
)
|
|
|
|
pytestmark = pytest.mark.unit
|
|
|
|
|
|
def _settings(
|
|
*, ocr: bool, ocr_incluster: bool = False, engine: str = "pypdfium2"
|
|
) -> SimpleNamespace:
|
|
return SimpleNamespace(
|
|
document_ocr_enabled=ocr,
|
|
document_ocr_incluster_enabled=ocr_incluster,
|
|
document_tier1_engine=engine,
|
|
)
|
|
|
|
|
|
def test_signature_is_stable_for_same_config() -> None:
|
|
assert escalation_tiers_signature(
|
|
_settings(ocr=False)
|
|
) == escalation_tiers_signature(_settings(ocr=False))
|
|
|
|
|
|
def test_enabling_ocr_changes_signature() -> None:
|
|
# Enabling OCR adds an escalation tier -> previously dead-lettered docs retry.
|
|
assert escalation_tiers_signature(
|
|
_settings(ocr=False)
|
|
) != escalation_tiers_signature(_settings(ocr=True))
|
|
|
|
|
|
def test_enabling_ocr_incluster_changes_signature() -> None:
|
|
# Enabling the in-cluster (tier2) rung adds an escalation tier independently of
|
|
# the upstream rung -> previously dead-lettered scanned docs become retryable.
|
|
assert escalation_tiers_signature(
|
|
_settings(ocr=False, ocr_incluster=False)
|
|
) != escalation_tiers_signature(_settings(ocr=False, ocr_incluster=True))
|
|
|
|
|
|
def test_tier1_engine_change_changes_signature() -> None:
|
|
assert escalation_tiers_signature(
|
|
_settings(ocr=False, engine="pypdfium2")
|
|
) != escalation_tiers_signature(_settings(ocr=False, engine="pymupdf"))
|