Suite de tests unitaires pour tous les modules pipeline qui ne dépendent pas du VLM — utiles pour garantir la non-régression après refactor et servir de spec vivante de chaque fonction. Fichiers : - tests/test_json_utils.py (20 tests) : parse_json_output + toutes les stratégies de récupération (fences, virgules manquantes, boucles vides, fermeture JSON, fallback _raw/_parse_error) - tests/test_deskew.py (11 tests) : détection Hough + correction, image synthétique + fixtures cache réel - tests/test_checkboxes.py (17 tests) : parse_ghs_injustifie, dark_ratio, inner_frac, et ground truth visuel sur 17 dossiers (mapping hash→OGC résolu au runtime pour éviter les constantes fragiles) - tests/test_validation.py (18 tests) : _check_cim10/ccam/ghm/ghs, cross-checks GHM↔GHS, annotate sur JSON vide et complet, preservation de l'input (copie défensive) - tests/test_schema.py (8 tests) : clean_dossier retire les champs debug, préserve les champs métier, compacte la validation, ne modifie pas l'input - tests/test_zones_config.py (8 tests) : load/save round-trip, merge avec defaults, résilience JSON corrompu, get_zone Total : 107 tests, 5.1 s d'exécution, tous passent. Aucune dépendance GPU, s'exécutent en CI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
86 lines
2.7 KiB
Python
86 lines
2.7 KiB
Python
"""Tests unitaires pour pipeline.zones_config."""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
import pytest
|
|
|
|
from pipeline.zones_config import (
|
|
DEFAULTS,
|
|
get_zone,
|
|
load_config,
|
|
save_config,
|
|
)
|
|
|
|
|
|
class TestLoadConfig:
|
|
def test_fichier_absent_retourne_defaults(self, tmp_path):
|
|
cfg = load_config(tmp_path / "inexistant.json")
|
|
assert cfg == DEFAULTS
|
|
|
|
def test_charge_depuis_fichier(self, tmp_path):
|
|
path = tmp_path / "zones.json"
|
|
custom = {
|
|
"recueil": {
|
|
"codage_reco": {"x1": 0.5, "y1": 0.1, "x2": 0.9, "y2": 0.4,
|
|
"description": "test"},
|
|
},
|
|
}
|
|
path.write_text(json.dumps(custom))
|
|
cfg = load_config(path)
|
|
assert cfg["recueil"]["codage_reco"]["x1"] == 0.5
|
|
|
|
def test_merge_avec_defaults(self, tmp_path):
|
|
"""Les zones non définies dans le fichier tombent en défaut."""
|
|
path = tmp_path / "zones.json"
|
|
partial = {
|
|
"recueil": {"codage_reco": {"x1": 0.1, "y1": 0.2, "x2": 0.3, "y2": 0.4}},
|
|
}
|
|
path.write_text(json.dumps(partial))
|
|
cfg = load_config(path)
|
|
# User override appliqué
|
|
assert cfg["recueil"]["codage_reco"]["x1"] == 0.1
|
|
# Default gardé pour l'autre zone
|
|
assert cfg["recueil"]["accord_checkbox"] == DEFAULTS["recueil"]["accord_checkbox"]
|
|
|
|
def test_json_corrompu_retombe_sur_defaults(self, tmp_path):
|
|
path = tmp_path / "corrupt.json"
|
|
path.write_text("{ not valid json [")
|
|
cfg = load_config(path)
|
|
assert cfg == DEFAULTS
|
|
|
|
|
|
class TestSaveConfig:
|
|
def test_save_puis_load_round_trip(self, tmp_path):
|
|
path = tmp_path / "zones.json"
|
|
original = {
|
|
"recueil": {
|
|
"codage_reco": {"x1": 0.11, "y1": 0.22, "x2": 0.33, "y2": 0.44,
|
|
"description": "abc"},
|
|
},
|
|
}
|
|
save_config(original, path)
|
|
reloaded = load_config(path)
|
|
assert reloaded["recueil"]["codage_reco"]["x1"] == 0.11
|
|
assert reloaded["recueil"]["codage_reco"]["description"] == "abc"
|
|
|
|
|
|
class TestGetZone:
|
|
def test_zone_existante(self):
|
|
z = get_zone("recueil", "codage_reco")
|
|
assert isinstance(z, tuple)
|
|
assert len(z) == 4
|
|
assert all(isinstance(v, float) for v in z)
|
|
|
|
def test_zone_inconnue_retourne_none(self):
|
|
assert get_zone("recueil", "zone_qui_nexiste_pas") is None
|
|
assert get_zone("page_fantaisiste", "whatever") is None
|
|
|
|
def test_config_explicite(self):
|
|
cfg = {
|
|
"recueil": {
|
|
"my_zone": {"x1": 0.0, "y1": 0.0, "x2": 1.0, "y2": 1.0},
|
|
},
|
|
}
|
|
assert get_zone("recueil", "my_zone", config=cfg) == (0.0, 0.0, 1.0, 1.0)
|