Document de Conception — Task Management System
Vue d’ensemble
Le Task Management System est une API RESTful permettant aux équipes de gérer leurs tâches et projets de manière collaborative. Le système est organisé en trois releases progressives :
- Release 1 — MVP : Gestion des tâches, projets, statuts, priorités, rôles et échéances
- Release 2 — Collaboration : Attribution des tâches, gestion des équipes
- Release 3 — Pilotage : Tableau de bord, historique des modifications, notifications
Le système expose une API RESTful JSON avec contrôle d’accès basé
sur les rôles (admin, manager,
developer, viewer).
Architecture
Vue d’ensemble architecturale
graph TD
Client["Client HTTP\n(navigateur, outil tiers)"]
Keycloak["Keycloak IdP\n(Release 2+)"]
API["Couche API RESTful\nExpress / Fastify"]
Auth["Middleware Auth\n(JWT + RBAC)"]
Validation["Middleware Validation\n(Zod / Joi)"]
Services["Couche Services\n(logique métier)"]
Repo["Couche Repository\n(accès données)"]
DB["Base de données\n(PostgreSQL)"]
Notif["Service Notifications\n(Release 3)"]
Client --> API
Client -->|"Authorization Code"| Keycloak
Keycloak -->|"JWT (RS256)"| Client
API --> Auth
Auth -->|"JWKS validation (Release 2+)"| Keycloak
Auth --> Validation
Validation --> Services
Services --> Repo
Repo --> DB
Services --> Notif
Flux d’authentification (Release 1 — JWT statique)
sequenceDiagram
participant C as Client
participant API as Backend API
participant DB as PostgreSQL
C->>API: Requête avec Bearer token (JWT HS256)
API-->>API: Décoder JWT (HS256 + JWT_SECRET)
API-->>API: Extraire { id, roles }
API-->>C: Réponse (200 / 401 si invalide)
Flux d’authentification (Release 2+ — Keycloak OIDC)
sequenceDiagram
participant U as Utilisateur
participant F as Frontend SPA
participant K as Keycloak
participant API as Backend API
participant DB as PostgreSQL
U->>F: Accès à l'application
F->>F: Pas de token → redirection
F->>K: GET /realms/task-management/protocol/openid-connect/auth
K-->>U: Page de connexion Keycloak
U->>K: Saisie email + mot de passe
K-->>F: Redirect avec authorization_code
F->>K: POST /token (code + PKCE verifier)
K-->>F: access_token (JWT RS256) + refresh_token
F->>API: POST /api/auth/login { keycloak_token }
API->>K: GET /realms/.../certs (JWKS)
K-->>API: Clés publiques RS256
API-->>API: Valider signature + claims (iss, exp, roles)
API->>DB: Upsert user (id, email, name, roles)
API-->>F: 200 { user, session_token }
F->>API: GET /api/tasks (Bearer session_token)
API-->>F: 200 { data, pagination }
Flux de création d’une tâche
sequenceDiagram
participant C as Client
participant Auth as Middleware Auth
participant RBAC as Middleware RBAC
participant Val as Validator
participant Svc as TaskService
participant Repo as TaskRepository
participant DB as PostgreSQL
C->>Auth: POST /api/tasks { description, priority, dueDate }
Auth-->>Auth: Vérifier JWT → req.user
Auth->>RBAC: requireRole('developer', 'manager', 'admin')
RBAC-->>RBAC: Vérifier rôle autorisé
RBAC->>Val: validateCreateTask(body)
Val-->>Val: Zod parse (description 1-250, priority enum)
Val->>Svc: createTask({ description, priority, dueDate, createdBy })
Svc-->>Svc: Priorité par défaut = 'medium'
Svc-->>Svc: Warning si dueDate passée
Svc->>Repo: create(task)
Repo->>DB: INSERT INTO tasks ...
DB-->>Repo: Task créée
Repo-->>Svc: Task
Svc-->>C: 201 { data: Task, warnings? }
Flux de mise à jour du statut d’une tâche
sequenceDiagram
participant C as Client
participant RBAC as Middleware RBAC
participant Svc as TaskService
participant Repo as TaskRepository
participant DB as PostgreSQL
C->>RBAC: PATCH /api/tasks/:id { status: 'completed' }
RBAC->>DB: SELECT task WHERE id = :id
DB-->>RBAC: Task (createdBy)
RBAC-->>RBAC: Vérifier owner ou manager/admin
RBAC->>Svc: updateTask(id, { status, updatedBy })
Svc-->>Svc: Si status = 'completed' → completedAt = now()
Svc-->>Svc: Recalculer isOverdue (GMT+1)
Svc->>Repo: update(id, data)
Repo->>DB: UPDATE tasks SET status, completedAt, updatedAt ...
DB-->>Repo: Task mise à jour
Repo-->>Svc: Task
Svc-->>C: 200 { data: Task }
Flux de pagination
sequenceDiagram
participant C as Client
participant Repo as TaskRepository
participant DB as PostgreSQL
C->>Repo: GET /api/tasks?page=2&limit=10&status=todo
Repo->>DB: SELECT ... WHERE status='todo' LIMIT 10 OFFSET 10
Repo->>DB: SELECT COUNT(*) WHERE status='todo'
DB-->>Repo: [tasks], total = 35
Repo-->>C: 200 { data: [...], pagination: { page:2, limit:10, total:35, totalPages:4 } }
Principes architecturaux
- Architecture en couches : API → Services → Repository → Base de données
- Séparation des responsabilités : chaque couche a un rôle unique
- Contrôle d’accès basé sur les rôles (RBAC) : appliqué au niveau du middleware
- Validation centralisée : toutes les entrées sont validées avant d’atteindre la logique métier
- Réponses JSON cohérentes : format uniforme pour les succès et les erreurs
Approche Design-First (OpenAPI)
Le contrat API est documenté dans une spécification OpenAPI
(backend/docs/api/openapi.yaml) suivant une approche
Design-First :
- La spécification OpenAPI est la source de vérité unique du contrat API
- La spec est écrite avant le code
d’implémentation, dérivée des critères d’acceptation du
requirements.md - Le Frontend consomme l’API selon cette spec ; le Backend l’implémente
- Toute modification d’API doit être reflétée dans la spec en premier, puis dans le code
- Une documentation interactive est servie depuis
backend/docs/api/ui/
Conventions API RESTful
| Méthode | Endpoint | Description |
|---|---|---|
| GET | /api/tasks |
Lister les tâches (paginé) |
| POST | /api/tasks |
Créer une tâche |
| GET | /api/tasks/:id |
Obtenir une tâche |
| PATCH | /api/tasks/:id |
Mettre à jour une tâche |
| DELETE | /api/tasks/:id |
Supprimer une tâche |
| GET | /api/projects |
Lister les projets (paginé) |
| GET | /api/projects/:id/tasks |
Tâches d’un projet (paginé) |
| GET | /api/tasks/:id/history |
Historique d’une tâche |
| GET | /api/notifications |
Notifications de l’utilisateur |
| PATCH | /api/notifications/:id/read |
Marquer comme lue |
Pagination
Tous les endpoints de liste (GET /api/tasks,
GET /api/projects,
GET /api/projects/:id/tasks, etc.) sont paginés
obligatoirement :
- Paramètres de requête :
page(défaut : 1),limit(défaut : 20) - La réponse inclut les métadonnées de pagination :
{
"data": [...],
"pagination": {
"page": 1,
"limit": 20,
"total": 57,
"totalPages": 3
}
}Fuseau horaire de référence
Toutes les dates et heures sont stockées en UTC dans la base de
données. L’évaluation du retard (isOverdue) et les
notifications d’échéance sont calculées en référence au fuseau
GMT+1 (Europe/Paris).
Codes HTTP
| Situation | Code |
|---|---|
| Succès lecture | 200 |
| Création réussie | 201 |
| Suppression réussie | 204 |
| Données invalides | 400 |
| Non autorisé | 403 |
| Ressource introuvable | 404 |
| Erreur serveur | 500 |
Composants et Interfaces
Composants principaux
1. Routeur API (/api)
Reçoit les requêtes HTTP, applique les middlewares d’authentification et de validation, délègue aux services.
2. Middleware d’authentification
Vérifie le token JWT et extrait l’identité de l’utilisateur. Retourne HTTP 401 si absent ou invalide.
3. Middleware RBAC
Vérifie que l’utilisateur possède les permissions requises pour l’opération demandée.
Matrice des permissions :
| Opération | viewer | developer | manager | admin |
|---|---|---|---|---|
| Lire tâches/projets | ✅ | ✅ | ✅ | ✅ |
| Créer une tâche | ❌ | ✅ | ✅ | ✅ |
| Modifier sa propre tâche | ❌ | ✅ | ✅ | ✅ |
| S’auto-attribuer une tâche | ❌ | ✅ | ✅ | ✅ |
| Modifier la tâche d’un autre | ❌ | ❌ | ✅ | ✅ |
| Créer un projet | ❌ | ❌ | ✅ | ✅ |
| Gérer les équipes | ❌ | ❌ | ✅ | ✅ |
| Gérer les utilisateurs | ❌ | ❌ | ❌ | ✅ |
4. Service des Tâches
(TaskService)
Logique métier pour la création, mise à jour, suppression et consultation des tâches. Gère la validation des statuts et priorités, le calcul des retards, et déclenche l’enregistrement dans l’historique.
5. Service des Projets
(ProjectService)
Gestion des projets et de leurs associations avec les tâches et les équipes.
6. Service des Équipes
(TeamService)
Gestion des équipes, des membres (max 6), et des associations avec les projets.
7. Service des
Attributions (AssignmentService)
Gestion des attributions de tâches aux utilisateurs.
8. Service d’Historique
(HistoryService)
Enregistrement automatique de chaque modification de statut ou de priorité.
9.
Service de Notifications (NotificationService)
(Release 3)
Génération des notifications 24h avant échéance et lors des dépassements.
10. Repository
Couche d’accès aux données. Abstrait les requêtes SQL. Chaque
entité a son propre repository (TaskRepository,
ProjectRepository, etc.).
Format de réponse d’erreur
Toutes les erreurs retournent un JSON avec le format suivant :
{
"error": "Message d'erreur lisible",
"code": "ERROR_CODE",
"details": {}
}Modèles de Données
Entité : Task
interface Task {
id: string; // UUID v4
description: string; // 1–250 caractères, obligatoire
status: 'todo' | 'in-progress' | 'completed';
priority: 'low' | 'medium' | 'high'; // défaut: 'medium'
projectId: string | null; // optionnel
dueDate: Date | null; // optionnel, stocké en UTC
isOverdue: boolean; // calculé: !completed && dueDate < now
createdAt: Date;
createdBy: string; // userId
updatedAt: Date;
updatedBy: string | null;
completedAt: Date | null; // renseigné quand status = 'completed'
}Entité : Project
interface Project {
id: string;
name: string; // obligatoire
description: string | null;
teamId: string | null; // association optionnelle à une Team
createdAt: Date;
createdBy: string;
// Pas de parentId — sous-projets non supportés
}Entité : User
interface User {
id: string;
email: string;
name: string;
roles: Array<'admin' | 'manager' | 'developer' | 'viewer'>;
createdAt: Date;
}Entité : Team
interface Team {
id: string;
name: string; // obligatoire
memberIds: string[]; // max 6 membres
createdAt: Date;
createdBy: string;
}Entité : Assignment
interface Assignment {
id: string;
taskId: string;
userId: string;
assignedAt: Date;
assignedBy: string;
}Entité : HistoryEntry (Release 3)
interface HistoryEntry {
id: string;
taskId: string;
field: 'status' | 'priority';
previousValue: string;
newValue: string;
changedBy: string; // userId
changedAt: Date;
}Type : PaginatedResponse
interface PaginatedResponse<T> {
data: T[];
pagination: {
page: number; // page courante (1-indexed)
limit: number; // nombre d'éléments par page
total: number; // nombre total d'éléments
totalPages: number; // nombre total de pages
};
}Entité : Notification (Release 3)
interface Notification {
id: string;
userId: string;
taskId: string;
type: 'due-soon' | 'overdue';
isRead: boolean;
createdAt: Date;
}Schéma de base de données (simplifié)
erDiagram
USER {
uuid id PK
string email
string name
string[] roles
timestamp createdAt
}
PROJECT {
uuid id PK
string name
string description
uuid teamId FK
uuid createdBy FK
timestamp createdAt
}
TASK {
uuid id PK
string description
enum status
enum priority
uuid projectId FK
timestamp dueDate
uuid createdBy FK
uuid updatedBy FK
timestamp createdAt
timestamp updatedAt
timestamp completedAt
}
TEAM {
uuid id PK
string name
uuid createdBy FK
timestamp createdAt
}
TEAM_MEMBER {
uuid teamId FK
uuid userId FK
}
ASSIGNMENT {
uuid id PK
uuid taskId FK
uuid userId FK
uuid assignedBy FK
timestamp assignedAt
}
HISTORY_ENTRY {
uuid id PK
uuid taskId FK
string field
string previousValue
string newValue
uuid changedBy FK
timestamp changedAt
}
NOTIFICATION {
uuid id PK
uuid userId FK
uuid taskId FK
string type
boolean isRead
timestamp createdAt
}
USER ||--o{ TASK : "crée"
USER ||--o{ ASSIGNMENT : "reçoit"
PROJECT ||--o{ TASK : "contient"
TEAM ||--o{ TEAM_MEMBER : "a"
USER ||--o{ TEAM_MEMBER : "appartient à"
TASK ||--o{ ASSIGNMENT : "a"
TASK ||--o{ HISTORY_ENTRY : "a"
TASK ||--o{ NOTIFICATION : "génère"
USER ||--o{ NOTIFICATION : "reçoit"
TEAM ||--o| PROJECT : "associée à"
Propriétés de Correction
Une propriété est une caractéristique ou un comportement qui doit être vrai pour toutes les exécutions valides d’un système — essentiellement, un énoncé formel de ce que le système doit faire. Les propriétés servent de pont entre les spécifications lisibles par l’humain et les garanties de correction vérifiables automatiquement.
Propriété 1 : Invariant de création — identifiant unique
Pour toute entité créée (Task, Project, Team, User), l’entité retournée doit posséder un identifiant unique non nul, différent de tout identifiant existant dans le système.
Valide : Exigences 1.1, 2.1, 10.1
Propriété 2 : Priorité par défaut
Pour toute Task créée sans spécification de priorité, la
priorité enregistrée doit être "medium".
Valide : Exigence 1.2
Propriété 3 : Rejet des descriptions invalides
Pour toute chaîne de caractères composée uniquement d’espaces ou vide, la soumission comme description de Task doit être rejetée avec HTTP 400, et la liste des tâches doit rester inchangée.
Valide : Exigences 1.4, 7.1
Propriété 4 : Rejet des descriptions trop longues
Pour toute chaîne de caractères de longueur supérieure à 250 caractères, la soumission comme description de Task doit être rejetée avec HTTP 400.
Valide : Exigence 1.5
Propriété 5 : Round-trip tâches d’un projet
Pour tout Project et tout ensemble de Tasks associées à
ce Project, une requête GET /api/projects/{id}/tasks
doit retourner exactement toutes les Tasks associées à ce
Project.
Valide : Exigences 2.2, 2.3, 8.2
Propriété 6 : Validation des valeurs d’énumération — acceptation
Pour toute valeur de statut dans
{"todo", "in-progress", "completed"} et toute valeur de
priorité dans {"low", "medium", "high"}, la mise à jour
d’une Task avec ces valeurs doit être acceptée.
Valide : Exigences 3.1, 4.1
Propriété 7 : Rejet des valeurs d’énumération invalides
Pour toute chaîne de caractères n’appartenant pas à
{"todo", "in-progress", "completed"} soumise comme
statut, ou n’appartenant pas à
{"low", "medium", "high"} soumise comme priorité, la
mise à jour doit être rejetée avec HTTP 400 et le message d’erreur
doit lister les valeurs valides.
Valide : Exigences 3.2, 4.2, 7.2, 7.3
Propriété 8 : Enregistrement de completedAt
Pour toute Task dont le statut est mis à jour à
"completed", le champ completedAt doit
être renseigné avec un horodatage non nul.
Valide : Exigence 3.3
Propriété 9 : Tri par priorité
Pour tout ensemble de Tasks avec des priorités mixtes,
le tri par priorité doit toujours retourner les Tasks dans l’ordre
"high" → "medium" →
"low".
Valide : Exigence 4.4
Propriété 10 : Validation des rôles utilisateurs
Pour tout rôle valide dans
{"admin", "manager", "developer", "viewer"}, la
création d’un User avec ce rôle doit être acceptée. Pour
toute chaîne n’appartenant pas à cet ensemble, la création doit
être rejetée avec HTTP 400.
Valide : Exigences 5.1, 5.2
Propriété 11 : RBAC — viewer ne peut pas écrire
Pour tout User avec uniquement le rôle
"viewer", toute tentative de création ou modification
de Task, Project, Team ou Assignment doit être rejetée avec HTTP
403.
Valide : Exigence 5.3
Propriété 12 : RBAC — developer ne peut modifier que ses propres tâches
Pour tout User avec le rôle "developer", la
modification d’une Task qui ne lui est pas attribuée doit être
rejetée avec HTTP 403, tandis que la modification d’une Task qui lui
est attribuée doit être acceptée.
Valide : Exigence 5.4
Propriété 13 : Round-trip échéance
Pour toute Task créée avec une dueDate
précise, la récupération de cette Task doit retourner exactement la
même valeur de dueDate (précision date et heure,
UTC).
Valide : Exigence 6.1
Propriété 14 : Invariant de retard
Pour toute Task dont le statut n’est pas
"completed" et dont la dueDate est
antérieure à l’instant présent, le champ isOverdue doit
être true.
Valide : Exigence 6.2
Propriété 15 : Tri par échéance
Pour tout ensemble de Tasks avec des
dueDate différentes, le tri par échéance doit retourner
les Tasks dans l’ordre chronologique croissant.
Valide : Exigence 6.3
Propriété 16 : Rejet d’attribution à un User inexistant
Pour tout userId ne correspondant à aucun
User existant, toute tentative d’attribution de Task à cet
identifiant doit être rejetée avec HTTP 404.
Valide : Exigence 7.5
Propriété 17 : Format d’erreur cohérent
Pour toute réponse d’erreur retournée par l’API (codes
400, 403, 404, 500), le corps de la réponse doit être un JSON
contenant au minimum un champ "error" avec un message
non vide.
Valide : Exigences 8.4, 8.5
Propriété 18 : Round-trip attribution
Pour toute Task et tout User existants, la création d’un
Assignment doit produire un enregistrement récupérable contenant
taskId, userId et un
assignedAt non nul.
Valide : Exigence 9.1
Propriété 19 : Attributions multiples indépendantes
Pour toute Task et tout ensemble de N Users distincts, la création de N Assignments doit produire N enregistrements distincts, chacun liant la Task à un User différent.
Valide : Exigence 9.2
Propriété 20 : Round-trip tâches attribuées à un User
Pour tout User et tout ensemble de Tasks qui lui sont
attribuées, une requête
GET /api/tasks?assignedTo={userId} doit retourner
exactement toutes ces Tasks.
Valide : Exigence 9.3
Propriété 21 : Suppression d’Assignment n’affecte pas la Task
Pour tout Assignment supprimé, la Task associée doit
toujours exister et être récupérable via
GET /api/tasks/{id}.
Valide : Exigence 9.4
Propriété 22 : Contrainte de capacité des équipes
Pour toute Team ayant déjà 6 membres, toute tentative d’ajout d’un membre supplémentaire doit être rejetée avec HTTP 400.
Valide : Exigence 10.3
Propriété 23 : Round-trip membres d’une équipe
Pour toute Team et tout ensemble de membres ajoutés, la consultation de cette Team doit retourner exactement tous les membres enregistrés.
Valide : Exigences 10.2, 10.4
Propriété 24 : Compteurs du tableau de bord
Pour tout ensemble de Tasks, les compteurs du tableau de bord par statut doivent correspondre exactement au nombre de Tasks ayant chaque statut dans cet ensemble.
Valide : Exigence 11.1
Propriété 25 : Filtrage du tableau de bord par projet
Pour tout Project, les statistiques du tableau de bord filtrées par ce Project ne doivent inclure que les Tasks appartenant à ce Project.
Valide : Exigence 11.2
Propriété 26 : Taux de complétion
Pour tout ensemble de N Tasks dont K ont le statut
"completed", le taux de complétion affiché doit être
égal à K / N * 100 (arrondi à l’entier le plus
proche).
Valide : Exigence 11.3
Propriété 27 : Enregistrement de l’historique des modifications
Pour toute modification de statut ou de priorité d’une Task, une entrée d’historique doit être créée contenant : l’identifiant de l’auteur, l’horodatage, le champ modifié, la valeur précédente et la nouvelle valeur.
Valide : Exigences 12.1, 12.2
Propriété 28 : Round-trip historique
Pour toute Task ayant subi N modifications, une requête
GET /api/tasks/{id}/history doit retourner exactement N
entrées dans l’ordre chronologique croissant.
Valide : Exigence 12.3
Propriété 29 : Génération de notification avant échéance
Pour toute Task non complétée dont la
dueDate est dans moins de 24 heures, le service de
notifications doit générer une notification de type
"due-soon" pour chaque User assigné à cette Task.
Valide : Exigence 13.1
Propriété 30 : Génération de notification de retard
Pour toute Task non complétée dont la
dueDate est dépassée, le service de notifications doit
générer une notification de type "overdue" pour chaque
User assigné à cette Task.
Valide : Exigence 13.2
Propriété 31 : Pas de notification pour tâche complétée
Pour toute Task avec le statut "completed",
aucune notification de retard ne doit être générée, même si la
dueDate est dépassée.
Valide : Exigence 13.3
Propriété 32 : Round-trip marquer notification comme lue
Pour toute notification non lue, après l’appel
PATCH /api/notifications/{id}/read, la notification
doit avoir isRead = true lorsqu’elle est récupérée.
Valide : Exigences 13.4, 13.5
Propriété 33 : Pagination des endpoints de liste
Pour tout endpoint de liste
(GET /api/tasks, GET /api/projects,
GET /api/projects/{id}/tasks), la réponse doit contenir
un objet pagination avec les champs page,
limit, total et totalPages.
Le nombre d’éléments retournés dans data ne doit jamais
dépasser limit. Si aucun paramètre de pagination n’est
fourni, les valeurs par défaut page=1 et
limit=20 doivent être appliquées.
Valide : Exigences 8.6, 8.7
Propriété 34 : Auto-attribution par un developer
Pour tout User avec le rôle "developer" et
toute Task existante, la création d’un Assignment par ce User pour
lui-même via POST /api/tasks/{id}/assignments doit être
acceptée et produire un enregistrement récupérable contenant
taskId, userId et un
assignedAt non nul.
Valide : Exigence 9.5
Propriété 35 : Évaluation du retard en GMT+1
Pour toute Task non complétée dont la
dueDate est exprimée en UTC, l’évaluation du champ
isOverdue doit être effectuée en convertissant
l’instant présent et la dueDate dans le fuseau GMT+1
(Europe/Paris). Une Task dont la dueDate est
2025-06-15T23:59:00+01:00 doit être identifiée comme en
retard à 2025-06-16T00:01:00+01:00 (heure GMT+1).
Valide : Exigence 6.6
Gestion des Erreurs
Stratégie globale
Toutes les erreurs sont interceptées par un middleware centralisé qui garantit un format de réponse JSON cohérent :
{
"error": "Message d'erreur lisible par l'humain",
"code": "ERROR_CODE_SNAKE_CASE",
"details": { "field": "description", "constraint": "maxLength" }
}Catalogue des erreurs
| Code d’erreur | HTTP | Cause |
|---|---|---|
VALIDATION_ERROR |
400 | Données d’entrée invalides (description vide, statut/priorité invalide, etc.) |
SUBPROJECT_NOT_SUPPORTED |
400 | Tentative de création d’un sous-projet |
TEAM_CAPACITY_EXCEEDED |
400 | Ajout d’un 7ème membre à une équipe |
FORBIDDEN |
403 | Opération non autorisée pour le rôle de l’utilisateur |
NOT_FOUND |
404 | Ressource introuvable (Task, Project, User, Team, Assignment) |
INTERNAL_ERROR |
500 | Erreur serveur inattendue |
Avertissements (non bloquants)
Certaines situations génèrent un avertissement dans la réponse sans bloquer l’opération :
{
"data": { "...": "task créée" },
"warnings": ["L'échéance spécifiée est déjà dépassée"]
}Cas concernés : - Création d’une Task avec une
dueDate dans le passé (Exigence 7.4)
Gestion des erreurs par couche
- Couche API : Capture les erreurs de validation (Zod/Joi) et retourne HTTP 400
- Couche Service : Lève des erreurs métier typées
(
NotFoundError,ForbiddenError,ValidationError) - Couche Repository : Propage les erreurs de base de données encapsulées
- Middleware global : Transforme toutes les erreurs non gérées en HTTP 500
Stratégie de Tests
Approche duale
Le projet utilise deux types de tests complémentaires :
- Tests unitaires (TDD) : Vérifient des exemples spécifiques, les cas limites et les conditions d’erreur
- Tests de propriétés (PBT) : Vérifient les propriétés universelles sur des entrées générées aléatoirement
Tests de propriétés (Property-Based Testing)
Bibliothèque : fast-check (TypeScript/JavaScript)
Configuration : - Minimum 100 itérations par test de propriété - Chaque test référence la propriété du document de conception via un commentaire de tag
Format de tag :
// Feature: task-management-system, Property N: <texte de la propriété>Exemple de test de propriété :
import fc from 'fast-check';
// Feature: task-management-system, Property 3: Rejet des descriptions invalides
it('rejette toute description vide ou composée uniquement d\'espaces', () => {
fc.assert(
fc.property(
fc.stringOf(fc.constantFrom(' ', '\t', '\n')),
(emptyDescription) => {
const result = validateTaskDescription(emptyDescription);
expect(result.valid).toBe(false);
expect(result.statusCode).toBe(400);
}
),
{ numRuns: 100 }
);
});Tests unitaires (BDD + TDD)
Bibliothèques : - Jest ou Vitest pour les tests unitaires - Supertest pour les tests d’intégration API - Cucumber.js pour les tests d’acceptation BDD (scénarios Gherkin)
Organisation des tests :
tests/
├── unit/
│ ├── services/
│ │ ├── taskService.test.ts
│ │ ├── projectService.test.ts
│ │ └── ...
│ └── validators/
│ └── taskValidator.test.ts
├── integration/
│ ├── api/
│ │ ├── tasks.test.ts
│ │ └── projects.test.ts
├── property/
│ ├── task.property.test.ts
│ ├── project.property.test.ts
│ └── ...
└── acceptance/
├── features/
│ ├── task-creation.feature
│ └── ...
└── step-definitions/
Couverture par release
Release 1 — MVP : Propriétés 1–17, 33, 35 + tests unitaires pour tous les scénarios Gherkin des Exigences 1–8
Release 2 — Collaboration : Propriétés 18–23, 34 + tests unitaires pour les Exigences 9–10
Release 3 — Pilotage : Propriétés 24–32 + tests unitaires pour les Exigences 11–13
Living Document
Les tests d’acceptation Cucumber.js génèrent automatiquement un
living document de la spécification. Ce document
HTML est produit à partir des résultats d’exécution des scénarios
Gherkin et est accessible à l’adresse
http://localhost:3000/docs/living-document/ dans le
sandbox.
Règles de test
- Chaque critère d’acceptation Gherkin doit avoir un test d’acceptation BDD correspondant
- Chaque propriété de correction doit être implémentée par un seul test de propriété
- Les tests de propriétés doivent utiliser des générateurs couvrant les cas limites (chaînes vides, valeurs limites, UUIDs invalides)
- Les tests unitaires se concentrent sur les exemples spécifiques, les cas limites et les conditions d’erreur