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 :

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

Approche Design-First (OpenAPI)

Le contrat API est documenté dans une spécification OpenAPI (backend/docs/api/openapi.yaml) suivant une approche Design-First :

  1. La spécification OpenAPI est la source de vérité unique du contrat API
  2. La spec est écrite avant le code d’implémentation, dérivée des critères d’acceptation du requirements.md
  3. Le Frontend consomme l’API selon cette spec ; le Backend l’implémente
  4. Toute modification d’API doit être reflétée dans la spec en premier, puis dans le code
  5. 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 :

{
  "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


Stratégie de Tests

Approche duale

Le projet utilise deux types de tests complémentaires :

  1. Tests unitaires (TDD) : Vérifient des exemples spécifiques, les cas limites et les conditions d’erreur
  2. 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