Le dépôt n'avait aucun test, aucun linter, aucune vérification de types. Outillage - ESLint 9 (flat config) sur api + les deux frontends, Ruff sur le Python - tsc --checkJs sur l'API (pas de TypeScript, juste la vérification) - Vitest : 401 tests JS ; pytest : 43 tests Python - Tests de mutation (Stryker), deux profils : logique pure et API complète - Hook pre-push `npm run check` (~17 s) — le déploiement Coolify est sur webhook, c'est donc la seule porte de qualité avant la mise en ligne - Workflow Forgejo Actions prêt (inerte tant qu'aucun runner n'est enregistré) Sécurité - Injection SQL authentifiée dans resolve-conflict : `field` était interpolé dans le SET sans allowlist - timingSafeEqual levait sur un jeton multi-octets (500 au lieu de 401) - setErrorHandler écrasait tous les 4xx en 500 - .env.example : ADMIN_JWT_SECRET et ADMIN_SEED_* n'étaient documentés nulle part alors que leur absence casse toute connexion admin Annulation réelle des crawl_run (migration 014) - L'API posait status='error' sans que le crawler en sache rien : le process continuait, et son UPDATE final ne matchait plus (run réussi affiché en erreur) - Protocole coopératif : drapeau cancel_requested lu à chaque lot, le crawler écrit lui-même status='cancelled' Cohérence géographique (migration 014) - Le trigger 013 supprimait les band_locations sans purger le point dénormalisé - L'édition admin de lat/lon n'atteignait jamais band_locations : la carte ignorait la correction. Override step_order = -1, dans une transaction Corrections - limit/offset NaN → 500 au lieu de 400 - OPTIONS sans `return reply` (Fastify poursuivait le cycle de vie) - listen() sans catch, cast ::text en dur sur les colonnes numériques - /admin/api/logs ne renvoyait pas sa pagination - a11y : sélecteur de langue annoncé comme liste vide (role=option manquant) Nettoyage - apps/web/quizz-site supprimé (sans rapport avec le projet) - Code mort : openModal(), LANG_NAMES, double import, variables inutilisées - .dockerignore ajoutés ; node_modules racine n'était pas gitignoré Co-Authored-By: Claude <noreply@anthropic.com>
45 lines
1.8 KiB
JavaScript
45 lines
1.8 KiB
JavaScript
import { defineConfig } from "vitest/config";
|
|
|
|
/**
|
|
* Environnement `node` par défaut (démarrage quasi instantané). Les rares
|
|
* fichiers qui ont besoin du DOM déclarent leur propre environnement via un
|
|
* docblock en tête de fichier :
|
|
*
|
|
* // @vitest-environment jsdom
|
|
*
|
|
* Aucun test ne démarre de conteneur ni de vraie base : les routes API sont
|
|
* testées via `fastify.inject()` avec un faux pool (test/helpers/fakePool.js),
|
|
* et les tests a11y parsent le HTML statique avec jsdom + axe-core.
|
|
*/
|
|
export default defineConfig({
|
|
test: {
|
|
environment: "node",
|
|
// Pool laissé au défaut (forks) : mesuré contre les alternatives sur cette
|
|
// suite, 401 tests —
|
|
// forks (défaut) : 3,5 s
|
|
// threads, isolate: true : lent
|
|
// threads, isolate: false : 18,8 s (mise en place de jsdom pénalisée)
|
|
// L'intuition « threads sans isolation = plus rapide » est fausse ici.
|
|
// Re-mesurer avant de changer.
|
|
include: ["apps/*/test/**/*.test.js"],
|
|
reporters: ["default"],
|
|
coverage: {
|
|
provider: "v8",
|
|
reporter: ["text", "lcov"],
|
|
include: ["apps/api/src/**/*.js"],
|
|
// migrate.js s'exécute au démarrage du conteneur et appelle process.exit :
|
|
// le couvrir demanderait une vraie base, ce qu'on s'interdit ici.
|
|
// server.js n'est qu'un point d'entrée (5 lignes), la logique est dans app.js.
|
|
exclude: ["apps/api/src/migrate.js", "apps/api/src/server.js"],
|
|
// Seuils = cliquet anti-régression. Ils ne prouvent PAS que les tests
|
|
// vérifient quelque chose (seule la mutation le fait, cf. `npm run
|
|
// test:mutation`), mais ils empêchent d'ajouter du code non exécuté.
|
|
thresholds: {
|
|
statements: 85,
|
|
branches: 80,
|
|
functions: 85,
|
|
lines: 85,
|
|
},
|
|
},
|
|
},
|
|
});
|