← Notes techniques
python · sqlite · concurrence

Ce que les tests ont trouvé avant les utilisateurs

Un système de rendez-vous pour transporteurs : deux quais, des créneaux de trente minutes, une pause déjeuner, et des gens qui cliquent en même temps.

date25 octobre 2026
projetRéservation de créneaux à quai
vérifications58 tests · 30 smoke

Le problème réel n'est pas l'interface

Une page de réservation a l'air simple : on affiche les créneaux libres, on clique, on confirme. Le vrai sujet est ailleurs — dans ce qui se passe quand deux transporteurs cliquent sur le même créneau à la même seconde, et dans ce qui se passe quand l'un annule.

Si la règle « un quai, un créneau, une réservation » vit uniquement dans le code applicatif, elle est fausse dès qu'il y a deux threads. Elle est aussi fausse dès qu'un redémarrage tombe au mauvais moment.

Décision 1 — la contrainte dans la base, pas dans le code

La garantie d'unicité est un index partiel sur (jour, créneau, quai), restreint aux réservations confirmées. Le mot important est partiel : une réservation annulée doit libérer le créneau, donc elle ne peut pas entrer dans la contrainte. Sans le filtre, annuler ne rendrait rien disponible, et le planning se remplirait de trous fantômes.

Conséquence assumée : le code applicatif peut être maladroit, la base refusera la double réservation de toute façon. Une seule source de vérité, celle qui survit aux redémarrages.

Décision 2 — les tests attaquent en parallèle

Un test séquentiel qui réserve deux fois le même créneau ne prouve rien. Le test utile lance dix threads sur trois quais et vérifie qu'il reste exactement trois confirmations — ni quatre, ni deux.

Ce test a imposé deux choses dans le code : un serveur multi-thread assumé, et un verrou autour des écritures plutôt qu'un espoir que SQLite se débrouille tout seul. Le mode d'ouverture de la connexion est explicite, sinon le premier appel concurrent lève une exception incompréhensible.

Décision 3 — les règles horaires en un seul endroit

Ouverture, créneaux de trente minutes, pause déjeuner, horizon de réservation, délai de préparation, nombre de quais : tout est dans un fichier de configuration unique. Les jours fériés ne sont pas une liste écrite à la main — ils sont calculés, Pâques comprise, parce qu'une liste de dates se périme silencieusement.

Le même module produit les créneaux affichés et valide une demande d'API. Si les deux divergeaient, un client pourrait réserver un créneau que l'écran n'a jamais montré.

Ce que ça a évité

Trois familles de bugs qui seraient sortis en production la première semaine : la double réservation sous charge, le créneau bloqué après annulation, et le décalage de fuseau dans les fichiers calendrier — un rendez-vous à 9 h qui arrive à 11 h dans l'agenda du transporteur parce que l'heure locale a été écrite au lieu de l'UTC.

Le total : 58 tests sur le cœur métier, dont la concurrence, et 30 vérifications de bout en bout sur l'API réelle. Rien de spectaculaire. C'est justement l'intérêt : un système de réservation n'a pas besoin d'être brillant, il a besoin d'avoir raison.

Ce que je ferais différemment

Écrire le test de concurrence avant la première version du stockage. Je l'ai ajouté au deuxième jour, et j'ai dû reprendre la couche d'accès aux données — pas parce qu'elle était fausse, mais parce qu'elle était écrite pour un monde à un seul utilisateur.

  • Python stdlib
  • SQLite
  • pytest
  • ThreadingHTTPServer
  • RFC 5545

La fiche du projet →