feat(gui): charger le dictionnaires.yml externe éditable en frozen (P1-4)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
82
tests/unit/test_gui_v6_config_paths.py
Normal file
82
tests/unit/test_gui_v6_config_paths.py
Normal file
@@ -0,0 +1,82 @@
|
||||
"""Résolution du dictionnaires.yml externe éditable (P1-4).
|
||||
|
||||
Pur : on simule frozen via monkeypatch (sys.frozen / sys.executable / _MEIPASS),
|
||||
aucun display, aucun modèle.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import gui_v6.config_paths as cp
|
||||
|
||||
|
||||
def _make_bundle(tmp_path: Path) -> Path:
|
||||
bundle = tmp_path / "bundle"
|
||||
(bundle / "config").mkdir(parents=True)
|
||||
(bundle / "config" / "dictionnaires.yml").write_text("whitelist_phrases: []\n", encoding="utf-8")
|
||||
return bundle
|
||||
|
||||
|
||||
def test_dev_returns_repo_config_when_present(monkeypatch):
|
||||
# En dev (non frozen) : pointe la config embarquée si elle existe.
|
||||
monkeypatch.setattr(cp.sys, "frozen", False, raising=False)
|
||||
path = cp.resolve_user_config_path()
|
||||
assert path is not None
|
||||
assert path.name == "dictionnaires.yml"
|
||||
assert path.exists()
|
||||
|
||||
|
||||
def test_frozen_copies_bundle_on_first_launch(tmp_path, monkeypatch):
|
||||
bundle = _make_bundle(tmp_path)
|
||||
exe_dir = tmp_path / "exe"
|
||||
exe_dir.mkdir()
|
||||
monkeypatch.setattr(cp.sys, "frozen", True, raising=False)
|
||||
monkeypatch.setattr(cp.sys, "_MEIPASS", str(bundle), raising=False)
|
||||
monkeypatch.setattr(cp.sys, "executable", str(exe_dir / "Anonymisation.exe"), raising=False)
|
||||
|
||||
out = cp.resolve_user_config_path()
|
||||
expected = exe_dir / "config" / "dictionnaires.yml"
|
||||
assert out == expected
|
||||
assert expected.exists() # copié depuis le bundle au 1er lancement
|
||||
assert expected.read_text(encoding="utf-8") == "whitelist_phrases: []\n"
|
||||
|
||||
|
||||
def test_frozen_keeps_existing_user_config(tmp_path, monkeypatch):
|
||||
bundle = _make_bundle(tmp_path)
|
||||
exe_dir = tmp_path / "exe"
|
||||
(exe_dir / "config").mkdir(parents=True)
|
||||
user_cfg = exe_dir / "config" / "dictionnaires.yml"
|
||||
user_cfg.write_text("whitelist_phrases: [HOPITAL_LOCAL]\n", encoding="utf-8")
|
||||
monkeypatch.setattr(cp.sys, "frozen", True, raising=False)
|
||||
monkeypatch.setattr(cp.sys, "_MEIPASS", str(bundle), raising=False)
|
||||
monkeypatch.setattr(cp.sys, "executable", str(exe_dir / "Anonymisation.exe"), raising=False)
|
||||
|
||||
out = cp.resolve_user_config_path()
|
||||
assert out == user_cfg
|
||||
# Ne JAMAIS écraser la perso établissement existante.
|
||||
assert "HOPITAL_LOCAL" in out.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_dev_returns_none_when_config_missing(tmp_path, monkeypatch):
|
||||
# En dev, si la config embarquée est absente : on renvoie None (pas de création).
|
||||
monkeypatch.setattr(cp.sys, "frozen", False, raising=False)
|
||||
monkeypatch.setattr(cp, "_bundled_config", lambda: tmp_path / "absent" / "dictionnaires.yml")
|
||||
assert cp.resolve_user_config_path() is None
|
||||
|
||||
|
||||
def test_frozen_copy_failure_falls_back(tmp_path, monkeypatch):
|
||||
# En frozen, si la copie échoue (ex. droits) : fallback sur la config embarquée, sans crash.
|
||||
bundle = _make_bundle(tmp_path)
|
||||
exe_dir = tmp_path / "exe"
|
||||
exe_dir.mkdir()
|
||||
monkeypatch.setattr(cp.sys, "frozen", True, raising=False)
|
||||
monkeypatch.setattr(cp.sys, "_MEIPASS", str(bundle), raising=False)
|
||||
monkeypatch.setattr(cp.sys, "executable", str(exe_dir / "Anonymisation.exe"), raising=False)
|
||||
|
||||
def _boom(*_args, **_kwargs):
|
||||
raise PermissionError("accès refusé")
|
||||
|
||||
monkeypatch.setattr(cp.shutil, "copyfile", _boom)
|
||||
|
||||
out = cp.resolve_user_config_path()
|
||||
assert out == cp._bundled_config()
|
||||
Reference in New Issue
Block a user