Files
lcbp3/tests/unit/ocr-sidecar/test_residency_wiring.py
T
admin a80ebef285
CI / CD Pipeline / build (push) Successful in 7m37s
CI / CD Pipeline / deploy (push) Failing after 20m15s
refactor(ai): OCR sidecar canonical naming cleanup — typhoon→np-dms, remove hardcoded keys, asyncio.to_thread, ADR-040/041
2026-06-20 16:37:04 +07:00

82 lines
3.0 KiB
Python

# File: tests/unit/ocr-sidecar/test_residency_wiring.py
# Change Log:
# - 2026-06-20: Added ADR-040 residency wiring tests for process_ocr.
# - 2026-06-20: Updated for async process_ocr (Phase 6 — async I/O refactor).
import asyncio
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import MagicMock, patch
import pytest
from test_path_traversal import load_app
class FakeAsyncResponse:
"""จำลอง httpx.AsyncClient response สำหรับ async process_ocr"""
def raise_for_status(self) -> None:
return None
def json(self) -> dict:
return {"choices": [{"message": {"content": "{\"natural_text\": \"ok\"}"}}]}
class FakeAsyncClient:
"""จำลอง httpx.AsyncClient สำหรับ async process_ocr"""
def __init__(self, *args, **kwargs) -> None:
self.payload = None
FakeAsyncClient.last_payload = None
async def post(self, url: str, json: dict, headers: dict) -> FakeAsyncResponse:
self.payload = json
FakeAsyncClient.last_payload = json
return FakeAsyncResponse()
async def aclose(self) -> None:
pass
FakeAsyncClient.last_payload = None
def test_process_ocr_uses_calculated_residency_keep_alive(tmp_path: Path) -> None:
"""T019: process_ocr ต้องเรียก calculate_ocr_residency และใช้ค่า keep_alive ที่คำนวณได้"""
app_module = load_app(tmp_path)
decision = SimpleNamespace(keep_alive_seconds=120, reason="headroom-sufficient", vram_headroom_mb=9000.0)
fake_client = FakeAsyncClient()
with patch.object(app_module, "calculate_ocr_residency", return_value=decision) as calculate, \
patch.object(app_module, "prepare_ocr_messages", return_value=[{"content": []}]), \
patch.object(app_module, "ollama_client", fake_client):
result = asyncio.run(app_module.process_ocr("/tmp/test.pdf", page_num=1))
assert result == "ok"
calculate.assert_called_once_with(app_module.OCR_ACTIVE_PROFILE)
assert FakeAsyncClient.last_payload["keep_alive"] == 120
def test_process_ocr_rejects_backend_keep_alive_override(tmp_path: Path) -> None:
"""T021: process_ocr ต้องปฏิเสธ keep_alive จาก backend"""
app_module = load_app(tmp_path)
async def run_test():
with pytest.raises(ValueError, match="keep_alive must be calculated"):
await app_module.process_ocr("/tmp/test.pdf", options_override={"keep_alive": 0})
asyncio.run(run_test())
def test_ocr_endpoint_rejects_keep_alive_override(tmp_path: Path) -> None:
"""T021: /ocr endpoint ต้องปฏิเสธ keep_alive ใน request body"""
app_module = load_app(tmp_path)
from fastapi.testclient import TestClient
client = TestClient(app_module.app)
response = client.post(
"/ocr",
json={"pdfPath": str(tmp_path / "document.pdf"), "keep_alive": 0},
headers={"X-API-Key": "test-key"},
)
assert response.status_code == 400