Nodefony 10.0.0-beta.5
Framework Node.js fullstack — HTTP & WebSocket, même contexte

Parité des adaptateurs — ce qui est prouvé identique, ce qui diffère, ce qui n'est pas prouvé

stablemis à jour 2026-09-24

Changer NF_DATABASE_URL de postgres:// à mongodb:// ne doit rien changer à ce que voit l’application. Cette page dit ce qui le prouve, les écarts assumés entre moteurs, et — surtout — ce qu’aucun banc ne prouve encore. Un développeur qui choisit son moteur doit pouvoir lire ici ce qu’il perd, avant de le découvrir en production.

📍 Documentation › Guides › Parité des adaptateurs

Le modèle — un contrat, un banc, N moteurs#

Chaque contrat de stockage a UN banc de tests, qui vit chez le propriétaire du contrat — et chaque adaptateur le branche. Comme un examen commun : la même copie est posée à tous les candidats, et c’est l’identité de la copie qui rend les notes comparables. Deux bancs recopiés divergeraient en silence, chacun passant ses propres tests.

Un adaptateur ne fournit au banc qu’un harnais : comment ouvrir la base, la vider, fabriquer le store, et quelles capacités il porte. Il n’écrit aucune assertion. Un écart de comportement entre deux moteurs devient donc un test rouge, par construction.

Contrat Banc (propriétaire) Mémoire Drizzle × sqlite / pg / mysql MongoDB
IRepository + IOrm runRepositoryContract() (orm-core/tests/support/repositoryContract.ts#runRepositoryContract) — ✅ ✅
ITokenStore runTokenStoreContract() (security/tests/support/tokenStoreContract.ts#runTokenStoreContract) ✅ ✅ ✅ (+ Redis)
IWebAuthnCredentialStore runWebAuthnStoreContract() (security/tests/support/webAuthnStoreContract.ts#runWebAuthnStoreContract) ✅ ✅ ✅
IWebhookStore runWebhookStoreContract() (security/tests/support/webhookStoreContract.ts#runWebhookStoreContract) ✅ ✅ ✅
ITotpSecretStore runTotpStoreContract() (security/tests/support/totpStoreContract.ts#runTotpStoreContract) ✅ ✅ ✅
Journal d’audit sous rafale runAuditBurstContract() (security/tests/support/auditBurstContract.ts#runAuditBurstContract) — ✅ ✅
Manifeste des stores runStoreManifestContract() (security/tests/support/storeManifestContract.ts#runStoreManifestContract) — ✅ ✅

Au-dessus de ces bancs unitaires, l’application entière démarre sur MongoDB et rejoue la passe d’intégration HTTP (npm run test:all -- --mongo, garde MONGO_BOOT_GATE dans scripts/test/vitest/gates.ts) : c’est ce qui prouve que les briques se câblent au boot, pas seulement qu’elles répondent une fois montées à la main.

Ce que ces bancs ont déjà trouvé#

Ils ne sont pas décoratifs : leur premier rejeu croisé a trouvé des écarts que chaque banc isolé laissait passer.

Écarts ASSUMÉS entre moteurs#

Ce sont des différences réelles, connues, qu’on a choisi de ne pas masquer. Le banc ne les exige donc pas — et les nomme.

Écart SQL MongoDB Ce que ça change pour vous
Savepoints réels savepoint()/rollbackTo() lèvent SavepointNotSupportedError (MongooseTransaction.ts#MongooseTransaction.rollbackTo) Pas de rollback PARTIEL sous MongoDB : le refus fait échouer le callback, et la transaction ENTIÈRE est annulée. Le banc éprouve ce refus (savepoints: false).
Expiration des jetons gc() balaie et compte gc() balaie et compte ; Redis : TTL natif, gc() rend toujours 0, à la seconde près Le banc vérifie que l’expiré a DISPARU plutôt qu’un compte (nativeTtl), et saute la borne à la milliseconde sous Redis.
Casse de $like suit la collation (sqlite/mysql insensibles, pg sensible) expression régulière, sensible Ne pas compter sur l’insensibilité à la casse : le banc n’utilise que des motifs à casse exacte.
Ordre de deux écritures concurrentes sqlite : le premier lancé gagne ; pg/mysql : ordre d’arrivée libre ordre d’arrivée libre Le banc exige ce qui vaut partout — aucun rejet, une seule révocation effective, aucune réécriture ultérieure — jamais QUI gagne.
Deux jetons au même secretHash sqlite/pg rejettent ; mysql écrase la ligne en conflit rejette Inatteignable en pratique (le hash vient d’un secret aléatoire). L’invariant portable — jamais deux jetons pour un secret — est exigé ; la manière, non (DrizzleTokenStore.ts#DrizzleTokenStore.put).
Visibilité avant commit hors transaction sqlite : connexion unique, tout lit dans la transaction ; pg/mysql : invisible invisible Seule règle portable : withTransaction(tx) est le SEUL moyen d’entrer dans une transaction.

Ce que les bancs ne prouvent PAS#

Rejouer le banc#

# Tout, avec le rapport de ce qui n'a PAS été lancé
npm run test:all                  # Drizzle × sqlite/pg/mariadb + mémoire
npm run test:all -- --mongo       # + démarrage de l'application sur MongoDB
npm run test:all -- --dialects    # + MySQL Community

# Un adaptateur seul (les variables viennent de scripts/test/vitest/gates.ts)
cd src/packages/@nodefony/mongoose && npx vitest run tests/integration/repository-contract.test.ts

📖 Lexique#

Terme Sens ici
Contrat L’interface qu’un store promet de respecter (ITokenStore, IRepository…), comportement compris.
Banc de contrat La suite de tests UNIQUE qui décrit ce comportement, rangée chez le propriétaire du contrat.
Harnais Ce qu’un adaptateur fournit au banc : ouvrir, vider, fabriquer le store, déclarer ses capacités. Jamais d’assertion.
Capacité Ce qu’un moteur porte ou non (les savepoints) — déclarée par le harnais, pour que le banc saute le cas en le NOMMANT.
Parité Deux adaptateurs passent la même copie. Ce n’est pas l’identité d’implémentation.

⚠️ Pièges#

🧪 Tests & couverture#

Les chiffres exacts vivent dans la carte de l’aperçu, régénérée depuis vitest — jamais figés ici.

Type Où Ce qui est prouvé
Unitaires (mémoire) @nodefony/security unit/tokenStoreContract.test.ts, unit/webAuthnCredentialStoreContract.test.ts, unit/webhookStore.test.ts, unit/totpStoreContract.test.ts les stores mémoire passent la même copie que les bases
Intégration (sqlite) @nodefony/drizzle repository-contract-sqlite.test.ts, token-store-sqlite.test.ts, webauthn-store-sqlite.test.ts, webhook-store-sqlite.test.ts les contrats sur SQLite
E2E (bases réelles) @nodefony/drizzle *-postgres.e2e.test.ts, *-mysql.e2e.test.ts · @nodefony/mongoose repository-contract.test.ts, token-store.test.ts, webauthn-credential-store.test.ts, webhook-store.test.ts les mêmes contrats sur PostgreSQL, MySQL/MariaDB et MongoDB

🛑 Prudence

Les suites E2E se skippent sans leurs variables d’infrastructure, et un skip compte comme vert. Source unique des variables : scripts/test/vitest/gates.ts à la racine.

🔗 Pour aller plus loin#