first commit
This commit is contained in:
116
CONTRIBUTING.md
Normal file
116
CONTRIBUTING.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# Contribuer au projet
|
||||
|
||||
## Prérequis
|
||||
|
||||
Les mêmes que pour le développement (voir [README](README.md)), plus :
|
||||
|
||||
- PHP 8.1+ avec l'extension `dom` (requise par HTMLPurifier et PHPUnit)
|
||||
|
||||
## Lancer les tests
|
||||
|
||||
```bash
|
||||
composer install
|
||||
vendor/bin/phpunit
|
||||
```
|
||||
|
||||
Avec rapport de couverture (nécessite Xdebug ou PCOV) :
|
||||
|
||||
```bash
|
||||
vendor/bin/phpunit --coverage-text
|
||||
```
|
||||
|
||||
## Analyse statique (PHPStan)
|
||||
|
||||
```bash
|
||||
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 :
|
||||
|
||||
```php
|
||||
// ✅ 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](docs/ARCHITECTURE.md). 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
|
||||
|
||||
```bash
|
||||
vendor/bin/php-cs-fixer fix
|
||||
```
|
||||
|
||||
Prévisualiser sans appliquer :
|
||||
|
||||
```bash
|
||||
vendor/bin/php-cs-fixer fix --dry-run
|
||||
```
|
||||
Reference in New Issue
Block a user