Aller au contenu

Tests des clés et véhicules

Les garages rattachés aux bâtiments ajoutent les matrices personal/group et la relocation transactionnelle de recovery décrites dans Tests immobilier et bâtiments.

Automatisation

Depuis le conteneur API :

pytest tests/test_vehicles.py tests/test_inventories.py tests/test_groups.py tests/test_bank.py
ruff check app tests
alembic upgrade head

Les tests véhicules contrôlent la paire véhicule/clé, le format de plaque, l’idempotence, le verrou, l’autorisation moteur, les protections génériques, le transfert de génération et l’atomicité du paiement serrurier.

Régression des doubles de clés

test_locksmith_duplicate_payment_and_insufficient_balance_are_atomic conserve les deux objets créés par le serrurier, puis vérifie successivement le verrouillage sans UUID (U/possession générale), chacune des deux instances exactes, l'autorisation moteur et les options du serrurier. test_duplicate_keys_remain_independent_when_transferred transfère les doubles un par un : le détenteur de la clé déplacée peut l'utiliser, l'ancien détenteur conserve ses droits tant qu'une autre clé reste dans son inventaire, puis les perd lorsque les deux ont quitté celui-ci. Le nouveau détenteur possédant alors deux clés doit continuer à verrouiller et démarrer sans exception de cardinalité.

Valider également :

npm --prefix fivem ci
npm --prefix fivem test
node --test fivem/tests/nui-transparency.test.mjs
node --check admin/public/app.js
node --check fivem/resources/[local]/medusa-admin/server.js
node --check fivem/resources/[local]/medusa-admin/web/app.js
node --check fivem/resources/[local]/medusa-vehicles/server.js
node --check fivem/resources/[local]/medusa-vehicles/web/app.js
node .agents/scripts/refresh-context.mjs --check
node .agents/scripts/validate-domain-context.mjs
git diff --check

La garde statique Lua doit aussi retourner zéro occurrence :

rg -n "\b(Number|String|Boolean)\(" fivem/resources -g "*.lua"

Recette à deux clients obligatoire

