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