82 lines
3.0 KiB
Python
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
|