Files
bigwo/tests/test_image_parser.py
2026-03-02 17:38:28 +08:00

136 lines
5.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""ImageParser 单元测试"""
import base64
from unittest.mock import MagicMock
import pytest
from exceptions import ApiError, ParseError
from parsers.image_parser import ImageParser, DEFAULT_VISION_PROMPT
@pytest.fixture
def mock_api_client():
return MagicMock()
@pytest.fixture
def parser(mock_api_client):
return ImageParser(mock_api_client)
class TestSupportedExtensions:
def test_supports_png(self, parser):
assert ".png" in parser.supported_extensions()
def test_supports_jpg(self, parser):
assert ".jpg" in parser.supported_extensions()
def test_supports_jpeg(self, parser):
assert ".jpeg" in parser.supported_extensions()
def test_supports_bmp(self, parser):
assert ".bmp" in parser.supported_extensions()
def test_supports_gif(self, parser):
assert ".gif" in parser.supported_extensions()
def test_supports_webp(self, parser):
assert ".webp" in parser.supported_extensions()
def test_has_six_extensions(self, parser):
assert len(parser.supported_extensions()) == 6
class TestParse:
def test_successful_parse(self, mock_api_client, tmp_path):
"""成功解析图片文件,返回 Vision API 的文本描述"""
img = tmp_path / "photo.png"
img.write_bytes(b"\x89PNG\r\n\x1a\n" + b"\x00" * 20)
mock_api_client.vision.return_value = "图片中包含一段中文文字"
parser = ImageParser(mock_api_client)
result = parser.parse(str(img))
assert result == "图片中包含一段中文文字"
mock_api_client.vision.assert_called_once()
def test_base64_encoding_correctness(self, mock_api_client, tmp_path):
"""验证传递给 API 的 base64 编码与文件内容一致"""
raw_bytes = b"\x89PNG\r\n\x1a\nSOME_IMAGE_DATA"
img = tmp_path / "check.png"
img.write_bytes(raw_bytes)
mock_api_client.vision.return_value = "ok"
parser = ImageParser(mock_api_client)
parser.parse(str(img))
call_args = mock_api_client.vision.call_args
sent_base64 = call_args.kwargs.get("image_base64") or call_args[1].get("image_base64") or call_args[0][1]
assert base64.b64decode(sent_base64) == raw_bytes
def test_system_prompt_passed_to_api(self, mock_api_client, tmp_path):
"""验证使用了正确的系统提示词,且包含文件名上下文"""
img = tmp_path / "prompt.png"
img.write_bytes(b"\x00")
mock_api_client.vision.return_value = "text"
parser = ImageParser(mock_api_client)
parser.parse(str(img))
call_args = mock_api_client.vision.call_args
sent_prompt = call_args.kwargs.get("system_prompt") or call_args[0][0]
assert DEFAULT_VISION_PROMPT in sent_prompt
assert "prompt" in sent_prompt
def test_file_not_found_raises_parse_error(self, parser):
"""文件不存在时抛出 ParseError"""
with pytest.raises(ParseError) as exc_info:
parser.parse("/nonexistent/path/missing.png")
assert exc_info.value.file_name == "missing.png"
assert "文件读取失败" in exc_info.value.reason
def test_unreadable_file_raises_parse_error(self, mock_api_client, tmp_path):
"""文件无法读取时抛出 ParseError使用目录路径模拟不可读文件"""
dir_path = tmp_path / "fakefile.jpg"
dir_path.mkdir()
parser = ImageParser(mock_api_client)
with pytest.raises(ParseError) as exc_info:
parser.parse(str(dir_path))
assert exc_info.value.file_name == "fakefile.jpg"
assert "文件读取失败" in exc_info.value.reason
def test_api_error_raises_parse_error(self, mock_api_client, tmp_path):
"""API 调用失败时抛出 ParseError"""
img = tmp_path / "api_fail.png"
img.write_bytes(b"\x89PNG")
mock_api_client.vision.side_effect = ApiError("服务不可用", status_code=503)
parser = ImageParser(mock_api_client)
with pytest.raises(ParseError) as exc_info:
parser.parse(str(img))
assert exc_info.value.file_name == "api_fail.png"
assert "Vision API 调用失败" in exc_info.value.reason
def test_api_rate_limit_error_raises_parse_error(self, mock_api_client, tmp_path):
"""API 速率限制错误(经重试耗尽后)也被包装为 ParseError"""
img = tmp_path / "rate.png"
img.write_bytes(b"\x89PNG")
mock_api_client.vision.side_effect = ApiError("速率限制重试耗尽", status_code=429)
parser = ImageParser(mock_api_client)
with pytest.raises(ParseError) as exc_info:
parser.parse(str(img))
assert "Vision API 调用失败" in exc_info.value.reason
def test_parse_error_contains_filename_for_missing_file(self, parser):
"""ParseError 包含正确的文件名"""
with pytest.raises(ParseError) as exc_info:
parser.parse("/tmp/does_not_exist/myimage.jpeg")
assert exc_info.value.file_name == "myimage.jpeg"
assert exc_info.value.reason != ""