Le build et les tests statiques ne prouvent pas le comportement en jeu. Utiliser deux personnages et au moins un véhicule vanilla puis un custom.

  1. Founder ouvre F10, enregistre un véhicule vide et immobile à moins de 10 m.
  2. Vérifier la plaque DDLLLDDD, le verrouillage immédiat, le moteur coupé et la clé principale à 0 kg.
  3. Reconfigurer la touche de verrouillage dans les options, puis tester U à bord, à pied, à 15 m et au-delà. Répéter juste après enregistrement puis après une sortie/re-entrée de zone pour couvrir une réplication tardive du state bag. Couper temporairement la réponse serveur en environnement de test et vérifier l'erreur visible après trois secondes, puis le bon fonctionnement à l'essai suivant. À pied, le geste de télécommande doit suivre chaque succès serveur ; à bord, trop loin, sans réponse ou sur refus, aucun geste ne doit être joué.
  4. Utiliser la clé en cliquant dessus sans fermer l’inventaire, puis depuis un slot rapide. Vérifier le geste anim@mp_player_intmenu@key_fob@ / fob_click, 1 signal au verrouillage, 2 au déverrouillage et aucun toast de succès. Pendant que l'inventaire reste focalisé, vérifier que U est ignoré mais que le clic sur la clé part bien ; fermer ensuite l'inventaire et vérifier U.
  5. Avec le second client sans clé, tester entrée et démarrage refusés. À bord d’une voiture fermée, vérifier la sortie bloquée ; sur moto, quad et bateau, vérifier la descente autorisée.
  6. Monter conducteur avec la clé et attendre sans toucher aux commandes : le moteur reste éteint. Appuyer sur l'action Accélérer (Z sur l'AZERTY courant) : le moteur démarre sans être recoupé à l'image suivante. L'arrêter avec G, puis le redémarrer avec G. Remapper successivement Accélérer dans GTA et « Démarrer ou arrêter le moteur » dans FiveM : les deux nouvelles touches doivent fonctionner sans lettre codée en dur. Sur un profil ayant déjà chargé l'ancien défaut M, affecter explicitement l'action moteur à G dans les options FiveM et vérifier que cette préférence personnelle est conservée après reconnexion. Moteur allumé, l'arrêter avec G, puis presser ou spammer Accélérer immédiatement et à 59,9 s : le moteur reste coupé, aucune notification n'apparaît et aucun événement moteur ne part. Pendant ce délai, G doit redémarrer immédiatement. Refaire l'arrêt, attendre 60 s, relâcher puis presser Accélérer : le démarrage est de nouveau demandé. Répéter en descendant, en changeant de siège ou de véhicule : le délai local doit être nettoyé. Un second conducteur avec une clé doit pouvoir démarrer sans hériter du délai ; un restart de la ressource ne doit laisser aucun état résiduel. Régler temporairement engineAccelerationRestartDelayMs à 0, puis à une valeur négative : dans les deux cas Accélérer redevient immédiatement disponible après l'arrêt, sans erreur Lua. Donner/déposer/stocker ensuite la clé pendant que le moteur tourne : il reste allumé et G peut l'arrêter, mais Accélérer et G refusent le redémarrage sans clé. Simuler personnage absent, refus API et réponse perdue : une erreur privée ou un timeout apparaît sous trois secondes, puis le prochain essai fonctionne sans reconnecter.
  7. Tester don, drop et stockage : seul le détenteur réel peut agir. Révoquer ensuite depuis F10/web et vérifier la disparition de l’objet.
  8. Transférer personnage vers groupe puis groupe vers personnage. Vérifier génération, nouvelle clé et permissions groupe séparées.
  9. Placer un serrurier sur un trottoir plat puis sur une pente et vérifier que ses pieds touchent le sol sans flottement ni enfoncement. Le déplacer, changer son modèle, sortir/revenir dans la zone puis redémarrer la ressource : une seule instance actualisée doit rester. Avec un modèle invalide en environnement de test, vérifier la ligne [medusa-vehicles] en F8 et l'absence de faux Ped. Tester ensuite prix 0/250/1000, solde insuffisant, inventaire plein, double simultané et reprogrammation. Après un premier double, conserver les deux clés dans l'inventaire et rejouer U, le clic sur chacune, G, Accélérer, F5 et l'ouverture du serrurier. Déposer ensuite un seul double : l'autre doit continuer à fonctionner immédiatement. Reprendre la clé, transférer chaque double séparément et vérifier que seul l'inventaire qui contient physiquement au moins une clé est autorisé. F8 et les logs API ne doivent contenir ni MultipleResultsFound ni réponse 500. Avec zéro compte personnel, vérifier l'option explicite, le conseil bancaire et les boutons grisés ; avec un ou plusieurs comptes personnels actifs, vérifier NUMÉRO — $SOLDE. Les comptes joints, de groupe, fermés ou en attente ne doivent pas apparaître. Avant la première ouverture, puis après cinq cycles ouverture/fermeture, vérifier à 1280×720, 1920×1080 et en ultralarge que l’état fermé ne laisse aucun fond noir ou voile et ne capture ni clic ni clavier. Ouverte, seule la console du serrurier doit peindre une surface sombre : aucun rectangle noir ne doit entourer le panneau, jusque dans les coins de l'écran.
  10. Attendre plus de 20 secondes, modifier dégâts/ouvrants, reconnecter puis redémarrer la ressource : plaque/verrou/état convergent et une seule entité existe.
  11. Détruire le véhicule : le dossier devient indisponible et reste restaurable. Tester /delv sur un temporaire puis sur un enregistré.
  12. Répéter sur auto/utilitaire/camion, moto, quad, bateau, avion et hélicoptère ; vérifier le refus train/spécial/modèle cassé.

Pannes et sécurité

  • Couper l’API : les nouvelles actions échouent fermées, avec erreur privée, sans couper un moteur déjà actif.
  • Falsifier network ID, plaque, vehicle ID, item UUID, owner et bucket : aucune mutation ne doit se produire.
  • Presser U simultanément sur les deux clients puis spammer : une seule transition par mutex/cooldown.
  • Désactiver VEHICLES_ENABLED avec des dossiers présents : aucune nouvelle action runtime, aucune perte de données.
  • Inspecter les audits de register, plaque, transfert, restore, despawn, delete, clés et serruriers ; les locks ordinaires ne doivent pas polluer le journal.

