Files
bigwo/tests/test_image_parser.py

136 lines
5.0 KiB
Python
Raw Permalink Normal View History

"""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 != ""