fix: label PDF uploads by content instead of a fixed image name
The service dispatches on the upload filename extension. A PDF sent as image.jpg made /ocr/text return a success response with hallucinated text. Only PDFs are relabelled; everything else keeps the legacy image label.
This commit is contained in:
@@ -23,6 +23,8 @@ from polygateway.transports.monkey_ocr import (
|
||||
MonkeyOcrTransport,
|
||||
_classify_status,
|
||||
_parse_middle_json,
|
||||
_sniff_media,
|
||||
_upload_part,
|
||||
)
|
||||
from polygateway.types import SourceConfig
|
||||
|
||||
@@ -167,6 +169,86 @@ class TestRecognizeText:
|
||||
assert b'filename="image.jpg"' in seen["body"] # CHS invokers.py:496 固定名
|
||||
|
||||
|
||||
_PDF_BODY = b"%PDF-1.4\n%synthetic body"
|
||||
_JUNK = b"x" * 200
|
||||
|
||||
|
||||
class TestSniffMedia:
|
||||
"""上传类型按内容判定(#34 设计 §3 Q2): 图片魔数优先 → 前 1KB 内 `%PDF-` → 未知。"""
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("data", "expected"),
|
||||
[
|
||||
pytest.param(_PDF_BODY, "pdf", id="pdf"),
|
||||
pytest.param(_JUNK + _PDF_BODY, "pdf", id="pdf-with-junk-prefix"),
|
||||
pytest.param(b"\xff\xd8\xff\xe0" + b"0" * 20, "image", id="jpeg"),
|
||||
pytest.param(b"\x89PNG\r\n\x1a\n" + b"0" * 20, "image", id="png"),
|
||||
pytest.param(b"II*\x00" + b"0" * 20, "image", id="tiff-le"),
|
||||
pytest.param(b"MM\x00*" + b"0" * 20, "image", id="tiff-be"),
|
||||
pytest.param(b"BM" + b"0" * 20, "image", id="bmp"),
|
||||
pytest.param(b"GIF89a" + b"0" * 20, "image", id="gif89a"),
|
||||
pytest.param(b"GIF87a" + b"0" * 20, "image", id="gif87a"),
|
||||
pytest.param(b"RIFF\x00\x00\x00\x00WEBPVP8 ", "image", id="webp"),
|
||||
# 图片魔数优先: 含 XMP 元数据的图片可能在 1KB 内带 `%PDF-` 字样
|
||||
pytest.param(b"\xff\xd8\xff\xe0" + b"<xmp>%PDF-</xmp>", "image", id="jpeg-mentions-pdf"),
|
||||
pytest.param(b"<html><body>paywall</body></html>", "unknown", id="html"),
|
||||
pytest.param(b"GARBAGE-NOT-AN-IMAGE", "unknown", id="garbage"),
|
||||
pytest.param(b"", "unknown", id="empty"),
|
||||
# 1KB 窗口之外的 `%PDF-` 不算
|
||||
pytest.param(b"x" * 1100 + b"%PDF-", "unknown", id="header-beyond-window"),
|
||||
],
|
||||
)
|
||||
def test_sniff(self, data, expected):
|
||||
assert _sniff_media(data) == expected
|
||||
|
||||
def test_upload_part_labels_pdf_only(self):
|
||||
assert _upload_part(_PDF_BODY) == ("doc.pdf", _PDF_BODY, "application/pdf")
|
||||
assert _upload_part(_JUNK + _PDF_BODY)[0] == "doc.pdf"
|
||||
|
||||
@pytest.mark.parametrize("data", [b"\xff\xd8\xff\xe0abc", b"\x89PNG\r\n\x1a\nabc", b"junk", b""])
|
||||
def test_everything_else_keeps_the_legacy_label(self, data):
|
||||
"""非 PDF 路径字节级不变: 服务对图片按内容解码(2026-10-21 实测 PNG/TIFF/BMP 标 image.jpg 成功)。"""
|
||||
assert _upload_part(data) == ("image.jpg", data, "image/jpeg")
|
||||
|
||||
|
||||
class TestUploadLabelPerMethod:
|
||||
"""三个调用点共用同一个标签点: 漏掉任何一个,E4 的静默错误结果就在那里复活。"""
|
||||
|
||||
@staticmethod
|
||||
def _capturing_transport(seen):
|
||||
def handler(request: httpx.Request) -> httpx.Response:
|
||||
if request.url.path == "/ocr/text":
|
||||
seen["body"] = request.read()
|
||||
return httpx.Response(200, json=_text_body())
|
||||
if request.url.path == "/parse":
|
||||
seen["body"] = request.read()
|
||||
return httpx.Response(200, json=_parse_body())
|
||||
return httpx.Response(200, content=_zip_bytes())
|
||||
|
||||
return _transport_for(handler)
|
||||
|
||||
async def test_recognize_text_labels_pdf_as_pdf(self):
|
||||
seen = {}
|
||||
t = self._capturing_transport(seen)
|
||||
await t.recognize_text(image=_PDF_BODY, source=_source(), call_id="c1")
|
||||
assert b'filename="doc.pdf"' in seen["body"]
|
||||
assert b"Content-Type: application/pdf" in seen["body"]
|
||||
|
||||
async def test_parse_layout_labels_pdf_as_pdf(self):
|
||||
seen = {}
|
||||
t = self._capturing_transport(seen)
|
||||
await t.parse_layout(image=_PDF_BODY, source=_source(), call_id="c1")
|
||||
assert b'filename="doc.pdf"' in seen["body"]
|
||||
assert b"Content-Type: application/pdf" in seen["body"]
|
||||
|
||||
async def test_parse_layout_keeps_image_label_for_images(self):
|
||||
seen = {}
|
||||
t = self._capturing_transport(seen)
|
||||
await t.parse_layout(image=b"\xff\xd8\xff\xe0abc", source=_source(), call_id="c1")
|
||||
assert b'filename="image.jpg"' in seen["body"]
|
||||
assert b"Content-Type: image/jpeg" in seen["body"]
|
||||
|
||||
|
||||
class TestParseLayout:
|
||||
async def test_full_chain_relative_url(self):
|
||||
t = _transport_for(
|
||||
|
||||
Reference in New Issue
Block a user