Conserver captures, timestamps, plaques, modèles et extraits de logs comme preuve. Ne déclarer les critères E2E validés qu’après cette recette réelle.

Administration spatiale F10

  1. Créer un véhicule personnel puis un véhicule de groupe. Actualiser F10 et vérifier les libellés PROPRIÉTAIRE : Prénom Nom et GROUPE : Nom, sans UUID. Tester aussi un nom long.
  2. Avec le véhicule actif dans la même instance, cliquer sur GPS, puis SE TP à pied et dans un véhicule. Vérifier le marqueur, le décalage sûr, les collisions et les audits.
  3. Rejouer GPS/TP sur un véhicule remisé, indisponible, sans entité runtime, dans un autre bucket et avec un UUID forgé. Rejouer sans ADMIN_ALLOW_VIEW_VEHICLES, puis sans ADMIN_ALLOW_TPC.
  4. Ouvrir RESTAURER, annuler, choisir DERNIER EMPLACEMENT, puis DEVANT MOI. Vérifier une seule entité, la position persistée, les portes verrouillées et le moteur coupé.
  5. Rejouer avec un occupant, une position invalide et deux restaurations simultanées : toutes doivent échouer sans déplacer un joueur ni créer de doublon.
  6. Vérifier les huit actions et la ligne propriétaire en 1280×720, 1920×1080 et 2560×1080, à la souris puis au clavier. Aucun bouton ne doit être coupé et le focus doit rester visible.

Attendu : VEH-API-005, VEH-LIFE-005, VEH-UI-005 et VEH-ADM-005 à 007 sont satisfaits ; les coordonnées sont dérivées côté serveur et aucun libellé ne promet un garage.

Administration web des propriétaires

  1. Ouvrir la page Véhicules avec un véhicule personnel et un véhicule de groupe ; vérifier les noms RP/groupes et l'absence d'UUID visible, puis filtrer par prénom, nom et groupe.
  2. Ouvrir Transférer, rechercher avec 0, 1 puis 2 caractères, changer le type en cours de recherche et sélectionner séparément le propriétaire et le destinataire de la clé.
  3. Transférer vers un personnage, puis vers un groupe avec un autre personnage destinataire ; vérifier le propriétaire, la révocation des anciennes clés et la nouvelle clé principale.
  4. Rejouer la recherche sans ADMIN_ALLOW_EDIT_VEHICLE, avec des UUID falsifiés ou supprimés, et vérifier que le dossier et les clés restent inchangés.
  5. Tester à 1280×720, 1920×1080, 2560×1080 et sur une largeur mobile : scroll, Échap, Annuler, clavier, focus, nom long et absence de débordement.

Attendu : VEH-API-006, VEH-WEB-001 et VEH-WEB-002 sont satisfaits ; vingt suggestions au maximum sont retournées, les réponses obsolètes ne remplacent pas la recherche courante et aucun UUID n'est demandé ou affiché.

Recette plaques et garages

Automatisé : exécuter pytest tests/test_vehicle_garages.py tests/test_vehicles.py tests/test_permissions.py tests/test_system.py. Vérifier génération signée, 100 plaques ambiantes uniques, validation manuelle, rangement idempotent, réservation/finalisation, conservation du carburant, mauvais garage et sorties toutes occupées. alembic heads doit retourner uniquement 0032_vehicle_garages.

