Spaces:
Running
Running
Claude
feat(sprint-H.2.c-d)!: suppression complète de adapters/legacy_engines/ et adapters/legacy_pipelines/
f54bb20 unverified | """Tests Sprint 30 — polish, accessibilité et DX. | |
| Sprint 30 livre quatre durcissements transverses : | |
| 1. ``picarones/i18n.py`` : chargement thread-safe via verrou explicite, | |
| ``lru_cache`` sur ``get_labels``, ``reload_translations()`` exposé. | |
| 2. ``BaseOCREngine._safe_version()`` log la stacktrace en DEBUG au | |
| lieu de swallow vers ``"unknown"`` silencieusement. | |
| 3. Badges CER WCAG : icône unicode + pattern de bordure + ``aria-label`` | |
| contextuel — la couleur n'est plus la seule info visuelle. | |
| 4. Pre-commit hooks : ``.pre-commit-config.yaml`` + section | |
| ``CONTRIBUTING.md``. | |
| 5. ``CHANGELOG.md`` rattrapé Sprints 10-30. | |
| 6. ``SPECS.md`` annexé d'un addendum couvrant Sprints 16-30. | |
| """ | |
| from __future__ import annotations | |
| from pathlib import Path | |
| ROOT = Path(__file__).parent.parent.parent | |
| # --------------------------------------------------------------------------- | |
| # 1. i18n thread-safe + lru_cache | |
| # --------------------------------------------------------------------------- | |
| class TestI18nCache: | |
| def test_get_labels_returns_dict(self): | |
| from picarones.reports.i18n import get_labels | |
| labels = get_labels("fr") | |
| assert isinstance(labels, dict) | |
| assert len(labels) > 5 | |
| def test_get_labels_unknown_falls_back_to_fr(self): | |
| from picarones.reports.i18n import get_labels | |
| fr = get_labels("fr") | |
| unknown = get_labels("xx-pas-existante") | |
| # Le fallback doit être le contenu fr | |
| assert unknown == fr | |
| def test_get_labels_cached(self): | |
| from picarones.reports import i18n | |
| i18n.reload_translations() | |
| # Premier appel — peuple le cache | |
| i18n.get_labels("fr") | |
| # Inspection : le cache lru a un hit | |
| # (lru_cache expose cache_info() sur la fonction wrappée) | |
| info_before = i18n._get_labels_cached.cache_info() | |
| i18n.get_labels("fr") | |
| info_after = i18n._get_labels_cached.cache_info() | |
| assert info_after.hits > info_before.hits | |
| def test_reload_translations_clears_cache(self): | |
| from picarones.reports import i18n | |
| i18n.get_labels("fr") | |
| info_before = i18n._get_labels_cached.cache_info() | |
| assert info_before.currsize >= 1 | |
| i18n.reload_translations() | |
| info_after = i18n._get_labels_cached.cache_info() | |
| assert info_after.currsize == 0 | |
| # Section retirée au sprint H.2.d : ``BaseOCREngine._safe_version()`` | |
| # n'existe plus (BaseOCREngine supprimé avec ``adapters/legacy_engines/``). | |
| # Le contrat équivalent côté canonique (``BaseOCRAdapter`` n'a pas de | |
| # ``version()``) est testé dans | |
| # ``tests/app/test_sprint_h2b_canonical_in_runner.py`` via | |
| # ``test_canonical_adapter_version_unknown``. | |
| # --------------------------------------------------------------------------- | |
| # 3. Badges CER WCAG (rapport HTML) | |
| # --------------------------------------------------------------------------- | |
| class TestBadgesAccessibility: | |
| def test_app_js_exposes_tier_helpers(self): | |
| path = ROOT / "picarones" / "reports" / "html" / "templates" / "_app.js" | |
| src = path.read_text(encoding="utf-8") | |
| for fn in ("cerTier", "cerTierIcon", "cerTierLabel"): | |
| assert f"function {fn}" in src, ( | |
| f"_app.js doit exposer ``function {fn}`` (Sprint 30 a11y)" | |
| ) | |
| def test_styles_define_tier_patterns(self): | |
| path = ROOT / "picarones" / "reports" / "html" / "templates" / "_styles.css" | |
| src = path.read_text(encoding="utf-8") | |
| for tier in ("excellent", "acceptable", "mediocre", "critical"): | |
| assert f'data-cer-tier="{tier}"' in src, ( | |
| f"_styles.css doit définir un pattern pour le tier {tier!r}" | |
| ) | |
| # Au moins quatre styles de bordure différents | |
| assert "border: 1.5px solid" in src | |
| assert "border: 1.5px dashed" in src | |
| assert "border: 1.5px dotted" in src | |
| assert "border: 1.5px double" in src | |
| def test_main_badge_carries_data_attr_and_aria(self): | |
| path = ROOT / "picarones" / "reports" / "html" / "templates" / "_app.js" | |
| src = path.read_text(encoding="utf-8") | |
| assert "setAttribute('data-cer-tier'" in src | |
| assert "setAttribute('aria-label'" in src | |
| # --------------------------------------------------------------------------- | |
| # 4. Pre-commit + CONTRIBUTING | |
| # --------------------------------------------------------------------------- | |
| class TestPreCommitInfra: | |
| def test_pre_commit_config_exists(self): | |
| path = ROOT / ".pre-commit-config.yaml" | |
| assert path.exists() | |
| text = path.read_text(encoding="utf-8") | |
| # Doit référencer ruff (alignement avec le job CI ``lint``) | |
| assert "ruff" in text.lower() | |
| def test_pre_commit_yaml_is_well_formed(self): | |
| import yaml | |
| path = ROOT / ".pre-commit-config.yaml" | |
| data = yaml.safe_load(path.read_text(encoding="utf-8")) | |
| assert isinstance(data, dict) | |
| assert "repos" in data | |
| assert isinstance(data["repos"], list) | |
| assert any( | |
| "ruff" in (repo.get("repo") or "").lower() | |
| for repo in data["repos"] | |
| ) | |
| def test_contributing_documents_pre_commit(self): | |
| path = ROOT / "CONTRIBUTING.md" | |
| text = path.read_text(encoding="utf-8") | |
| assert "pre-commit" in text.lower() | |
| assert "pre-commit install" in text | |
| # --------------------------------------------------------------------------- | |
| # 5. Documentation rattrapée | |
| # --------------------------------------------------------------------------- | |
| class TestChangelogAndSpecsUpdated: | |
| def test_changelog_mentions_recent_sprints(self): | |
| text = (ROOT / "CHANGELOG.md").read_text(encoding="utf-8") | |
| # Backport Sprints 10-22 et 23-30 doivent être mentionnés | |
| for sprint in ("Sprint 11", "Sprint 17", "Sprint 19", "Sprint 22", | |
| "Sprint 24", "Sprint 27", "Sprint 30"): | |
| assert sprint in text, ( | |
| f"CHANGELOG.md doit mentionner {sprint} (Sprint 30 backport)" | |
| ) | |
| def test_specs_addendum_present(self): | |
| text = (ROOT / "SPECS.md").read_text(encoding="utf-8") | |
| assert "Addendum" in text | |
| # Au moins quatre des nouvelles fonctionnalités annexées | |
| for keyword in ("narrative", "Pareto", "glossaire", "snapshots"): | |
| assert keyword in text.lower() or keyword in text, ( | |
| f"SPECS.md addendum doit couvrir {keyword!r}" | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # 6. Intégration : un rapport généré porte les attributs WCAG dans son JS | |
| # --------------------------------------------------------------------------- | |
| class TestGeneratedReportCarriesA11y: | |
| def test_generated_html_embeds_tier_helpers(self, tmp_path): | |
| from picarones.evaluation import synthetic as fixtures | |
| from picarones.reports.html.generator import ReportGenerator | |
| b = fixtures.generate_sample_benchmark(n_docs=4) | |
| out = tmp_path / "rapport.html" | |
| ReportGenerator(b, lang="fr").generate(out) | |
| html = out.read_text(encoding="utf-8") | |
| # Les fonctions JS doivent figurer dans le bundle inline | |
| assert "cerTier" in html | |
| assert "cerTierIcon" in html | |
| # Les règles CSS pour les patterns aussi | |
| assert 'data-cer-tier="excellent"' in html | |