Claude commited on
Commit
a4a8458
·
unverified ·
1 Parent(s): d104975

fix(ci): tests HTR-United remote + bandit B324 SHA-1 cache

Browse files

Échecs CI sur la branche claude/fix-module-rewiring-MHssX :
``CI / Tests`` (Linux+macOS+Windows, Python 3.11/3.12) + ``CI /
Security scanners``.

## 1. Tests HTR-United remote en CI

Phase 4.4 du chantier post-rewrite avait fait passer le router
``/api/htr-united/catalogue`` de ``from_demo()`` exclusif à
``from_remote(timeout=5)`` avec fallback démo. En local avec
``PICARONES_HTR_UNITED_OFFLINE=1``, ``from_demo()`` est forcé et les
tests passent. En CI sans cette variable, ``from_remote()`` réussit
mais le catalogue distant retourne parfois des entrées avec
``id=""`` (schéma YAML évolutif), ce qui cassait
``test_known_entry_calls_importer`` (422 Pydantic au lieu de 200).

Sprint J (commit 9228764) avait déjà appliqué le fix à
``test_sprint6_web_interface.py::test_import_valid_entry``, mais
``tests/web/routers/test_s4_importers_router.py::test_known_entry_calls_importer``
avait été oublié. Même correctif appliqué : filtre sur entrées
non-vides + skip si catalogue uniquement entrées vides.

## 2. Security scanner — bandit B324 (SHA-1 high severity)

Phase 2.3 du chantier (commit 5e48c0b) avait introduit
``hashlib.sha1`` dans ``_engine_config_for_fingerprint`` pour
identifier un prompt LLM dans le nom du fichier partial — pas une
garantie crypto, juste un cache key court. Bandit (qui ne peut pas
deviner l'intention) signale ça en High severity B324.

Fix : ajout du flag ``usedforsecurity=False`` (Python 3.9+) qui
exprime explicitement l'intention non-crypto et neutralise le faux
positif.

Vérifications locales :
- ``bandit -r picarones/ -ll`` : 0 High, 19 Low (vs 1 High avant).
- Suite sans ``PICARONES_HTR_UNITED_OFFLINE`` : 4686 passed,
14 skipped (vs 1 failed avant).

https://claude.ai/code/session_01ArfZ8kcgv7Cyda7VbJVmpn

picarones/app/services/benchmark_runner.py CHANGED
@@ -528,8 +528,13 @@ def _engine_config_for_fingerprint(engine: Any) -> dict:
528
  # Hasher le prompt pour éviter de polluer le nom du fichier
529
  # partiel avec un prompt multi-lignes (et de fuiter le
530
  # contenu d'un prompt institutionnel dans un nom de fichier).
 
 
 
 
531
  cfg["prompt_sha1"] = hashlib.sha1(
532
  str(prompt).encode("utf-8"),
 
533
  ).hexdigest()[:12]
534
  llm = getattr(engine, "llm_adapter", None)
535
  if llm is not None:
 
528
  # Hasher le prompt pour éviter de polluer le nom du fichier
529
  # partiel avec un prompt multi-lignes (et de fuiter le
530
  # contenu d'un prompt institutionnel dans un nom de fichier).
531
+ # SHA-1 utilisé comme identifiant de cache uniquement
532
+ # (fingerprint partial store, pas une garantie crypto) —
533
+ # ``usedforsecurity=False`` neutralise le faux positif
534
+ # bandit B324 et exprime l'intention pour le reviewer.
535
  cfg["prompt_sha1"] = hashlib.sha1(
536
  str(prompt).encode("utf-8"),
537
+ usedforsecurity=False,
538
  ).hexdigest()[:12]
539
  llm = getattr(engine, "llm_adapter", None)
540
  if llm is not None:
tests/web/routers/test_s4_importers_router.py CHANGED
@@ -109,9 +109,16 @@ class TestHTRUnitedImport:
109
  assert "non trouvée" in r.json()["detail"]
110
 
111
  def test_known_entry_calls_importer(self, tmp_path: Path) -> None:
112
- """Avec un entry_id du catalogue démo, l'endpoint appelle
113
  ``import_htr_united_corpus``. On mocke pour éviter le
114
- download réel."""
 
 
 
 
 
 
 
115
  from fastapi.testclient import TestClient
116
 
117
  app = _make_app()
@@ -120,12 +127,18 @@ class TestHTRUnitedImport:
120
  ) as mock_import:
121
  mock_import.return_value = {"imported": 3, "output_dir": str(tmp_path)}
122
 
123
- # Récupère un entry_id du catalogue démo.
124
  with TestClient(app) as client:
125
  catalog = client.get("/api/htr-united/catalogue").json()
126
- if not catalog["entries"]:
127
- pytest.skip("Catalogue démo vide")
128
- entry_id = catalog["entries"][0]["id"]
 
 
 
 
 
 
 
129
 
130
  r = client.post(
131
  "/api/htr-united/import",
@@ -135,7 +148,7 @@ class TestHTRUnitedImport:
135
  "max_samples": 3,
136
  },
137
  )
138
- assert r.status_code == 200
139
  assert mock_import.called
140
 
141
 
 
109
  assert "non trouvée" in r.json()["detail"]
110
 
111
  def test_known_entry_calls_importer(self, tmp_path: Path) -> None:
112
+ """Avec un entry_id du catalogue, l'endpoint appelle
113
  ``import_htr_united_corpus``. On mocke pour éviter le
114
+ download réel.
115
+
116
+ Phase 4.4 du chantier post-rewrite : le router utilise
117
+ désormais ``from_remote()`` avec fallback démo ; en mode
118
+ remote certaines entrées peuvent avoir un ``id`` vide
119
+ (schéma YAML distant évolutif). On filtre pour récupérer
120
+ un id réellement importable, sinon on skip — un id vide
121
+ serait rejeté par Pydantic en 422 (sécurité OK)."""
122
  from fastapi.testclient import TestClient
123
 
124
  app = _make_app()
 
127
  ) as mock_import:
128
  mock_import.return_value = {"imported": 3, "output_dir": str(tmp_path)}
129
 
 
130
  with TestClient(app) as client:
131
  catalog = client.get("/api/htr-united/catalogue").json()
132
+ non_empty = [
133
+ e for e in catalog.get("entries", []) if e.get("id")
134
+ ]
135
+ if not non_empty:
136
+ pytest.skip(
137
+ "Catalogue HTR-United sans entrée avec id non-vide "
138
+ "(probable en CI réseau-restreint, ``from_remote`` "
139
+ "fallback démo limite).",
140
+ )
141
+ entry_id = non_empty[0]["id"]
142
 
143
  r = client.post(
144
  "/api/htr-united/import",
 
148
  "max_samples": 3,
149
  },
150
  )
151
+ assert r.status_code == 200, r.text
152
  assert mock_import.called
153
 
154