document-extract-agent / tests /test_config.py
kennethzychew's picture
phase 5: set CONFIDENCE_THRESHOLD=0.50, record SROIE results (T10)
51f6fc8
Raw
History Blame Contribute Delete
4.56 kB
"""Unit tests for the configuration loader.
Tests pass ``_env_file=None`` and explicit field overrides so they are fully
isolated from any real ``.env`` file or ambient environment variables.
"""
from __future__ import annotations
from pathlib import Path
from typing import Any
import pytest
from pydantic import ValidationError
from doc_agent.config import (
MULTIMODAL_BACKENDS,
ConfigError,
Settings,
load_config,
)
def _load(**overrides: Any) -> Settings:
"""Load config in isolation from the ambient environment.
Args:
**overrides: Field overrides forwarded to ``load_config``.
Returns:
A validated ``Settings`` instance.
"""
return load_config(_env_file=None, **overrides)
def test_valid_gemini_config_parses() -> None:
"""A Gemini backend with an API key and vision_direct parses cleanly."""
settings = _load(
extraction_backend="gemini",
gemini_api_key="test-key",
image_strategy="vision_direct",
)
assert settings.extraction_backend == "gemini"
assert settings.gemini_api_key == "test-key"
assert settings.image_strategy == "vision_direct"
def test_valid_ollama_config_parses() -> None:
"""An Ollama backend with ocr_then_text needs no API key and parses."""
settings = _load(
extraction_backend="ollama",
image_strategy="ocr_then_text",
)
assert settings.extraction_backend == "ollama"
assert settings.image_strategy == "ocr_then_text"
def test_gemini_requires_api_key() -> None:
"""Selecting Gemini without a key fails fast with an actionable message."""
with pytest.raises(ConfigError) as exc_info:
_load(
extraction_backend="gemini",
gemini_api_key="",
image_strategy="ocr_then_text",
)
assert "GEMINI_API_KEY" in str(exc_info.value)
def test_gemini_blank_api_key_is_rejected() -> None:
"""A whitespace-only key is treated as missing."""
with pytest.raises(ConfigError):
_load(
extraction_backend="gemini",
gemini_api_key=" ",
image_strategy="ocr_then_text",
)
def test_vision_direct_requires_multimodal_backend() -> None:
"""vision_direct with a text-only backend fails fast with guidance."""
with pytest.raises(ConfigError) as exc_info:
_load(
extraction_backend="ollama",
image_strategy="vision_direct",
)
message = str(exc_info.value)
assert "vision_direct" in message
assert "ocr_then_text" in message
def test_unknown_backend_is_rejected() -> None:
"""An unsupported backend name is rejected by the type constraint."""
with pytest.raises(ConfigError):
_load(extraction_backend="openai", gemini_api_key="x")
def test_confidence_threshold_out_of_range_is_rejected() -> None:
"""A threshold outside [0, 1] fails validation."""
with pytest.raises(ConfigError):
_load(
extraction_backend="ollama",
image_strategy="ocr_then_text",
confidence_threshold=1.5,
)
def test_defaults_are_applied() -> None:
"""Unset optional fields fall back to the documented defaults."""
settings = _load(
extraction_backend="ollama",
image_strategy="ocr_then_text",
)
assert settings.gemini_model == "gemini-flash-latest"
assert settings.ollama_host == "http://localhost:11434"
assert settings.ollama_model == "qwen2.5:7b"
assert settings.confidence_threshold == pytest.approx(0.50)
def test_paths_are_path_objects() -> None:
"""Path-typed settings are coerced to ``pathlib.Path``."""
settings = _load(
extraction_backend="ollama",
image_strategy="ocr_then_text",
)
assert isinstance(settings.inbox_dir, Path)
assert isinstance(settings.processed_dir, Path)
assert isinstance(settings.review_dir, Path)
assert isinstance(settings.export_dir, Path)
assert isinstance(settings.db_path, Path)
def test_settings_model_raises_validation_error_directly() -> None:
"""Constructing ``Settings`` directly surfaces a pydantic ValidationError."""
with pytest.raises(ValidationError):
Settings(
_env_file=None,
extraction_backend="gemini",
gemini_api_key="",
image_strategy="ocr_then_text",
)
def test_only_gemini_is_multimodal() -> None:
"""Ollama is not a multimodal backend; Gemini is."""
assert "gemini" in MULTIMODAL_BACKENDS
assert "ollama" not in MULTIMODAL_BACKENDS