"""Tests for the Ollama VLM client.""" import base64 import json from io import BytesIO from unittest.mock import AsyncMock, MagicMock import pytest from PIL import Image from odoo_ocr.clients.base import BaseVLMClient from odoo_ocr.clients.ollama import OllamaClient from odoo_ocr.config import Settings from odoo_ocr.schemas import ExtractedInvoice @pytest.fixture def settings() -> Settings: return Settings( ollama_base_url="http://test-ollama:11434", cache=Settings().model_dump()["cache"] | {"enabled": False}, ) @pytest.mark.asyncio async def test_complete_parses_response(settings: Settings) -> None: client = OllamaClient(settings) fake_response = { "model": "glm-ocr", "message": {"role": "assistant", "content": '{"text": "Invoice 123"}'}, "done": True, "prompt_eval_count": 100, "eval_count": 20, } resp = MagicMock() resp.json.return_value = fake_response resp.raise_for_status = lambda: None client.http = AsyncMock() client.http.post.return_value = resp img = Image.new("RGB", (50, 50), color="red") response = await client.complete( system_prompt="ocr", user_prompt="read", images=[img], ) assert response.content == '{"text": "Invoice 123"}' assert response.model == "glm-ocr" assert response.prompt_tokens == 100 assert response.completion_tokens == 20 def test_parse_json_extracted_invoice() -> None: data = { "vendor_name": "Acme", "invoice_number": "1", "invoice_date": "2024-01-01", "line_items": [], "subtotal": "100", "tax_total": "20", "total": "120", } result = BaseVLMClient.parse_json(json.dumps(data), ExtractedInvoice) assert isinstance(result, ExtractedInvoice) assert result.vendor_name == "Acme" def test_encode_image_roundtrip() -> None: img = Image.new("RGB", (10, 10), color="blue") encoded = OllamaClient._encode_image(img) # type: ignore[attr-defined] decoded = Image.open(BytesIO(base64.b64decode(encoded))) assert decoded.size == (10, 10)