Recette FiveM obligatoire avec deux clients :

  1. démarrer puis redémarrer deux fois medusa-vehicles et vérifier dans F8/CitizenFX l'absence de Error parsing script, Failed to load script, export manquant et erreur NUI ;
  2. visiter les neuf sites et vérifier cercle bleu de 2 mètres sans borne, blip, catégorie et sorties sans collision ; renommer puis désactiver/réactiver un garage et vérifier qu'un seul blip à jour subsiste ;
  3. ranger avec E un véhicule contenant conducteur, passager joueur et PNJ ; tous sortent et une seule entité disparaît après sauvegarde ;
  4. sortir simultanément le même véhicule ; une seule réservation et une seule entité existent ;
  5. occuper toutes les sorties ; aucun spawn ni débit ne se produit ;
  6. vérifier dégâts, roues, carburant, plaque, verrouillage et moteur coupé après sortie ;
  7. redémarrer la ressource puis FXServer avec états active/garaged/impounded ; seuls les active réapparaissent une fois ;
  8. tester F10 et web avec fondateur puis permissions unitaires refusées ;
  9. dans F10, utiliser SE TP sur un garage actif puis inactif, à pied et dans un véhicule ; vérifier le refus sans ADMIN_ALLOW_VIEW_GARAGES, sans ADMIN_ALLOW_TPC, avec UUID forgé ou autre bucket, puis contrôler l'audit unique après succès ;
  10. créer 100 véhicules ambiants et contrôler format/style, absence de collision et resmon ;
  11. enregistrer un véhicule proche et vérifier l'absence de No such export describeTarget ; dans une recette contrôlée, arrêter medusa-vehicles et vérifier une erreur F10 propre sans Failed to fetch non géré ;
  12. créer puis déplacer un serrurier avec deux modèles valides et vérifier son apparition sans reconnexion sur les deux clients ;
  13. rejouer U, clé inventaire, G/Z, délai moteur 60 s, serrurier, GPS/TP et restauration.
  14. ouvrir Administration > Garages, déplacer un rond puis ajouter et déplacer une sortie ; annuler une fois avec Retour/Échap puis confirmer avec E, et vérifier sur les deux clients que seule la cible a changé et que son UUID est resté identique ;
  15. réserver une sortie, rejouer une ancienne révision et tenter de supprimer la dernière sortie active d'un garage actif : les trois actions sont refusées sans état partiel ;
  16. tester enregistrement, transfert et réinitialisation de clés avec joueurs connectés, personnage hors ligne, homonyme et groupe : les connectés portent Prénom Nom (ID serveur), aucun UUID n'est visible et une valeur falsifiée est refusée côté serveur ;
  17. provoquer plaque invalide/dupliquée, permission refusée, erreur garage et indisponibilité API : une bannière française typée apparaît dans F10, tout en conservant les traces F8/F9 ;
  18. vérifier qu'aucune carte véhicule ne contient SCANNER et que l'unique action supérieure Identifier le véhicule proche distingue dossier enregistré, plaque ambiante et absence de cible.

Résultat attendu : aucune donnée technique dans la NUI, aucun fond noir, aucune double transition, et des erreurs françaises explicites. La validation des coordonnées en jeu ne peut pas être remplacée par les tests statiques locaux.

Recette MED-20260827-001

Révision corrective des libellés et du backdrop

  1. Sans aucune affectation, ouvrir F10 puis le web : chaque carte ORIGINAL doit afficher REPLI AUTOMATIQUE et 0 affectation(s) explicite(s). Confirmer avec un véhicule sans règle qu'il résout toujours ORIGINAL.
  2. Affecter SPORT à un véhicule personnage puis à un véhicule groupe. Vérifier modèle, plaque, PROPRIÉTAIRE : Prénom Nom ou GROUPE : Nom dans F10 et le web. Rechercher successivement par modèle, plaque et nom ; rechercher un fragment de l'UUID ne doit rien retourner.
  3. Ouvrir la confirmation ORIGINAL : elle reprend le libellé métier, mais la requête conserve scope, cible interne et révision. Une fixture orpheline contrôlée affiche VÉHICULE INDISPONIBLE, jamais sa clé technique.
  4. Avant la connexion au serveur, puis après cinq cycles serrurier, garage, Alt et F5, contrôler 1280×720, 1920×1080 et ultrawide. Les quatre coins et le centre hors panneau doivent montrer le monde, et Échap/fermeture/stop de ressource doivent rendre focus et curseur au jeu.
  5. Exécuter node --test tests/nui-transparency.test.mjs depuis fivem/. Le test doit refuser une fixture avec color-scheme:dark ou une racine opaque, accepter le loading screen et passer sur toutes les ui_page courantes.

