"""Tests for the GenAI + OCR factories (Task 4.3). The factories pick between fake and real clients based on ``IX_TEST_MODE``. CI runs with ``IX_TEST_MODE=fake``, production runs without — so the selection knob is the one lever between hermetic CI and real clients. """ from __future__ import annotations from ix.config import AppConfig from ix.genai import make_genai_client from ix.genai.fake import FakeGenAIClient from ix.genai.ollama_client import OllamaClient from ix.ocr import make_ocr_client from ix.ocr.fake import FakeOCRClient from ix.ocr.surya_client import SuryaOCRClient def _cfg(**overrides: object) -> AppConfig: """Build an AppConfig without loading the repo's .env.example.""" return AppConfig(_env_file=None, **overrides) # type: ignore[call-arg] class TestGenAIFactory: def test_fake_mode_returns_fake(self) -> None: cfg = _cfg(test_mode="fake") client = make_genai_client(cfg) assert isinstance(client, FakeGenAIClient) def test_production_returns_ollama_with_configured_url(self) -> None: cfg = _cfg( test_mode=None, ollama_url="http://ollama.host:11434", genai_call_timeout_seconds=42, ) client = make_genai_client(cfg) assert isinstance(client, OllamaClient) # Inspect the private attrs for binding correctness. assert client._base_url == "http://ollama.host:11434" assert client._per_call_timeout_s == 42 class TestOCRFactory: def test_fake_mode_returns_fake(self) -> None: cfg = _cfg(test_mode="fake") client = make_ocr_client(cfg) assert isinstance(client, FakeOCRClient) def test_production_surya_returns_surya(self) -> None: cfg = _cfg(test_mode=None, ocr_engine="surya") client = make_ocr_client(cfg) assert isinstance(client, SuryaOCRClient) def test_unknown_engine_raises(self) -> None: cfg = _cfg(test_mode=None, ocr_engine="tesseract") import pytest with pytest.raises(ValueError, match="ocr_engine"): make_ocr_client(cfg)