Tests et CI : la discipline qui tient le systĂšme
La philosophie : intégration > mocks
La rÚgle du projet : tester contre de vraies dépendances plutÎt que des mocks. Les tests Python tournent sur un PostgreSQL réel jetable (testcontainers) et un fakeredis :
# le test d'isolation RLS le plus important du repo (en substance) :
async def test_rls_isolation(pg_container):
async with session_with_tenant(factory, tenant_a):
await create_variant(name="secret-A")
async with session_with_tenant(factory, tenant_b):
rows = await list_variants()
assert rows == [] # le tenant B ne voit RIEN de A
Pourquoi c'est non nĂ©gociable ici : la RLS ne se mocke pas. Un mock de session validerait du code qui fuit en prod ; seul un vrai Postgres avec les vraies policies prouve l'isolation. MĂȘme logique pour la recherche hybride (tsvector, pg_trgm, HNSW : comportements rĂ©els uniquement). Config pytest stricte : asyncio_mode=auto, et filterwarnings=error â un warning EST un Ă©chec.
La pipeline GitLab : chaque invariant a son job
lint:python (ruff) typecheck (mypy --strict/pkg) arch:imports (import-linter)
arch:deps (deptry) test:python (pytest+testcontainers, par package)
lint:web / test:web test:e2e (Playwright) secrets (gitleaks)
lint:sql (squawk) deadcode (vulture) sonarcloud
Ă retenir : l'architecture a ses propres jobs (arch:*) au mĂȘme rang que les tests. Et les hooks locaux vĂ©rifient les invariants mĂ©tier avant mĂȘme le commit : pas de WHERE tenant_id manuel, pas de schĂ©ma en dur, pas de commit sur main.
Deux renforts récents complÚtent le dispositif :
- le Quality Gate SonarCloud est bloquant sur les MRs (couverture et code smells du new code â on ne juge que ce qu'on ajoute, la dette historique se rĂ©sorbe sĂ©parĂ©ment) ;
- des suites E2E Playwright (Quotes, Mappings IA â ~35 scĂ©narios chacune) tournent contre l'environnement de dev dĂ©ployĂ© et publient leur rapport dans un canal Slack dĂ©diĂ©, avec le commit ET la version rĂ©ellement servie par l'env (pour dĂ©tecter les « dĂ©ploiements dĂ©calĂ©s »). Un Ă©chec E2E ping le canal : la recette est continue, pas Ă©vĂ©nementielle.
Le déploiement hybride
L'API part sur AWS ECR (release par tag semver, manuelle), le front Nuxt sur GCP Cloud Run â deux clouds, deux cadences, reliĂ©s par un repo de changelogs de dĂ©ploiement (ArgoCD cĂŽtĂ© prod). La release est un bouton conscient, pas un push automatique : sur un produit B2B, on choisit quand embarquer quoi.
Récap final du cours : le chemin complet
Tu peux maintenant suivre une requĂȘte de bout en bout : JWT Logto â tenant_id â session RLS (SET LOCAL) â service â LangGraph astream â SDK Anthropic streaming (caching, tools ReAct, model_view/ui_events) â recherche hybride RRF â SSE AG-UI â UI gĂ©nĂ©rative Nuxt â le tout tracĂ© dans Langfuse, gardĂ© par mypy/ruff/import-linter, testĂ© sur vrais conteneurs. C'Ă©tait la carte ; le territoire t'attend dans le repo. đ
Pourquoi la RLS DOIT ĂȘtre testĂ©e sur un vrai Postgres ?