133 lines
4.7 KiB
Python
133 lines
4.7 KiB
Python
import json
|
||
|
||
import httpx
|
||
import pytest
|
||
from app.schemas.ocr import OcrMeasurement
|
||
from app.services.baidu_ocr import BaiduOcrClient
|
||
from app.services.ocr import OcrService
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_webimage_recognition_returns_text_and_confidence() -> None:
|
||
def handler(request: httpx.Request) -> httpx.Response:
|
||
if request.url.path.endswith("/token"):
|
||
return httpx.Response(200, json={"access_token": "token", "expires_in": 3600})
|
||
assert request.url.path.endswith("/webimage")
|
||
return httpx.Response(
|
||
200,
|
||
json={
|
||
"words_result": [
|
||
{"words": "产品名称:测试面霜", "probability": {"average": 0.92}},
|
||
{"words": "pH:5.6", "probability": {"average": 0.88}},
|
||
]
|
||
},
|
||
)
|
||
|
||
client = BaiduOcrClient(httpx.MockTransport(handler))
|
||
client.api_key = "api-key"
|
||
client.secret_key = "secret-key"
|
||
text, confidence, pages = await client.recognize_image(b"fake-image")
|
||
|
||
assert text == "产品名称:测试面霜\npH:5.6"
|
||
assert confidence == pytest.approx(0.9)
|
||
assert pages == 1
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_paddle_document_recognition_polls_and_downloads_result() -> None:
|
||
calls = {"query": 0}
|
||
|
||
def handler(request: httpx.Request) -> httpx.Response:
|
||
if request.url.path.endswith("/token"):
|
||
return httpx.Response(200, json={"access_token": "token", "expires_in": 3600})
|
||
if request.url.path.endswith("/task/query"):
|
||
calls["query"] += 1
|
||
status = "processing" if calls["query"] == 1 else "success"
|
||
result = {"status": status}
|
||
if status == "success":
|
||
result["parse_result_url"] = "https://mock.bcebos.com/result.json"
|
||
return httpx.Response(200, json={"error_code": 0, "result": result})
|
||
if request.url.path.endswith("/task"):
|
||
return httpx.Response(200, json={"error_code": 0, "result": {"task_id": "task-1"}})
|
||
if request.url.path.endswith("/result.json"):
|
||
return httpx.Response(
|
||
200,
|
||
content=json.dumps(
|
||
{
|
||
"pages": [
|
||
{
|
||
"text": "产品名称:测试面霜",
|
||
"tables": [{"markdown": "|检测项|值|\n|pH|5.6|"}],
|
||
}
|
||
]
|
||
},
|
||
ensure_ascii=False,
|
||
).encode(),
|
||
)
|
||
raise AssertionError(f"unexpected URL: {request.url}")
|
||
|
||
client = BaiduOcrClient(httpx.MockTransport(handler))
|
||
client.api_key = "api-key"
|
||
client.secret_key = "secret-key"
|
||
client.poll_interval = 0.001
|
||
text, confidence, pages = await client.recognize_document(b"%PDF-test", "report.pdf")
|
||
|
||
assert "测试面霜" in text
|
||
assert "|pH|5.6|" in text
|
||
assert confidence is None
|
||
assert pages == 1
|
||
assert calls["query"] == 2
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("raw_value", "value", "standard", "expected"),
|
||
[
|
||
("5.6", 5.6, "pH 4.5-6.5", "合格"),
|
||
("3.8", 3.8, "色差 ΔE≤3.0", "不合格"),
|
||
("+18%", 18, "喷出量偏差≤±15%", "不合格"),
|
||
("0.9 N·m", 0.9, "开启力矩 0.8~3.2N·m", "合格"),
|
||
],
|
||
)
|
||
def test_standard_numeric_evaluation(raw_value: str, value: float, standard: str, expected: str) -> None:
|
||
measurement = OcrMeasurement(
|
||
item="检测项",
|
||
raw_value=raw_value,
|
||
value=value,
|
||
confidence=0.95,
|
||
needs_review=False,
|
||
)
|
||
result, reason = OcrService.evaluate_standard(measurement, standard)
|
||
|
||
assert result == expected
|
||
assert reason is None
|
||
|
||
|
||
def test_upload_type_is_detected_from_content_not_filename() -> None:
|
||
assert OcrService._detect_type(b"%PDF-1.7") == ("application/pdf", ".pdf")
|
||
assert OcrService._detect_type(b"\x89PNG\r\n\x1a\nrest") == ("image/png", ".png")
|
||
assert OcrService._detect_type(b"\xff\xd8\xffrest") == ("image/jpeg", ".jpg")
|
||
|
||
|
||
def test_standard_judgment_rejects_unit_mismatch() -> None:
|
||
measurement = OcrMeasurement(
|
||
item="铅含量",
|
||
raw_value="8mg/L",
|
||
value=8,
|
||
unit="mg/L",
|
||
confidence=0.95,
|
||
needs_review=False,
|
||
)
|
||
|
||
result, reason = OcrService.evaluate_standard(measurement, "铅≤10mg/kg")
|
||
|
||
assert result == "待人工确认"
|
||
assert reason == "检测值单位 mg/L 与标准单位 mg/kg 不一致"
|
||
|
||
|
||
def test_generic_quantity_does_not_fuzzy_match_a_standard() -> None:
|
||
class Rule:
|
||
risk_point = "销售包装可视面及最小销售单元完整标注"
|
||
judge_standard = "中英文标签内容完整"
|
||
|
||
assert OcrService._best_rule("数量", [Rule()]) is None
|