Révision corrective F5, sièges et Alt

Cette recette runtime est obligatoire pour fermer REV-F5-001 à REV-TARGET-002 ; les tests de contrat Node ne remplacent pas les natives FiveM.

  1. Dans un véhicule compatible, ouvrir F5 puis déplacer rapidement la souris sur les deux axes et utiliser la molette. La vue, la caméra cinématique et la station radio ne doivent jamais changer. Sans fermer F5, vérifier direction, accélération, freinage, frein à main et sortie au clavier puis à la manette. Fermer successivement par F5, Échap, bouton, sortie, mort, despawn, autre NUI et restart de medusa-vehicles ; caméra, radio, curseur et commandes doivent être immédiatement rendus, sans double fermeture. Cela couvre REV-F5-001 et REV-F5-002.
  2. Avec deux clients, sélectionner successivement chaque place libre d'une 2 portes, 4 portes puis d'une grande cabine. Attendre dix secondes sur chaque siège avec le conducteur libre : le joueur doit rester exactement sur l'index choisi. Faire occuper la place au dernier moment, lancer deux demandes concurrentes, sortir, mourir, despawner puis redémarrer la ressource pendant la transition. Aucun occupant ne doit être éjecté, aucun échec ne doit déplacer au conducteur et la tentative suivante doit fonctionner. Cela couvre REV-SEAT-001 et REV-SEAT-002.
  3. À pied, maintenir Alt gauche près d'un véhicule puis déplacer le curseur : caméra et roue native doivent rester fixes. Cliquer à gauche le véhicule sous le pointeur à moins de 3 m ; le panneau extérieur doit s'ouvrir. Répéter avec plusieurs véhicules proches, un obstacle, hors portée, relâchement Alt, Échap, clic droit, mort et disparition de la cible. Redémarrer ensuite medusa-interactions sans redémarrer medusa-vehicles et recommencer : le fournisseur doit se réinscrire. Une inscription volontairement refusée doit produire un message visible, pas seulement une ligne F8. Cela couvre REV-TARGET-001 et REV-TARGET-002.
  4. Remapper F5 et l'interaction ciblée dans les options FiveM, puis rejouer les trois parcours. Les actions U, G, TAB, E garages et E serrurier doivent rester inchangées.

Révision Alt extérieur définitive et lisibilité handling

Les contrôles Node/Playwright valident le contrat et la présentation, mais le contour et les natives de focus doivent encore être constatés dans FiveM avant de fermer les critères runtime.

  1. Ouvrir F10 puis Medusa Admin web aux largeurs 360, 768, 1280 et 1920 px. Vérifier les titres Profils disponibles, Profils appliqués et Base ORIGINAL des véhicules. Une affectation SPORT doit porter PROFIL APPLIQUÉ. Chaque source montre modèle, famille, provenance et état français sans chemin ni empreinte dans le résumé.
  2. Ouvrir Détails techniques au clavier puis à la souris. Vérifier que nom de handling, ressource, chemin, empreinte complète, date, révision et diagnostic sont lisibles, sélectionnables et copiables. Rechercher successivement GTA / FiveM, Détectée en jeu, un chemin, une empreinte et un diagnostic ; chaque recherche doit retrouver la même carte. Une source absente, invalide ou ambiguë doit rester signalée sans ouvrir le détail.
  3. Dans FiveM, à pied et à moins de 3 m, maintenir Alt puis viser avec le curseur un véhicule enregistré vide, le même véhicule occupé, puis un véhicule PNJ. Le contour turquoise doit suivre exactement le véhicule visé à 16:9 et en ultrawide. Répéter immédiatement après son streaming ou un redémarrage de medusa-vehicles : une réplication tardive peut retarder brièvement le contour, jamais le désactiver durablement.
  4. Cliquer une fois puis double-cliquer rapidement : un seul panneau extérieur doit s'ouvrir pour le même véhicule. Le panneau garde son focus après relâchement Alt ; Échap ou sa fermeture le rend ensuite au jeu. Rejouer après redémarrage de medusa-interactions sans redémarrer medusa-vehicles.
  5. Tester successivement hors véhicule, hors portée, cible disparue entre contour et clic, flag VEHICLE_CONTROL_MENUS_ENABLED=false, train et véhicule spécial. Aucun panneau ne doit s'ouvrir. Après un clic refusé, un message français avec un code ALT-… doit apparaître à l'écran. Simuler enfin une panne du callback CEF : elle doit produire le même type de retour visible.
  6. Avec le debug actif, observer F8 pendant tous les parcours : seules les transitions mode, raycast, candidat, sélection et focus sont journalisées. Une visée immobile ne doit jamais produire une ligne par frame.

