Files
slim-blog/CONTRIBUTING.md
2026-03-16 16:58:54 +01:00

6.0 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 AuthApplicationService createUser() (normalisation, unicité via exceptions métier, longueur mdp), authenticate(), changePassword(), login/logout/isLoggedIn()
AuthServiceRateLimitTest AuthApplicationService checkRateLimit() (IP libre, verrouillée, expirée, minimum 1 minute, deleteExpired()), recordFailure() (constantes MAX_ATTEMPTS/LOCK_MINUTES), resetRateLimit()
LoginAttemptRepositoryTest PdoLoginAttemptRepository findByIp(), recordFailure() (INSERT vs UPDATE, compteur, seuil exact, fenêtre temporelle), resetForIp(), deleteExpired()
PasswordResetServiceTest PasswordResetApplicationService 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 PdoPasswordResetRepository 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 PdoUserRepository 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 applicatifs (AuthApplicationService, PasswordResetApplicationService, PostApplicationService) 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 (PdoUserRepository, etc.) testent l'implémentation concrète avec un mock PDO — c'est intentionnel. Ils doivent vérifier l'intention générale des requêtes et les valeurs retournées, sans figer inutilement chaque détail interne (noms exacts de placeholders, méthode PDO précise utilisée quand cela n'apporte rien, etc.).

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 :

  1. L'entité (NomDomaine/NomEntite.php)
  2. L'interface du dépôt (NomDomaine/NomEntiteRepositoryInterface.php)
  3. L'implémentation du dépôt (NomDomaine/NomEntiteRepository.php implements NomEntiteRepositoryInterface)
  4. Les exceptions métier si nécessaires (NomDomaine/Exception/)
  5. 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