"""Tests d'intégration CLI — smoke + chemins clés (Phase 6 chantier). Avant Phase 6 : seul ``test_chantier4.py::TestCliWorkflows`` testait les commandes ``diagnose``/``economics``/``edition`` via leur ``--help``. Les autres commandes (``run``, ``history``, ``compare``, ``robustness``, ``metrics``, ``info``, ``engines``, ``demo``, ``report``) n'avaient aucun test d'intégration — une régression sur leur enregistrement Click ou leur signature passait inaperçue jusqu'à ce qu'un utilisateur réel les invoque. Couvre : 1. **Smoke ``--help``** : toutes les commandes répondent sans exit code != 0 et listent leurs options principales. 2. **Demo end-to-end** : ``picarones demo`` génère un rapport HTML complet (sans corpus, sans moteur réel) — c'est le chemin que la doc README pointe pour l'évaluation rapide. 3. **Engines matrix** : ``picarones engines`` affiche les 8 moteurs du catalogue canonique (cohérence avec ``/api/engines``). 4. **Info dependencies** : ``picarones info`` liste Picarones + dépendances clé. 5. **History --regression** : sans base, retourne un message lisible au lieu de planter. Pas de tests qui invoquent un vrai benchmark (corpus + moteur réel) — ceux-ci vivent dans ``tests/integration/`` quand un OCR est dispo. """ from __future__ import annotations import pytest @pytest.fixture def runner(): from click.testing import CliRunner return CliRunner() @pytest.fixture def cli(): from picarones.interfaces.cli import cli as cli_group return cli_group # ────────────────────────────────────────────────────────────────────── # 1. Smoke --help pour toutes les commandes # ────────────────────────────────────────────────────────────────────── _ALL_COMMANDS = [ "run", "diagnose", "economics", "edition", "compare", "robustness", "history", "metrics", "info", "engines", "demo", "report", "serve", ] class TestSmokeHelp: """Toutes les commandes CLI doivent répondre à ``--help`` sans crash et lister leurs options principales. Garde-fou contre une régression d'enregistrement Click.""" @pytest.mark.parametrize("cmd_name", _ALL_COMMANDS) def test_help_works(self, runner, cli, cmd_name: str) -> None: result = runner.invoke(cli, [cmd_name, "--help"]) assert result.exit_code == 0, ( f"`picarones {cmd_name} --help` a échoué : " f"exit={result.exit_code}, output={result.output[:500]}" ) # Le help doit au moins inclure le nom de la commande. assert cmd_name in result.output.lower() or "usage" in result.output.lower() def test_root_help_lists_all_commands(self, runner, cli) -> None: """``picarones --help`` doit lister toutes les sous-commandes canoniques — sinon une commande enregistrée mais non groupée passe inaperçue.""" result = runner.invoke(cli, ["--help"]) assert result.exit_code == 0 for cmd_name in _ALL_COMMANDS: assert cmd_name in result.output, ( f"Sous-commande ``{cmd_name}`` absente de " f"``picarones --help`` :\n{result.output}" ) # ────────────────────────────────────────────────────────────────────── # 2. Demo end-to-end : génération d'un rapport sans moteur # ────────────────────────────────────────────────────────────────────── class TestDemoCommand: """``picarones demo`` est le chemin d'évaluation rapide pour un utilisateur qui n'a pas Tesseract ni de corpus : il génère un rapport HTML synthétique pour explorer l'UI.""" def test_demo_generates_html_file(self, runner, cli, tmp_path): output = tmp_path / "demo.html" result = runner.invoke(cli, ["demo", "--output", str(output)]) assert result.exit_code == 0, result.output assert output.exists() assert output.stat().st_size > 5000, ( "Le rapport démo doit faire au moins 5 Ko " "(HTML + Chart.js inline + données synthétiques)" ) # Sanity : c'est bien du HTML. head = output.read_text(encoding="utf-8")[:200] assert "" in head or "