Les tests automatisés associés sont interactions-target-ui.test.mjs, les contrats cible de vehicle-regression.test.mjs et admin/tests/vehicles.spec.js. La preuve finale de REV-TARGET-003 à REV-TARGET-005 exige néanmoins une capture ou un log de cette recette FiveM.

Références Cfx du fournisseur Alt

Le test target-provider-callbacks.test.mjs reproduit la forme reçue à la frontière Cfx : une table portant __cfx_functionReference et un appel métatable. Il vérifie les fonctions locales, les doubles Cfx, le refus des tables ordinaires/callbacks absents, les exceptions protégées, l'appel unique de resolve et action, puis unregister/réinscription sans doublon. Il interdit aussi le retour de type(options.resolve) ~= 'function' et de la condition stricte historique sur provider.action. Cela couvre la preuve locale T-TARGET-CFXREF-001.

La preuve runtime T-TARGET-PROVIDER-BOOT-001 reste obligatoire :

  1. Activer temporairement le debug de medusa-interactions, démarrer les ressources et vérifier dans F8 resource_loaded | version=1.7.4, puis target_provider_registered | id=medusa-vehicles.
  2. Maintenir Alt près d'un véhicule enregistré : target_mode doit annoncer providers=1, le raycast terminer avec type=2, le contour turquoise apparaître et un clic ouvrir exactement un panneau.
  3. Répéter sur un véhicule PNJ, avec les mêmes résultats et sans inscription refusée.
  4. Redémarrer uniquement medusa-interactions, attendre la réinscription automatique et rejouer les deux parcours. Le registre doit toujours annoncer un seul fournisseur et une seule action par clic, sans focus, curseur ou contour résiduel après fermeture.
  5. Remettre le debug à sa valeur d'exploitation après avoir conservé le log ou la capture de preuve.

Automatisation obligatoire

Set-Location api
.\.venv\Scripts\python.exe -m pytest tests/test_vehicle_handling.py tests/test_vehicle_compartments.py tests/test_vehicles.py tests/test_vehicle_garages.py tests/test_inventories.py tests/test_inventory_drops.py tests/test_inventory_storages.py tests/test_permissions.py tests/test_system.py -q -p no:cacheprovider
Set-Location ..\fivem
npm test
Set-Location ..\admin
npm run test:e2e
Set-Location ..
node .agents/scripts/refresh-context.mjs --check
node .agents/scripts/validate-domain-context.mjs
git diff --check

Sur une stack PostgreSQL de test, ajouter obligatoirement :

docker compose exec api alembic upgrade head
docker compose exec api alembic check
docker compose exec api ruff check app tests
docker compose exec api pytest

Matrice des 77 critères

Critères Preuve automatisée minimale Preuve runtime obligatoire
AC-001–AC-005 extracteur/fixtures, priorité, profils, versions, preview comparaison vanilla/realistic/custom avant-après, annulation/restart
AC-006–AC-013 transferts, capacités, sessions, PNJ, conversion et rollback TAB extérieur/intérieur à deux clients sur classes représentatives
AC-014–AC-024 contrats Alt/F5, actions, rate limits, réservations/replay raycast, ouvrants, chaque siège, moteur G/F5/Accélérer et concurrence
AC-025–AC-033 ORIGINAL sans dérive, sources, empreintes, diagnostics/export application successive puis ORIGINAL sur terre/mer/air
AC-034–AC-052 lifecycle, suppression, explosion, notifications, warning clé garage/fourrière/despawn/explosion réelle et sessions partagées
AC-053–AC-062 permissions, bornes, absence handling NUI, Playwright responsive rôles unitaires, AZERTY remappé, souris et manette
AC-063–AC-077 migration paresseuse, flags x8, idempotence, redaction restart, pannes API/DB/Redis, 48 joueurs/100 véhicules et p95

