Add tests for extract_barcode_from_image
9 new test cases covering: plain digit response, thinking-tag stripping, digit extraction from surrounding text, empty/no-digit response → None, exception handling → None, correct model/URL forwarding, EAN_PROMPT usage, and base64 image encoding in request payload. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2490293dcf
commit
55c71823d1
1 changed files with 91 additions and 1 deletions
|
|
@ -1,8 +1,11 @@
|
||||||
"""Tests für die Vision-LLM JSON-Extraktion."""
|
"""Tests für die Vision-LLM JSON-Extraktion."""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from musiksammlung.vision_llm import _extract_json
|
from musiksammlung.vision_llm import EAN_PROMPT, _extract_json, extract_barcode_from_image
|
||||||
|
|
||||||
|
|
||||||
def test_extract_pure_json():
|
def test_extract_pure_json():
|
||||||
|
|
@ -35,3 +38,90 @@ def test_extract_json_empty_raises():
|
||||||
def test_extract_json_no_json_raises():
|
def test_extract_json_no_json_raises():
|
||||||
with pytest.raises(ValueError, match="Kein JSON"):
|
with pytest.raises(ValueError, match="Kein JSON"):
|
||||||
_extract_json("Hier ist kein JSON, nur Text.")
|
_extract_json("Hier ist kein JSON, nur Text.")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# extract_barcode_from_image
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _mock_response(content: str) -> MagicMock:
|
||||||
|
"""Erstellt einen Mock-httpx-Response mit gegebenem LLM-Inhalt."""
|
||||||
|
resp = MagicMock()
|
||||||
|
resp.json.return_value = {"message": {"content": content}}
|
||||||
|
resp.raise_for_status.return_value = None
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
|
class TestExtractBarcodeFromImage:
|
||||||
|
"""Tests für extract_barcode_from_image."""
|
||||||
|
|
||||||
|
def test_returns_digits_from_plain_response(self, tmp_path: Path) -> None:
|
||||||
|
img = tmp_path / "cover.jpg"
|
||||||
|
img.write_bytes(b"fake")
|
||||||
|
with patch("httpx.post", return_value=_mock_response("4006408262121")):
|
||||||
|
result = extract_barcode_from_image(img)
|
||||||
|
assert result == "4006408262121"
|
||||||
|
|
||||||
|
def test_strips_thinking_tags(self, tmp_path: Path) -> None:
|
||||||
|
img = tmp_path / "cover.jpg"
|
||||||
|
img.write_bytes(b"fake")
|
||||||
|
raw = "<think>Ich suche den Barcode...</think>\n4006408262121"
|
||||||
|
with patch("httpx.post", return_value=_mock_response(raw)):
|
||||||
|
result = extract_barcode_from_image(img)
|
||||||
|
assert result == "4006408262121"
|
||||||
|
|
||||||
|
def test_extracts_digits_from_surrounding_text(self, tmp_path: Path) -> None:
|
||||||
|
img = tmp_path / "cover.jpg"
|
||||||
|
img.write_bytes(b"fake")
|
||||||
|
raw = "Der EAN-Barcode lautet: 0028943753227."
|
||||||
|
with patch("httpx.post", return_value=_mock_response(raw)):
|
||||||
|
result = extract_barcode_from_image(img)
|
||||||
|
assert result == "0028943753227"
|
||||||
|
|
||||||
|
def test_returns_none_when_no_digits(self, tmp_path: Path) -> None:
|
||||||
|
img = tmp_path / "cover.jpg"
|
||||||
|
img.write_bytes(b"fake")
|
||||||
|
with patch("httpx.post", return_value=_mock_response("Kein Barcode erkannt.")):
|
||||||
|
result = extract_barcode_from_image(img)
|
||||||
|
assert result is None
|
||||||
|
|
||||||
|
def test_returns_none_on_empty_response(self, tmp_path: Path) -> None:
|
||||||
|
img = tmp_path / "cover.jpg"
|
||||||
|
img.write_bytes(b"fake")
|
||||||
|
with patch("httpx.post", return_value=_mock_response("")):
|
||||||
|
result = extract_barcode_from_image(img)
|
||||||
|
assert result is None
|
||||||
|
|
||||||
|
def test_returns_none_on_exception(self, tmp_path: Path) -> None:
|
||||||
|
img = tmp_path / "cover.jpg"
|
||||||
|
img.write_bytes(b"fake")
|
||||||
|
with patch("httpx.post", side_effect=Exception("connection refused")):
|
||||||
|
result = extract_barcode_from_image(img)
|
||||||
|
assert result is None
|
||||||
|
|
||||||
|
def test_sends_correct_model_and_url(self, tmp_path: Path) -> None:
|
||||||
|
img = tmp_path / "cover.jpg"
|
||||||
|
img.write_bytes(b"fake")
|
||||||
|
with patch("httpx.post", return_value=_mock_response("1234567890123")) as mock_post:
|
||||||
|
extract_barcode_from_image(img, model="my-model", base_url="http://myhost:11434")
|
||||||
|
url, = mock_post.call_args.args
|
||||||
|
assert url == "http://myhost:11434/api/chat"
|
||||||
|
assert mock_post.call_args.kwargs["json"]["model"] == "my-model"
|
||||||
|
|
||||||
|
def test_uses_ean_prompt(self, tmp_path: Path) -> None:
|
||||||
|
img = tmp_path / "cover.jpg"
|
||||||
|
img.write_bytes(b"fake")
|
||||||
|
with patch("httpx.post", return_value=_mock_response("9783161484100")) as mock_post:
|
||||||
|
extract_barcode_from_image(img)
|
||||||
|
messages = mock_post.call_args.kwargs["json"]["messages"]
|
||||||
|
assert messages[0]["content"] == EAN_PROMPT
|
||||||
|
|
||||||
|
def test_image_is_base64_encoded_in_request(self, tmp_path: Path) -> None:
|
||||||
|
img = tmp_path / "cover.jpg"
|
||||||
|
img.write_bytes(b"test image data")
|
||||||
|
with patch("httpx.post", return_value=_mock_response("0000000000000")) as mock_post:
|
||||||
|
extract_barcode_from_image(img)
|
||||||
|
messages = mock_post.call_args.kwargs["json"]["messages"]
|
||||||
|
import base64
|
||||||
|
expected_b64 = base64.b64encode(b"test image data").decode()
|
||||||
|
assert messages[0]["images"] == [expected_b64]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue