5.7 KiB
Contribuer au projet
Prérequis
Les mêmes que pour le développement (voir README), plus :
- PHP 8.4.1+ avec l'extension
dom(requise par HTMLPurifier et PHPUnit)
Lancer les tests
composer install
vendor/bin/phpunit
Avec rapport de couverture (nécessite Xdebug ou PCOV) :
vendor/bin/phpunit --coverage-text
Analyse statique (PHPStan)
vendor/bin/phpstan analyse
Cette commande utilise phpstan.neon qui définit le niveau 8 et les chemins analysés. PDO est nativement connu de PHPStan — aucun stub supplémentaire n'est nécessaire.
PHPStan est configuré au niveau 8, le plus strict. L'exécuter avant toute Pull Request garantit la cohérence des types et détecte les erreurs avant l'exécution.
Suite de tests
Les tests sont dans tests/, organisés en miroir de src/.
tests/Auth/
| Fichier | Classe testée | Ce qui est vérifié |
|---|---|---|
AuthServiceTest |
AuthService |
createUser() (normalisation, unicité via exceptions métier, longueur mdp), authenticate(), changePassword(), login/logout/isLoggedIn() |
AuthServiceRateLimitTest |
AuthService |
checkRateLimit() (IP libre, verrouillée, expirée, minimum 1 minute, deleteExpired()), recordFailure() (constantes MAX_ATTEMPTS/LOCK_MINUTES), resetRateLimit() |
LoginAttemptRepositoryTest |
LoginAttemptRepository |
findByIp(), recordFailure() (INSERT vs UPDATE, compteur, seuil exact, fenêtre temporelle), resetForIp(), deleteExpired() |
PasswordResetServiceTest |
PasswordResetService |
requestReset() (email inconnu silencieux, invalidation, création, envoi, URL), validateToken() (inexistant, expiré, valide), resetPassword() (token invalide, mdp trop court via WeakPasswordException, mise à jour + consommation) |
PasswordResetRepositoryTest |
PasswordResetRepository |
create(), findActiveByHash() (filtre used_at = null), invalidateByUserId() et markAsUsed() (jamais de delete) |
tests/User/
| Fichier | Classe testée | Ce qui est vérifié |
|---|---|---|
UserTest |
User |
Construction, validation (username, email, hash, rôle), limites min/max, fromArray() |
UserRepositoryTest |
UserRepository |
findAll/ById/ByUsername/ByEmail(), create(), updatePassword(), delete() |
tests/Shared/
| Fichier | Classe testée | Ce qui est vérifié |
|---|---|---|
SessionManagerTest |
SessionManager / SessionManagerInterface |
isAuthenticated(), getUserId(), rôles, écriture $_SESSION, destroy() |
HtmlSanitizerTest |
HtmlSanitizer |
Balises autorisées conservées, XSS supprimé (<script>, handlers JS, javascript:, data:, <iframe>, <form>), CSS (text-align conservé, reste supprimé) |
MigratorTest |
Migrator |
Création de la table migrations, idempotence de run(), non-rejeu des migrations déjà appliquées, enregistrement en table, syncFtsIndex() (indexation des articles absents, absence de doublons) |
SeederTest |
Seeder |
Insertion du compte admin quand absent, idempotence (pas d'INSERT si le compte existe), normalisation username/email, hachage bcrypt, format created_at |
Conventions
Mocks
Les services métier (AuthService, PasswordResetService, PostService) utilisent les interfaces comme type des dépendances. Mocker l'interface plutôt que la classe concrète :
// ✅ Correct
$repo = $this->createMock(UserRepositoryInterface::class);
// ❌ À éviter
$repo = $this->createMock(UserRepository::class);
Les tests de repositories (UserRepositoryTest, etc.) testent l'implémentation concrète avec un mock PDO — c'est intentionnel.
Exceptions métier
Les erreurs métier doivent lever l'exception la plus spécifique disponible :
| Situation | Exception | Namespace |
|---|---|---|
| Nom d'utilisateur déjà pris | DuplicateUsernameException |
App\User\Exception |
| Email déjà utilisé | DuplicateEmailException |
App\User\Exception |
| Mot de passe trop court | WeakPasswordException |
App\User\Exception |
| Entité introuvable en base | NotFoundException |
App\Shared\Exception |
Ajouter un test
- Nommage :
test+ description camelCase français (testCreateUserNomDejaUtilise) - Structure : Arrange / Act / Assert, une assertion logique par test
- Isolation : dépendances toujours mockées via leur interface
- PHPDoc : chaque méthode de test est documentée avec une ligne décrivant le comportement attendu
Ajouter un domaine
Voir la section Domaines PHP dans Architecture. Lors de l'ajout d'un domaine, créer systématiquement :
- L'entité (
NomDomaine/NomEntite.php) - L'interface du dépôt (
NomDomaine/NomEntiteRepositoryInterface.php) - L'implémentation du dépôt (
NomDomaine/NomEntiteRepository.php implements NomEntiteRepositoryInterface) - Les exceptions métier si nécessaires (
NomDomaine/Exception/) - Les tests dans
tests/NomDomaine/
Formatage du code
vendor/bin/php-cs-fixer fix
Prévisualiser sans appliquer :
vendor/bin/php-cs-fixer fix --dry-run