Chaque AC reçoit dans le plan une preuve nommée, une capture/log/hash runtime ou reste non coché. Les tests statiques de contrats ne prouvent ni les natives FiveM, ni la latence, ni un essai à deux clients.

Parcours runtime à deux clients

  1. Sauvegarder PostgreSQL, appliquer 0033 et confirmer le head/readiness.
  2. Tester sans affectation un vanilla, un realistic-handling, chacun des six customs et un modèle nouvellement ajouté ; comparer les valeurs avant/après puis revenir à ORIGINAL.
  3. Affecter successivement catégorie, modèle et UUID ; changer plaque/propriétaire, ranger, sortir, restaurer et redémarrer. La priorité et l'UUID doivent rester identiques.
  4. Ouvrir coffre par TAB à pied, boîte à gants par TAB sur les deux sièges avant et vérifier le fallback personnel aux sièges arrière. Tester verrou, 2/5 km/h, 4/4,25 m et hayon arraché. Refaire avant/après rangement-sortie de garage, après restauration et après chaque ordre de restart Inventaire/Véhicules. Avec les flags actifs mais les capacités retirées en recette, plusieurs appuis rapprochés doivent produire une seule réparation bornée, un message visible et jamais l'ouverture silencieuse du personnel sur la cible réelle.
  5. Mettre du contenu dans un PNJ, enregistrer l'entité puis répéter avec une capacité trop petite ; le premier contenu migre, le second reste intégralement temporaire.
  6. À deux clients, muter le même slot et viser le même siège. Un seul gagnant est accepté, sans duplication, éjection ni réservation orpheline.
  7. Tester Alt+clic et F5 sur 2 portes, 4 portes, moto, bateau, hélicoptère, avion et grande cabine ; vérifier portes/vitres/capot/coffre/feux, dégâts et permissions par siège.
  8. Arrêter le moteur par G puis F5, essayer Accélérer avant/après 60 s et redémarrer immédiatement par G/F5. Changer de siège pendant chaque état.
  9. Provoquer panne, feu, moteur à -4000, streaming, despawn et enfin explosion confirmée. Seule l'explosion commitée purge exactement une fois.
  10. Tester les huit combinaisons de flags, puis interrompre API, DB et Redis séparément. Aucun faux succès, replay destructif ou surface fantôme ne doit apparaître.
  11. Mesurer avec 48 joueurs/100 véhicules : API p95 sous 500 ms, pages de 50, aucun scan/raycast/API par frame et aucun état résiduel après fermeture/stop.

Recette MED-20260830-002 — retrait du régulateur et placement du compteur

Vérifications automatisées

Depuis api/ :

..\.venv\Scripts\python.exe -m pytest tests/test_speedometer_placement_migration.py tests/test_vehicle_preferences.py tests/test_vehicles.py tests/test_system.py tests/test_vehicle_handling.py tests/test_vehicle_compartments.py tests/test_vehicle_garages.py tests/test_inventories.py tests/test_inventory_drops.py tests/test_inventory_storages.py tests/test_permissions.py -q -p no:cacheprovider
..\.venv\Scripts\python.exe -m alembic heads
..\.venv\Scripts\python.exe -m alembic upgrade 0035_speedometer_cruise_v2:0036_speedometer_placement --sql

Depuis fivem/ :

npm test
node --test tests/vehicle-speedometer.test.mjs tests/vehicle-regression.test.mjs tests/nui-transparency.test.mjs

