IIIF-Studio / backend /tests /test_profiles.py
Claude
fix: Sprint 6 — CLAUDE.md endpoint sync, UncertaintyConfig validator, annotation docs
894cbe3 unverified
"""
Tests de chargement et validation des profils JSON — un test par profil.
"""
# 1. stdlib
import json
from pathlib import Path
# 2. third-party
import pytest
from pydantic import ValidationError
# 3. local
from app.schemas.corpus_profile import CorpusProfile, LayerType, ScriptType
PROFILES_DIR = Path(__file__).resolve().parent.parent.parent / "profiles"
PROFILE_FILES = [
"medieval-illuminated.json",
"medieval-textual.json",
"early-modern-print.json",
"modern-handwritten.json",
]
def load_profile(filename: str) -> CorpusProfile:
path = PROFILES_DIR / filename
with path.open(encoding="utf-8") as f:
data = json.load(f)
return CorpusProfile.model_validate(data)
# ---------------------------------------------------------------------------
# Tests de chargement
# ---------------------------------------------------------------------------
def test_medieval_illuminated_loads():
profile = load_profile("medieval-illuminated.json")
assert profile.profile_id == "medieval-illuminated"
assert profile.script_type == ScriptType.CAROLINE
def test_medieval_textual_loads():
profile = load_profile("medieval-textual.json")
assert profile.profile_id == "medieval-textual"
assert profile.script_type == ScriptType.GOTHIC
def test_early_modern_print_loads():
profile = load_profile("early-modern-print.json")
assert profile.profile_id == "early-modern-print"
assert profile.script_type == ScriptType.PRINT
def test_modern_handwritten_loads():
profile = load_profile("modern-handwritten.json")
assert profile.profile_id == "modern-handwritten"
assert profile.script_type == ScriptType.CURSIVE
# ---------------------------------------------------------------------------
# Tests de cohérence
# ---------------------------------------------------------------------------
@pytest.mark.parametrize("filename", PROFILE_FILES)
def test_profile_has_required_fields(filename: str):
profile = load_profile(filename)
assert profile.profile_id
assert profile.label
assert len(profile.language_hints) >= 1
assert len(profile.active_layers) >= 1
assert "primary" in profile.prompt_templates
@pytest.mark.parametrize("filename", PROFILE_FILES)
def test_profile_active_layers_are_valid_layer_types(filename: str):
profile = load_profile(filename)
valid_values = {lt.value for lt in LayerType}
for layer in profile.active_layers:
assert layer.value in valid_values
@pytest.mark.parametrize("filename", PROFILE_FILES)
def test_profile_uncertainty_config_bounds(filename: str):
profile = load_profile(filename)
assert 0.0 <= profile.uncertainty_config.flag_below <= 1.0
assert 0.0 <= profile.uncertainty_config.min_acceptable <= 1.0
assert profile.uncertainty_config.min_acceptable <= profile.uncertainty_config.flag_below
@pytest.mark.parametrize("filename", PROFILE_FILES)
def test_profile_is_frozen(filename: str):
profile = load_profile(filename)
with pytest.raises((TypeError, ValidationError)):
profile.label = "Hacked" # type: ignore[misc]
@pytest.mark.parametrize("filename", PROFILE_FILES)
def test_profile_prompt_templates_point_to_txt_files(filename: str):
profile = load_profile(filename)
for key, path in profile.prompt_templates.items():
assert path.endswith(".txt"), f"Template '{key}' doit pointer vers un .txt"
assert path.startswith("prompts/"), f"Template '{key}' doit être dans prompts/"
def test_medieval_illuminated_has_iconography():
profile = load_profile("medieval-illuminated.json")
assert LayerType.ICONOGRAPHY_DETECTION in profile.active_layers
def test_medieval_illuminated_has_iconography_prompt():
profile = load_profile("medieval-illuminated.json")
assert "iconography" in profile.prompt_templates
def test_early_modern_print_no_iconography():
profile = load_profile("early-modern-print.json")
assert LayerType.ICONOGRAPHY_DETECTION not in profile.active_layers
def test_modern_handwritten_no_iconography():
profile = load_profile("modern-handwritten.json")
assert LayerType.ICONOGRAPHY_DETECTION not in profile.active_layers
# ---------------------------------------------------------------------------
# Tests UncertaintyConfig cross-field validation
# ---------------------------------------------------------------------------
def test_uncertainty_config_flag_below_must_exceed_min():
"""flag_below < min_acceptable doit lever une erreur."""
from app.schemas.corpus_profile import UncertaintyConfig
with pytest.raises(ValidationError):
UncertaintyConfig(flag_below=0.2, min_acceptable=0.5)
def test_uncertainty_config_valid():
"""flag_below >= min_acceptable est accepté."""
from app.schemas.corpus_profile import UncertaintyConfig
uc = UncertaintyConfig(flag_below=0.5, min_acceptable=0.3)
assert uc.flag_below == 0.5