La suite vérifie notamment contrat v3, valeurs finies, pas de 5, UUID/idempotence/révision, 48 comptes en smoke SQLite, les 16 combinaisons de flags API, la syntaxe Lua, l'absence totale du runtime/mapping/callback/flag de régulateur, le masque d'entrées du placement et le garde-fou global de transparence. Elle impose aussi l'identité byte-à-byte des trois YTD, des trois Lua et de la licence, les hashes des six exports F5, les trois thèmes stricts, le renderer natif sans MPH ni runtime/commande/KVP/son amont et l'absence de SVG de substitution. Le SQL hors ligne et le smoke SQLite ne prouvent ni l'upgrade PostgreSQL réel ni son p95 de 500 ms.

Recette FiveM obligatoire

  1. Sur une copie PostgreSQL au head 0035 contenant small, normal et large, appliquer 0036 et vérifier 80/100/120, (0.5,0.8), head unique, contraintes, ownership, timestamps et second upgrade head sans mutation.
  2. Avec deux comptes et deux personnages d'un même compte, appliquer CLASSIC, D6 STREET et D7 RACING à 60/100/140 %, plusieurs positions et les modes Automatique/Jour/Nuit. Reconnecter, changer de personnage et redémarrer API/HUD/véhicules : les cinq dimensions restent account-wide et l'autre compte est isolé.
  3. Ouvrir le placement à 0, 1 et 1,1 km/h, conducteur/passager, mort, pause et entité absente. Seul le conducteur à 1 km/h ou moins obtient l'éditeur ; chaque refus explique la cause sans focus.
  4. Tester drag, molette, slider, boutons ±5 %, flèches 0,25 %, Maj 1 %, Ctrl anti-snap, Recentrer et reset complet confirmé/non confirmé. Refaire à la manette avec stick, croix, épaules, modificateur et appliquer/annuler, puis débrancher/rebrancher la manette.
  5. Pour chaque thème à 60/100/140 %, placer aux quatre bords en 1280×720, 1920×1080, 16:10, 21:9 et 32:9 avec safe zone minimale/maximale. Changer résolution à chaud : le rendu se borne sans PATCH, puis retrouve la position désirée au retour.
  6. Annuler/fermer puis invalider par accélération, siège, sortie, mort, pause, flag off et restart HUD/véhicules. Aucun PATCH ne part, l'aperçu masqué disparaît et aucun focus/contrôle ne reste bloqué. Provoquer 400/409/503/timeout/réseau et réponses réordonnées : le brouillon et la cause restent récupérables, sans faux succès.
  7. Vérifier que K, Page précédente et Page suivante n'ont plus de mapping ni d'effet. Parcourir les quatre combinaisons compteur/F5 et tester séparément U, G, délai Accélérer 60 s, Alt+clic, TAB/coffre/boîte à gants, ouvrants, vitres, sièges, handling, garages et serrurier.

Mesure de performance

Sur le serveur de recette, simuler 48 sessions GET/PATCH sur PostgreSQL et conserver p50/p95, erreurs, doublons et conflits ; attendu p95 <500 ms hors panne et une seule ligne/requête. Mesurer medusa-vehicles pendant 60 s éditeur fermé : delta moyen ≤0,10 ms. Pendant un drag à 30/60/144 FPS, vérifier zéro API/événement réseau/log par mouvement, maximum 20 snapshots/s et aucune croissance de file ou mémoire.

Régression avec énergie

La matrice U/G, clés, serrurier, Alt, TAB, sièges, handling, garages et speedmeters doit être rejouée avec les seuils énergie. Voir Tests du système énergie.

Course des compartiments MED-20260904-001

Exécuter node --test tests/vehicle-compartment-runtime.test.mjs tests/vehicle-regression.test.mjs. En jeu, ouvrir puis fermer vingt fois rapidement un coffre, puis une boîte à gants. Fermer pendant le tick de surveillance, puis enchaîner immédiatement coffre A vers boîte à gants ou coffre B. Le nouveau contexte doit rester ouvert et F8 ne doit jamais contenir activeCompartment nil. Terminer par un cycle normal et provoquer successivement distance, vitesse, verrouillage et départ d’un siège avant : chaque sécurité doit encore fermer uniquement le compartiment courant, sans spam.