Working state but no uploads
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -24,6 +24,7 @@ public/assets/
|
|||||||
database/*.sqlite
|
database/*.sqlite
|
||||||
database/*.sqlite-shm
|
database/*.sqlite-shm
|
||||||
database/*.sqlite-wal
|
database/*.sqlite-wal
|
||||||
|
database/.provision.lock
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
# Cache & Logs
|
# Cache & Logs
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ interface PasswordResetRepositoryInterface
|
|||||||
public function create(int $userId, string $tokenHash, string $expiresAt): void;
|
public function create(int $userId, string $tokenHash, string $expiresAt): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consomme atomiquement un token non utilisé et non expiré.
|
* @return array<string, mixed>|null
|
||||||
*
|
|
||||||
* L'implémentation doit effectuer l'opération en une seule étape SQL
|
|
||||||
* afin d'éviter les courses entre lecture et écriture.
|
|
||||||
*
|
|
||||||
* @param string $tokenHash Hash SHA-256 du token de reset
|
|
||||||
* @param string $usedAt Horodatage de consommation au format SQL
|
|
||||||
* @return array<string, mixed>|null Les données du token consommé, ou null si le token est invalide, expiré ou déjà utilisé
|
|
||||||
*/
|
*/
|
||||||
public function consumeActiveToken(string $tokenHash, string $usedAt): ?array;
|
public function findActiveByHash(string $tokenHash): ?array;
|
||||||
|
|
||||||
|
public function invalidateByUserId(int $userId): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>|null
|
||||||
|
*/
|
||||||
|
public function consumeActiveToken(string $tokenHash, string $usedAt): ?array;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use Tests\ControllerTestCase;
|
|||||||
* mots de passe non identiques, mot de passe faible, mot de passe actuel
|
* mots de passe non identiques, mot de passe faible, mot de passe actuel
|
||||||
* incorrect, erreur inattendue et succès.
|
* incorrect, erreur inattendue et succès.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class AccountControllerTest extends ControllerTestCase
|
final class AccountControllerTest extends ControllerTestCase
|
||||||
{
|
{
|
||||||
/** @var \Slim\Views\Twig&MockObject */
|
/** @var \Slim\Views\Twig&MockObject */
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use Tests\ControllerTestCase;
|
|||||||
* AuthService, FlashService et Twig sont mockés — aucune session PHP,
|
* AuthService, FlashService et Twig sont mockés — aucune session PHP,
|
||||||
* aucune base de données, aucun serveur HTTP n'est requis.
|
* aucune base de données, aucun serveur HTTP n'est requis.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class AuthControllerTest extends ControllerTestCase
|
final class AuthControllerTest extends ControllerTestCase
|
||||||
{
|
{
|
||||||
/** @var \Slim\Views\Twig&MockObject */
|
/** @var \Slim\Views\Twig&MockObject */
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* - MAX_ATTEMPTS = 5 : nombre d'échecs avant verrouillage
|
* - MAX_ATTEMPTS = 5 : nombre d'échecs avant verrouillage
|
||||||
* - LOCK_MINUTES = 15 : durée du verrouillage en minutes
|
* - LOCK_MINUTES = 15 : durée du verrouillage en minutes
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class AuthServiceRateLimitTest extends TestCase
|
final class AuthServiceRateLimitTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var UserRepositoryInterface&MockObject */
|
/** @var UserRepositoryInterface&MockObject */
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* Les dépendances sont remplacées par des mocks via leurs interfaces pour
|
* Les dépendances sont remplacées par des mocks via leurs interfaces pour
|
||||||
* isoler le service.
|
* isoler le service.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class AuthServiceTest extends TestCase
|
final class AuthServiceTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var UserRepositoryInterface&MockObject */
|
/** @var UserRepositoryInterface&MockObject */
|
||||||
@@ -58,7 +59,7 @@ final class AuthServiceTest extends TestCase
|
|||||||
$password = 'motdepasse1';
|
$password = 'motdepasse1';
|
||||||
$user = $this->makeUser('alice', 'alice@example.com', $password);
|
$user = $this->makeUser('alice', 'alice@example.com', $password);
|
||||||
|
|
||||||
$this->userRepository->method('findByUsername')->with('alice')->willReturn($user);
|
$this->userRepository->expects($this->once())->method('findByUsername')->with('alice')->willReturn($user);
|
||||||
|
|
||||||
$result = $this->service->authenticate('alice', $password);
|
$result = $this->service->authenticate('alice', $password);
|
||||||
|
|
||||||
@@ -73,7 +74,7 @@ final class AuthServiceTest extends TestCase
|
|||||||
$password = 'motdepasse1';
|
$password = 'motdepasse1';
|
||||||
$user = $this->makeUser('alice', 'alice@example.com', $password);
|
$user = $this->makeUser('alice', 'alice@example.com', $password);
|
||||||
|
|
||||||
$this->userRepository->method('findByUsername')->with('alice')->willReturn($user);
|
$this->userRepository->expects($this->once())->method('findByUsername')->with('alice')->willReturn($user);
|
||||||
|
|
||||||
$result = $this->service->authenticate('ALICE', $password);
|
$result = $this->service->authenticate('ALICE', $password);
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ final class AuthServiceTest extends TestCase
|
|||||||
$password = 'ancienmdp1';
|
$password = 'ancienmdp1';
|
||||||
$user = $this->makeUser('alice', 'alice@example.com', $password, 1);
|
$user = $this->makeUser('alice', 'alice@example.com', $password, 1);
|
||||||
|
|
||||||
$this->userRepository->method('findById')->with(1)->willReturn($user);
|
$this->userRepository->expects($this->once())->method('findById')->with(1)->willReturn($user);
|
||||||
$this->userRepository->expects($this->once())->method('updatePassword')->with(1);
|
$this->userRepository->expects($this->once())->method('updatePassword')->with(1);
|
||||||
|
|
||||||
$this->service->changePassword(1, $password, 'nouveaumdp1');
|
$this->service->changePassword(1, $password, 'nouveaumdp1');
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* PDO et PDOStatement sont mockés pour isoler complètement
|
* PDO et PDOStatement sont mockés pour isoler complètement
|
||||||
* le dépôt de la base de données.
|
* le dépôt de la base de données.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class LoginAttemptRepositoryTest extends TestCase
|
final class LoginAttemptRepositoryTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var PDO&MockObject */
|
/** @var PDO&MockObject */
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ use Psr\Http\Server\RequestHandlerInterface;
|
|||||||
use Slim\Psr7\Factory\ServerRequestFactory;
|
use Slim\Psr7\Factory\ServerRequestFactory;
|
||||||
use Slim\Psr7\Response;
|
use Slim\Psr7\Response;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class MiddlewareTest extends TestCase
|
final class MiddlewareTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var SessionManagerInterface&MockObject */
|
/** @var SessionManagerInterface&MockObject */
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ use Tests\ControllerTestCase;
|
|||||||
* - showReset() valide le token avant d'afficher le formulaire
|
* - showReset() valide le token avant d'afficher le formulaire
|
||||||
* - reset() couvre 5 chemins de sortie (token vide, mismatch, trop court, invalide, succès)
|
* - reset() couvre 5 chemins de sortie (token vide, mismatch, trop court, invalide, succès)
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class PasswordResetControllerTest extends ControllerTestCase
|
final class PasswordResetControllerTest extends ControllerTestCase
|
||||||
{
|
{
|
||||||
/** @var \Slim\Views\Twig&MockObject */
|
/** @var \Slim\Views\Twig&MockObject */
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* PDO et PDOStatement sont mockés pour isoler complètement
|
* PDO et PDOStatement sont mockés pour isoler complètement
|
||||||
* le dépôt de la base de données.
|
* le dépôt de la base de données.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class PasswordResetRepositoryTest extends TestCase
|
final class PasswordResetRepositoryTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var PDO&MockObject */
|
/** @var PDO&MockObject */
|
||||||
@@ -60,7 +61,7 @@ final class PasswordResetRepositoryTest extends TestCase
|
|||||||
$expiresAt = date('Y-m-d H:i:s', time() + 3600);
|
$expiresAt = date('Y-m-d H:i:s', time() + 3600);
|
||||||
$stmt = $this->stmtOk();
|
$stmt = $this->stmtOk();
|
||||||
|
|
||||||
$this->db->method('prepare')
|
$this->db->expects($this->once())->method('prepare')
|
||||||
->with($this->stringContains('INSERT INTO password_resets'))
|
->with($this->stringContains('INSERT INTO password_resets'))
|
||||||
->willReturn($stmt);
|
->willReturn($stmt);
|
||||||
|
|
||||||
@@ -162,7 +163,7 @@ final class PasswordResetRepositoryTest extends TestCase
|
|||||||
$userId = 42;
|
$userId = 42;
|
||||||
$stmt = $this->stmtOk();
|
$stmt = $this->stmtOk();
|
||||||
|
|
||||||
$this->db->method('prepare')
|
$this->db->expects($this->once())->method('prepare')
|
||||||
->with($this->stringContains('UPDATE password_resets'))
|
->with($this->stringContains('UPDATE password_resets'))
|
||||||
->willReturn($stmt);
|
->willReturn($stmt);
|
||||||
|
|
||||||
@@ -200,7 +201,7 @@ final class PasswordResetRepositoryTest extends TestCase
|
|||||||
public function testInvalidateByUserIdNeverCallsDelete(): void
|
public function testInvalidateByUserIdNeverCallsDelete(): void
|
||||||
{
|
{
|
||||||
$stmt = $this->stmtOk();
|
$stmt = $this->stmtOk();
|
||||||
$this->db->method('prepare')
|
$this->db->expects($this->once())->method('prepare')
|
||||||
->with($this->stringContains('UPDATE'))
|
->with($this->stringContains('UPDATE'))
|
||||||
->willReturn($stmt);
|
->willReturn($stmt);
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ use App\User\UserRepository;
|
|||||||
use PDO;
|
use PDO;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class PasswordResetServiceIntegrationTest extends TestCase
|
final class PasswordResetServiceIntegrationTest extends TestCase
|
||||||
{
|
{
|
||||||
private PDO $db;
|
private PDO $db;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* Vérifie la génération de token, la validation et la réinitialisation
|
* Vérifie la génération de token, la validation et la réinitialisation
|
||||||
* du mot de passe. Les dépendances sont mockées via leurs interfaces.
|
* du mot de passe. Les dépendances sont mockées via leurs interfaces.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class PasswordResetServiceTest extends TestCase
|
final class PasswordResetServiceTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var PasswordResetRepositoryInterface&MockObject */
|
/** @var PasswordResetRepositoryInterface&MockObject */
|
||||||
@@ -95,7 +96,7 @@ final class PasswordResetServiceTest extends TestCase
|
|||||||
$this->resetRepository->method('invalidateByUserId');
|
$this->resetRepository->method('invalidateByUserId');
|
||||||
$this->resetRepository->expects($this->once())
|
$this->resetRepository->expects($this->once())
|
||||||
->method('create')
|
->method('create')
|
||||||
->with($user->getId(), $this->isType('string'), $this->isType('string'));
|
->with($user->getId(), $this->callback('is_string'), $this->callback('is_string'));
|
||||||
$this->mailService->method('send');
|
$this->mailService->method('send');
|
||||||
|
|
||||||
$this->service->requestReset('alice@example.com', 'https://blog.exemple.com');
|
$this->service->requestReset('alice@example.com', 'https://blog.exemple.com');
|
||||||
@@ -116,9 +117,9 @@ final class PasswordResetServiceTest extends TestCase
|
|||||||
->method('send')
|
->method('send')
|
||||||
->with(
|
->with(
|
||||||
'alice@example.com',
|
'alice@example.com',
|
||||||
$this->isType('string'),
|
$this->callback('is_string'),
|
||||||
'emails/password-reset.twig',
|
'emails/password-reset.twig',
|
||||||
$this->isType('array'),
|
$this->callback('is_array'),
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->service->requestReset('alice@example.com', 'https://blog.exemple.com');
|
$this->service->requestReset('alice@example.com', 'https://blog.exemple.com');
|
||||||
@@ -173,7 +174,7 @@ final class PasswordResetServiceTest extends TestCase
|
|||||||
$tokenRaw = 'montokenbrut';
|
$tokenRaw = 'montokenbrut';
|
||||||
$tokenHash = hash('sha256', $tokenRaw);
|
$tokenHash = hash('sha256', $tokenRaw);
|
||||||
|
|
||||||
$this->resetRepository->method('findActiveByHash')->with($tokenHash)->willReturn([
|
$this->resetRepository->expects($this->once())->method('findActiveByHash')->with($tokenHash)->willReturn([
|
||||||
'user_id' => 1,
|
'user_id' => 1,
|
||||||
'token_hash' => $tokenHash,
|
'token_hash' => $tokenHash,
|
||||||
'expires_at' => date('Y-m-d H:i:s', time() - 3600),
|
'expires_at' => date('Y-m-d H:i:s', time() - 3600),
|
||||||
@@ -194,13 +195,13 @@ final class PasswordResetServiceTest extends TestCase
|
|||||||
$tokenRaw = 'montokenbrut';
|
$tokenRaw = 'montokenbrut';
|
||||||
$tokenHash = hash('sha256', $tokenRaw);
|
$tokenHash = hash('sha256', $tokenRaw);
|
||||||
|
|
||||||
$this->resetRepository->method('findActiveByHash')->with($tokenHash)->willReturn([
|
$this->resetRepository->expects($this->once())->method('findActiveByHash')->with($tokenHash)->willReturn([
|
||||||
'user_id' => $user->getId(),
|
'user_id' => $user->getId(),
|
||||||
'token_hash' => $tokenHash,
|
'token_hash' => $tokenHash,
|
||||||
'expires_at' => date('Y-m-d H:i:s', time() + 3600),
|
'expires_at' => date('Y-m-d H:i:s', time() + 3600),
|
||||||
'used_at' => null,
|
'used_at' => null,
|
||||||
]);
|
]);
|
||||||
$this->userRepository->method('findById')->with($user->getId())->willReturn($user);
|
$this->userRepository->expects($this->once())->method('findById')->with($user->getId())->willReturn($user);
|
||||||
|
|
||||||
$result = $this->service->validateToken($tokenRaw);
|
$result = $this->service->validateToken($tokenRaw);
|
||||||
|
|
||||||
@@ -224,8 +225,8 @@ final class PasswordResetServiceTest extends TestCase
|
|||||||
'used_at' => null,
|
'used_at' => null,
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->resetRepository->method('findActiveByHash')->willReturn($row);
|
$this->resetRepository->expects($this->once())->method('findActiveByHash')->willReturn($row);
|
||||||
$this->userRepository->method('findById')->with(999)->willReturn(null);
|
$this->userRepository->expects($this->once())->method('findById')->with(999)->willReturn(null);
|
||||||
|
|
||||||
$result = $this->service->validateToken('token-valide-mais-user-supprime');
|
$result = $this->service->validateToken('token-valide-mais-user-supprime');
|
||||||
|
|
||||||
@@ -282,7 +283,7 @@ final class PasswordResetServiceTest extends TestCase
|
|||||||
|
|
||||||
$this->resetRepository->expects($this->once())
|
$this->resetRepository->expects($this->once())
|
||||||
->method('consumeActiveToken')
|
->method('consumeActiveToken')
|
||||||
->with($tokenHash, $this->isType('string'))
|
->with($tokenHash, $this->callback('is_string'))
|
||||||
->willReturn([
|
->willReturn([
|
||||||
'user_id' => $user->getId(),
|
'user_id' => $user->getId(),
|
||||||
'token_hash' => $tokenHash,
|
'token_hash' => $tokenHash,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use Tests\ControllerTestCase;
|
|||||||
* rendu de la liste, création réussie, erreur de création,
|
* rendu de la liste, création réussie, erreur de création,
|
||||||
* suppression avec catégorie introuvable, succès et erreur métier.
|
* suppression avec catégorie introuvable, succès et erreur métier.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class CategoryControllerTest extends ControllerTestCase
|
final class CategoryControllerTest extends ControllerTestCase
|
||||||
{
|
{
|
||||||
/** @var \Slim\Views\Twig&MockObject */
|
/** @var \Slim\Views\Twig&MockObject */
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ namespace Tests\Category;
|
|||||||
use App\Category\Category;
|
use App\Category\Category;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class CategoryModelTest extends TestCase
|
final class CategoryModelTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testConstructAndGettersExposeCategoryData(): void
|
public function testConstructAndGettersExposeCategoryData(): void
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* PDO et PDOStatement sont mockés pour isoler complètement
|
* PDO et PDOStatement sont mockés pour isoler complètement
|
||||||
* le dépôt de la base de données.
|
* le dépôt de la base de données.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class CategoryRepositoryTest extends TestCase
|
final class CategoryRepositoryTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var PDO&MockObject */
|
/** @var PDO&MockObject */
|
||||||
@@ -221,7 +222,7 @@ final class CategoryRepositoryTest extends TestCase
|
|||||||
$category = Category::fromArray($this->rowPhp);
|
$category = Category::fromArray($this->rowPhp);
|
||||||
$stmt = $this->stmtForWrite();
|
$stmt = $this->stmtForWrite();
|
||||||
|
|
||||||
$this->db->method('prepare')
|
$this->db->expects($this->once())->method('prepare')
|
||||||
->with($this->stringContains('INSERT INTO categories'))
|
->with($this->stringContains('INSERT INTO categories'))
|
||||||
->willReturn($stmt);
|
->willReturn($stmt);
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* et la suppression (blocage si articles rattachés).
|
* et la suppression (blocage si articles rattachés).
|
||||||
* Le repository est remplacé par un mock pour isoler le service.
|
* Le repository est remplacé par un mock pour isoler le service.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class CategoryServiceTest extends TestCase
|
final class CategoryServiceTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var CategoryRepositoryInterface&MockObject */
|
/** @var CategoryRepositoryInterface&MockObject */
|
||||||
@@ -129,7 +130,7 @@ final class CategoryServiceTest extends TestCase
|
|||||||
{
|
{
|
||||||
$category = new Category(5, 'PHP', 'php');
|
$category = new Category(5, 'PHP', 'php');
|
||||||
|
|
||||||
$this->repository->method('hasPost')->with(5)->willReturn(false);
|
$this->repository->expects($this->once())->method('hasPost')->with(5)->willReturn(false);
|
||||||
$this->repository->expects($this->once())->method('delete')->with(5);
|
$this->repository->expects($this->once())->method('delete')->with(5);
|
||||||
|
|
||||||
$this->service->delete($category);
|
$this->service->delete($category);
|
||||||
@@ -142,7 +143,7 @@ final class CategoryServiceTest extends TestCase
|
|||||||
{
|
{
|
||||||
$category = new Category(5, 'PHP', 'php');
|
$category = new Category(5, 'PHP', 'php');
|
||||||
|
|
||||||
$this->repository->method('hasPost')->with(5)->willReturn(true);
|
$this->repository->expects($this->once())->method('hasPost')->with(5)->willReturn(true);
|
||||||
$this->repository->expects($this->never())->method('delete');
|
$this->repository->expects($this->never())->method('delete');
|
||||||
|
|
||||||
$this->expectException(\InvalidArgumentException::class);
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
@@ -181,7 +182,7 @@ final class CategoryServiceTest extends TestCase
|
|||||||
public function testFindBySlugReturnsCategoryWhenFound(): void
|
public function testFindBySlugReturnsCategoryWhenFound(): void
|
||||||
{
|
{
|
||||||
$cat = new Category(3, 'PHP', 'php');
|
$cat = new Category(3, 'PHP', 'php');
|
||||||
$this->repository->method('findBySlug')->with('php')->willReturn($cat);
|
$this->repository->expects($this->once())->method('findBySlug')->with('php')->willReturn($cat);
|
||||||
|
|
||||||
$this->assertSame($cat, $this->service->findBySlug('php'));
|
$this->assertSame($cat, $this->service->findBySlug('php'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use Slim\Psr7\Response as SlimResponse;
|
|||||||
* Chaque test de contrôleur invoque directement l'action (méthode publique)
|
* Chaque test de contrôleur invoque directement l'action (méthode publique)
|
||||||
* sans passer par le routeur Slim — les middlewares sont testés séparément.
|
* sans passer par le routeur Slim — les middlewares sont testés séparément.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
abstract class ControllerTestCase extends TestCase
|
abstract class ControllerTestCase extends TestCase
|
||||||
{
|
{
|
||||||
// ── Factories ────────────────────────────────────────────────────
|
// ── Factories ────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ use Tests\ControllerTestCase;
|
|||||||
* - upload : absence de fichier, erreur PSR-7, exceptions métier (taille, MIME, stockage), succès
|
* - upload : absence de fichier, erreur PSR-7, exceptions métier (taille, MIME, stockage), succès
|
||||||
* - delete : introuvable, non-propriétaire, succès propriétaire, succès admin
|
* - delete : introuvable, non-propriétaire, succès propriétaire, succès admin
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class MediaControllerTest extends ControllerTestCase
|
final class MediaControllerTest extends ControllerTestCase
|
||||||
{
|
{
|
||||||
/** @var \Slim\Views\Twig&MockObject */
|
/** @var \Slim\Views\Twig&MockObject */
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use App\Media\Media;
|
|||||||
use DateTime;
|
use DateTime;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class MediaModelTest extends TestCase
|
final class MediaModelTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testConstructAndGettersExposeMediaData(): void
|
public function testConstructAndGettersExposeMediaData(): void
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* PDO et PDOStatement sont mockés pour isoler complètement
|
* PDO et PDOStatement sont mockés pour isoler complètement
|
||||||
* le dépôt de la base de données.
|
* le dépôt de la base de données.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class MediaRepositoryTest extends TestCase
|
final class MediaRepositoryTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var PDO&MockObject */
|
/** @var PDO&MockObject */
|
||||||
@@ -258,7 +259,7 @@ final class MediaRepositoryTest extends TestCase
|
|||||||
$media = Media::fromArray($this->rowImage);
|
$media = Media::fromArray($this->rowImage);
|
||||||
$stmt = $this->stmtForWrite();
|
$stmt = $this->stmtForWrite();
|
||||||
|
|
||||||
$this->db->method('prepare')
|
$this->db->expects($this->once())->method('prepare')
|
||||||
->with($this->stringContains('INSERT INTO media'))
|
->with($this->stringContains('INSERT INTO media'))
|
||||||
->willReturn($stmt);
|
->willReturn($stmt);
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ use PHPUnit\Framework\TestCase;
|
|||||||
use Psr\Http\Message\StreamInterface;
|
use Psr\Http\Message\StreamInterface;
|
||||||
use Psr\Http\Message\UploadedFileInterface;
|
use Psr\Http\Message\UploadedFileInterface;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class MediaServiceDuplicateAfterInsertRaceTest extends TestCase
|
final class MediaServiceDuplicateAfterInsertRaceTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var MediaRepositoryInterface&MockObject */
|
/** @var MediaRepositoryInterface&MockObject */
|
||||||
@@ -67,7 +69,7 @@ final class MediaServiceDuplicateAfterInsertRaceTest extends TestCase
|
|||||||
private function makeUploadedFileFromPath(string $path, int $size): UploadedFileInterface
|
private function makeUploadedFileFromPath(string $path, int $size): UploadedFileInterface
|
||||||
{
|
{
|
||||||
$stream = $this->createMock(StreamInterface::class);
|
$stream = $this->createMock(StreamInterface::class);
|
||||||
$stream->method('getMetadata')->with('uri')->willReturn($path);
|
$stream->expects($this->once())->method('getMetadata')->with('uri')->willReturn($path);
|
||||||
|
|
||||||
$file = $this->createMock(UploadedFileInterface::class);
|
$file = $this->createMock(UploadedFileInterface::class);
|
||||||
$file->method('getSize')->willReturn($size);
|
$file->method('getSize')->willReturn($size);
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ use PHPUnit\Framework\TestCase;
|
|||||||
use Psr\Http\Message\UploadedFileInterface;
|
use Psr\Http\Message\UploadedFileInterface;
|
||||||
use Psr\Http\Message\StreamInterface;
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class MediaServiceEdgeCasesTest extends TestCase
|
final class MediaServiceEdgeCasesTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testRejectsWhenSizeUnknown(): void
|
public function testRejectsWhenSizeUnknown(): void
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ use PHPUnit\Framework\TestCase;
|
|||||||
use Psr\Http\Message\UploadedFileInterface;
|
use Psr\Http\Message\UploadedFileInterface;
|
||||||
use Psr\Http\Message\StreamInterface;
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class MediaServiceInvalidMimeTest extends TestCase
|
final class MediaServiceInvalidMimeTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testRejectsNonImageContentEvenWithImageLikeFilename(): void
|
public function testRejectsNonImageContentEvenWithImageLikeFilename(): void
|
||||||
@@ -21,7 +23,7 @@ final class MediaServiceInvalidMimeTest extends TestCase
|
|||||||
file_put_contents($tmpFile, 'not an image');
|
file_put_contents($tmpFile, 'not an image');
|
||||||
|
|
||||||
$stream = $this->createMock(StreamInterface::class);
|
$stream = $this->createMock(StreamInterface::class);
|
||||||
$stream->method('getMetadata')->with('uri')->willReturn($tmpFile);
|
$stream->expects($this->once())->method('getMetadata')->with('uri')->willReturn($tmpFile);
|
||||||
|
|
||||||
$file = $this->createMock(UploadedFileInterface::class);
|
$file = $this->createMock(UploadedFileInterface::class);
|
||||||
$file->method('getSize')->willReturn(filesize($tmpFile));
|
$file->method('getSize')->willReturn(filesize($tmpFile));
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ use PHPUnit\Framework\TestCase;
|
|||||||
use Psr\Http\Message\StreamInterface;
|
use Psr\Http\Message\StreamInterface;
|
||||||
use Psr\Http\Message\UploadedFileInterface;
|
use Psr\Http\Message\UploadedFileInterface;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class MediaServiceInvalidTempPathTest extends TestCase
|
final class MediaServiceInvalidTempPathTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testRejectsWhenTemporaryPathIsMissing(): void
|
public function testRejectsWhenTemporaryPathIsMissing(): void
|
||||||
@@ -17,7 +19,7 @@ final class MediaServiceInvalidTempPathTest extends TestCase
|
|||||||
$repository = $this->createMock(MediaRepositoryInterface::class);
|
$repository = $this->createMock(MediaRepositoryInterface::class);
|
||||||
|
|
||||||
$stream = $this->createMock(StreamInterface::class);
|
$stream = $this->createMock(StreamInterface::class);
|
||||||
$stream->method('getMetadata')->with('uri')->willReturn(null);
|
$stream->expects($this->once())->method('getMetadata')->with('uri')->willReturn(null);
|
||||||
|
|
||||||
$file = $this->createMock(UploadedFileInterface::class);
|
$file = $this->createMock(UploadedFileInterface::class);
|
||||||
$file->method('getSize')->willReturn(128);
|
$file->method('getSize')->willReturn(128);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ use Psr\Http\Message\UploadedFileInterface;
|
|||||||
* - stockage : fichier écrit sur disque, media créé en base
|
* - stockage : fichier écrit sur disque, media créé en base
|
||||||
* - suppression : fichier supprimé du disque et entrée retirée de la base
|
* - suppression : fichier supprimé du disque et entrée retirée de la base
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class MediaServiceTest extends TestCase
|
final class MediaServiceTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var MediaRepositoryInterface&MockObject */
|
/** @var MediaRepositoryInterface&MockObject */
|
||||||
@@ -196,7 +197,7 @@ final class MediaServiceTest extends TestCase
|
|||||||
public function testFindByIdReturnsMedia(): void
|
public function testFindByIdReturnsMedia(): void
|
||||||
{
|
{
|
||||||
$media = new Media(3, 'photo.jpg', '/media/photo.jpg', 'abc123', 1);
|
$media = new Media(3, 'photo.jpg', '/media/photo.jpg', 'abc123', 1);
|
||||||
$this->repository->method('findById')->with(3)->willReturn($media);
|
$this->repository->expects($this->once())->method('findById')->with(3)->willReturn($media);
|
||||||
|
|
||||||
$this->assertSame($media, $this->service->findById(3));
|
$this->assertSame($media, $this->service->findById(3));
|
||||||
}
|
}
|
||||||
@@ -210,7 +211,7 @@ final class MediaServiceTest extends TestCase
|
|||||||
private function makeUploadedFile(int $size): UploadedFileInterface
|
private function makeUploadedFile(int $size): UploadedFileInterface
|
||||||
{
|
{
|
||||||
$stream = $this->createMock(StreamInterface::class);
|
$stream = $this->createMock(StreamInterface::class);
|
||||||
$stream->method('getMetadata')->with('uri')->willReturn('/nonexistent/path');
|
$stream->method('getMetadata')->willReturnMap([['uri', '/nonexistent/path']]);
|
||||||
|
|
||||||
$file = $this->createMock(UploadedFileInterface::class);
|
$file = $this->createMock(UploadedFileInterface::class);
|
||||||
$file->method('getSize')->willReturn($size);
|
$file->method('getSize')->willReturn($size);
|
||||||
@@ -226,7 +227,7 @@ final class MediaServiceTest extends TestCase
|
|||||||
private function makeUploadedFileFromPath(string $path, int $size): UploadedFileInterface
|
private function makeUploadedFileFromPath(string $path, int $size): UploadedFileInterface
|
||||||
{
|
{
|
||||||
$stream = $this->createMock(StreamInterface::class);
|
$stream = $this->createMock(StreamInterface::class);
|
||||||
$stream->method('getMetadata')->with('uri')->willReturn($path);
|
$stream->method('getMetadata')->willReturnMap([['uri', $path]]);
|
||||||
|
|
||||||
$file = $this->createMock(UploadedFileInterface::class);
|
$file = $this->createMock(UploadedFileInterface::class);
|
||||||
$file->method('getSize')->willReturn($size);
|
$file->method('getSize')->willReturn($size);
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ use App\Shared\Html\HtmlSanitizerInterface;
|
|||||||
use PDO;
|
use PDO;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class PostConcurrentUpdateIntegrationTest extends TestCase
|
final class PostConcurrentUpdateIntegrationTest extends TestCase
|
||||||
{
|
{
|
||||||
private PDO $db;
|
private PDO $db;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ use Tests\ControllerTestCase;
|
|||||||
* - update() : 404, droits insuffisants, succès, erreur de validation
|
* - update() : 404, droits insuffisants, succès, erreur de validation
|
||||||
* - delete() : 404, droits insuffisants, succès
|
* - delete() : 404, droits insuffisants, succès
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class PostControllerTest extends ControllerTestCase
|
final class PostControllerTest extends ControllerTestCase
|
||||||
{
|
{
|
||||||
/** @var \Slim\Views\Twig&MockObject */
|
/** @var \Slim\Views\Twig&MockObject */
|
||||||
@@ -100,7 +101,7 @@ final class PostControllerTest extends ControllerTestCase
|
|||||||
public function testIndexFiltersByCategoryWhenSlugProvided(): void
|
public function testIndexFiltersByCategoryWhenSlugProvided(): void
|
||||||
{
|
{
|
||||||
$category = new Category(3, 'PHP', 'php');
|
$category = new Category(3, 'PHP', 'php');
|
||||||
$this->categoryService->method('findBySlug')->with('php')->willReturn($category);
|
$this->categoryService->expects($this->once())->method('findBySlug')->with('php')->willReturn($category);
|
||||||
|
|
||||||
$this->postService->expects($this->once())
|
$this->postService->expects($this->once())
|
||||||
->method('getAllPosts')
|
->method('getAllPosts')
|
||||||
@@ -233,7 +234,7 @@ final class PostControllerTest extends ControllerTestCase
|
|||||||
public function testFormRendersFilledFormWhenUserIsAuthor(): void
|
public function testFormRendersFilledFormWhenUserIsAuthor(): void
|
||||||
{
|
{
|
||||||
$post = $this->buildPostEntity(7, 'Titre', 'Contenu', 'titre', 5);
|
$post = $this->buildPostEntity(7, 'Titre', 'Contenu', 'titre', 5);
|
||||||
$this->postService->method('getPostById')->with(7)->willReturn($post);
|
$this->postService->expects($this->once())->method('getPostById')->with(7)->willReturn($post);
|
||||||
$this->sessionManager->method('isAdmin')->willReturn(false);
|
$this->sessionManager->method('isAdmin')->willReturn(false);
|
||||||
$this->sessionManager->method('isEditor')->willReturn(false);
|
$this->sessionManager->method('isEditor')->willReturn(false);
|
||||||
$this->sessionManager->method('getUserId')->willReturn(5);
|
$this->sessionManager->method('getUserId')->willReturn(5);
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ use App\Post\PostExtension;
|
|||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class PostExtensionTest extends TestCase
|
final class PostExtensionTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var array<string, TwigFunction> */
|
/** @var array<string, TwigFunction> */
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ use App\Shared\Database\Migrator;
|
|||||||
use PDO;
|
use PDO;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class PostFtsUsernameSyncIntegrationTest extends TestCase
|
final class PostFtsUsernameSyncIntegrationTest extends TestCase
|
||||||
{
|
{
|
||||||
private PDO $db;
|
private PDO $db;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ namespace Tests\Post;
|
|||||||
use App\Post\Post;
|
use App\Post\Post;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class PostModelEdgeCasesTest extends TestCase
|
final class PostModelEdgeCasesTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testFromArrayKeepsMissingOptionalFieldsNull(): void
|
public function testFromArrayKeepsMissingOptionalFieldsNull(): void
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use App\Post\Post;
|
|||||||
use DateTime;
|
use DateTime;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class PostModelTest extends TestCase
|
final class PostModelTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testConstructAndGettersExposePostData(): void
|
public function testConstructAndGettersExposePostData(): void
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* PDO et PDOStatement sont mockés pour isoler complètement
|
* PDO et PDOStatement sont mockés pour isoler complètement
|
||||||
* le dépôt de la base de données.
|
* le dépôt de la base de données.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class PostRepositoryTest extends TestCase
|
final class PostRepositoryTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var PDO&MockObject */
|
/** @var PDO&MockObject */
|
||||||
@@ -339,7 +340,7 @@ final class PostRepositoryTest extends TestCase
|
|||||||
$post = Post::fromArray($this->rowPost);
|
$post = Post::fromArray($this->rowPost);
|
||||||
$stmt = $this->stmtForWrite();
|
$stmt = $this->stmtForWrite();
|
||||||
|
|
||||||
$this->db->method('prepare')
|
$this->db->expects($this->once())->method('prepare')
|
||||||
->with($this->stringContains('INSERT INTO posts'))
|
->with($this->stringContains('INSERT INTO posts'))
|
||||||
->willReturn($stmt);
|
->willReturn($stmt);
|
||||||
|
|
||||||
@@ -403,7 +404,7 @@ final class PostRepositoryTest extends TestCase
|
|||||||
$post = Post::fromArray($this->rowPost);
|
$post = Post::fromArray($this->rowPost);
|
||||||
$stmt = $this->stmtForWrite(1);
|
$stmt = $this->stmtForWrite(1);
|
||||||
|
|
||||||
$this->db->method('prepare')
|
$this->db->expects($this->once())->method('prepare')
|
||||||
->with($this->stringContains('UPDATE posts'))
|
->with($this->stringContains('UPDATE posts'))
|
||||||
->willReturn($stmt);
|
->willReturn($stmt);
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* Couvre la création, la mise à jour, la suppression et les lectures.
|
* Couvre la création, la mise à jour, la suppression et les lectures.
|
||||||
* HtmlSanitizerInterface et PostRepository sont mockés pour isoler la logique métier.
|
* HtmlSanitizerInterface et PostRepository sont mockés pour isoler la logique métier.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class PostServiceTest extends TestCase
|
final class PostServiceTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var PostRepositoryInterface&MockObject */
|
/** @var PostRepositoryInterface&MockObject */
|
||||||
@@ -65,7 +66,7 @@ final class PostServiceTest extends TestCase
|
|||||||
public function testGetPostsByUserIdDelegatesToRepository(): void
|
public function testGetPostsByUserIdDelegatesToRepository(): void
|
||||||
{
|
{
|
||||||
$posts = [$this->makePost(1, 'Titre', 'slug-titre')];
|
$posts = [$this->makePost(1, 'Titre', 'slug-titre')];
|
||||||
$this->repository->method('findByUserId')->with(3, null)->willReturn($posts);
|
$this->repository->expects($this->once())->method('findByUserId')->with(3, null)->willReturn($posts);
|
||||||
|
|
||||||
$this->assertSame($posts, $this->service->getPostsByUserId(3));
|
$this->assertSame($posts, $this->service->getPostsByUserId(3));
|
||||||
}
|
}
|
||||||
@@ -181,7 +182,7 @@ final class PostServiceTest extends TestCase
|
|||||||
public function testSearchPostsDelegatesToRepository(): void
|
public function testSearchPostsDelegatesToRepository(): void
|
||||||
{
|
{
|
||||||
$posts = [$this->makePost(1, 'Résultat', 'resultat')];
|
$posts = [$this->makePost(1, 'Résultat', 'resultat')];
|
||||||
$this->repository->method('search')->with('mot', null, null)->willReturn($posts);
|
$this->repository->expects($this->once())->method('search')->with('mot', null, null)->willReturn($posts);
|
||||||
|
|
||||||
$this->assertSame($posts, $this->service->searchPosts('mot'));
|
$this->assertSame($posts, $this->service->searchPosts('mot'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use Tests\ControllerTestCase;
|
|||||||
* - Flux vide : XML minimal valide
|
* - Flux vide : XML minimal valide
|
||||||
* - Appel à getRecentPosts() avec la constante FEED_LIMIT (20)
|
* - Appel à getRecentPosts() avec la constante FEED_LIMIT (20)
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class RssControllerTest extends ControllerTestCase
|
final class RssControllerTest extends ControllerTestCase
|
||||||
{
|
{
|
||||||
/** @var PostServiceInterface&MockObject */
|
/** @var PostServiceInterface&MockObject */
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use App\Shared\Http\ClientIpResolver;
|
|||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Slim\Psr7\Factory\ServerRequestFactory;
|
use Slim\Psr7\Factory\ServerRequestFactory;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class ClientIpResolverTest extends TestCase
|
final class ClientIpResolverTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testResolveReturnsDefaultWhenRemoteAddrMissing(): void
|
public function testResolveReturnsDefaultWhenRemoteAddrMissing(): void
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ namespace Tests\Shared;
|
|||||||
use App\Shared\Config;
|
use App\Shared\Config;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class ConfigTest extends TestCase
|
final class ConfigTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testGetTwigCacheReturnsFalseInDev(): void
|
public function testGetTwigCacheReturnsFalseInDev(): void
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* Couvre la conversion de valeurs brutes de base de données en DateTime,
|
* Couvre la conversion de valeurs brutes de base de données en DateTime,
|
||||||
* ainsi que les cas silencieux (null, chaîne vide, valeur invalide).
|
* ainsi que les cas silencieux (null, chaîne vide, valeur invalide).
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class DateParserTest extends TestCase
|
final class DateParserTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ use PHPUnit\Framework\TestCase;
|
|||||||
use Slim\Csrf\Guard;
|
use Slim\Csrf\Guard;
|
||||||
use Slim\Psr7\Factory\ResponseFactory;
|
use Slim\Psr7\Factory\ResponseFactory;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class ExtensionTest extends TestCase
|
final class ExtensionTest extends TestCase
|
||||||
{
|
{
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ namespace Tests\Shared;
|
|||||||
use App\Shared\Http\FlashService;
|
use App\Shared\Http\FlashService;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class FlashServiceConsumeTest extends TestCase
|
final class FlashServiceConsumeTest extends TestCase
|
||||||
{
|
{
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ namespace Tests\Shared;
|
|||||||
use App\Shared\Http\FlashService;
|
use App\Shared\Http\FlashService;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class FlashServiceTest extends TestCase
|
final class FlashServiceTest extends TestCase
|
||||||
{
|
{
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use App\Shared\Http\ClientIpResolver;
|
|||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Slim\Psr7\Factory\ServerRequestFactory;
|
use Slim\Psr7\Factory\ServerRequestFactory;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class HelperEdgeCasesTest extends TestCase
|
final class HelperEdgeCasesTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testClientIpResolverFallsBackToRemoteAddr(): void
|
public function testClientIpResolverFallsBackToRemoteAddr(): void
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ namespace Tests\Shared;
|
|||||||
use App\Shared\Html\HtmlPurifierFactory;
|
use App\Shared\Html\HtmlPurifierFactory;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class HtmlPurifierFactoryTest extends TestCase
|
final class HtmlPurifierFactoryTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testCreateBuildsPurifierAndSanitizesDangerousHtml(): void
|
public function testCreateBuildsPurifierAndSanitizesDangerousHtml(): void
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* Ces tests utilisent une vraie instance HTMLPurifier (pas de mock)
|
* Ces tests utilisent une vraie instance HTMLPurifier (pas de mock)
|
||||||
* car c'est le comportement de purification lui-même qui est testé.
|
* car c'est le comportement de purification lui-même qui est testé.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class HtmlSanitizerTest extends TestCase
|
final class HtmlSanitizerTest extends TestCase
|
||||||
{
|
{
|
||||||
private HtmlSanitizer $sanitizer;
|
private HtmlSanitizer $sanitizer;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* syncFtsIndex() requiert les tables posts, users et posts_fts — elles sont
|
* syncFtsIndex() requiert les tables posts, users et posts_fts — elles sont
|
||||||
* créées minimalement avant chaque test qui en a besoin.
|
* créées minimalement avant chaque test qui en a besoin.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class MigratorTest extends TestCase
|
final class MigratorTest extends TestCase
|
||||||
{
|
{
|
||||||
private PDO $db;
|
private PDO $db;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* PDO et PDOStatement sont mockés pour isoler le Seeder de la base de données.
|
* PDO et PDOStatement sont mockés pour isoler le Seeder de la base de données.
|
||||||
* Les variables d'environnement sont définies dans setUp() et restaurées dans tearDown().
|
* Les variables d'environnement sont définies dans setUp() et restaurées dans tearDown().
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class SeederTest extends TestCase
|
final class SeederTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var PDO&MockObject */
|
/** @var PDO&MockObject */
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ namespace Tests\Shared;
|
|||||||
use App\Shared\Http\SessionManager;
|
use App\Shared\Http\SessionManager;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
|
|
||||||
final class SessionManagerEdgeCasesTest extends TestCase
|
final class SessionManagerEdgeCasesTest extends TestCase
|
||||||
{
|
{
|
||||||
private SessionManager $manager;
|
private SessionManager $manager;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* session_status() === PHP_SESSION_ACTIVE dans SessionManager, ce qui les rend
|
* session_status() === PHP_SESSION_ACTIVE dans SessionManager, ce qui les rend
|
||||||
* sans effet en contexte CLI et évite toute notice PHP.
|
* sans effet en contexte CLI et évite toute notice PHP.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class SessionManagerTest extends TestCase
|
final class SessionManagerTest extends TestCase
|
||||||
{
|
{
|
||||||
private SessionManager $manager;
|
private SessionManager $manager;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* Couvre la translittération ASCII, la normalisation en minuscules,
|
* Couvre la translittération ASCII, la normalisation en minuscules,
|
||||||
* le remplacement des caractères non alphanumériques, et les cas limites.
|
* le remplacement des caractères non alphanumériques, et les cas limites.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class SlugHelperTest extends TestCase
|
final class SlugHelperTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ use Tests\ControllerTestCase;
|
|||||||
* - updateRole() : introuvable, propre rôle, cible admin, rôle invalide, succès
|
* - updateRole() : introuvable, propre rôle, cible admin, rôle invalide, succès
|
||||||
* - delete() : introuvable, cible admin, soi-même, succès
|
* - delete() : introuvable, cible admin, soi-même, succès
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class UserControllerTest extends ControllerTestCase
|
final class UserControllerTest extends ControllerTestCase
|
||||||
{
|
{
|
||||||
/** @var \Slim\Views\Twig&MockObject */
|
/** @var \Slim\Views\Twig&MockObject */
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* PDO et PDOStatement sont mockés pour isoler complètement
|
* PDO et PDOStatement sont mockés pour isoler complètement
|
||||||
* le dépôt de la base de données.
|
* le dépôt de la base de données.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class UserRepositoryTest extends TestCase
|
final class UserRepositoryTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var PDO&MockObject */
|
/** @var PDO&MockObject */
|
||||||
@@ -258,7 +259,7 @@ final class UserRepositoryTest extends TestCase
|
|||||||
$user = User::fromArray($this->rowAlice);
|
$user = User::fromArray($this->rowAlice);
|
||||||
$stmt = $this->stmtForWrite();
|
$stmt = $this->stmtForWrite();
|
||||||
|
|
||||||
$this->db->method('prepare')
|
$this->db->expects($this->once())->method('prepare')
|
||||||
->with($this->stringContains('INSERT INTO users'))
|
->with($this->stringContains('INSERT INTO users'))
|
||||||
->willReturn($stmt);
|
->willReturn($stmt);
|
||||||
|
|
||||||
@@ -301,7 +302,7 @@ final class UserRepositoryTest extends TestCase
|
|||||||
$newHash = password_hash('nouveaumdp', PASSWORD_BCRYPT);
|
$newHash = password_hash('nouveaumdp', PASSWORD_BCRYPT);
|
||||||
$stmt = $this->stmtForWrite();
|
$stmt = $this->stmtForWrite();
|
||||||
|
|
||||||
$this->db->method('prepare')
|
$this->db->expects($this->once())->method('prepare')
|
||||||
->with($this->stringContains('UPDATE users'))
|
->with($this->stringContains('UPDATE users'))
|
||||||
->willReturn($stmt);
|
->willReturn($stmt);
|
||||||
|
|
||||||
@@ -322,7 +323,7 @@ final class UserRepositoryTest extends TestCase
|
|||||||
{
|
{
|
||||||
$stmt = $this->stmtForWrite();
|
$stmt = $this->stmtForWrite();
|
||||||
|
|
||||||
$this->db->method('prepare')
|
$this->db->expects($this->once())->method('prepare')
|
||||||
->with($this->stringContains('UPDATE users'))
|
->with($this->stringContains('UPDATE users'))
|
||||||
->willReturn($stmt);
|
->willReturn($stmt);
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* Les dépendances sont remplacées par des mocks via leurs interfaces pour
|
* Les dépendances sont remplacées par des mocks via leurs interfaces pour
|
||||||
* isoler le service.
|
* isoler le service.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class UserServiceTest extends TestCase
|
final class UserServiceTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var UserRepositoryInterface&MockObject */
|
/** @var UserRepositoryInterface&MockObject */
|
||||||
@@ -186,7 +187,7 @@ final class UserServiceTest extends TestCase
|
|||||||
public function testFindByIdReturnsUser(): void
|
public function testFindByIdReturnsUser(): void
|
||||||
{
|
{
|
||||||
$user = $this->makeUser('alice', 'alice@example.com');
|
$user = $this->makeUser('alice', 'alice@example.com');
|
||||||
$this->userRepository->method('findById')->with(1)->willReturn($user);
|
$this->userRepository->expects($this->once())->method('findById')->with(1)->willReturn($user);
|
||||||
|
|
||||||
$this->assertSame($user, $this->service->findById(1));
|
$this->assertSame($user, $this->service->findById(1));
|
||||||
}
|
}
|
||||||
@@ -199,7 +200,7 @@ final class UserServiceTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testDeleteDelegatesToRepository(): void
|
public function testDeleteDelegatesToRepository(): void
|
||||||
{
|
{
|
||||||
$this->userRepository->method('findById')->with(5)->willReturn($this->makeUser('alice', 'alice@example.com'));
|
$this->userRepository->expects($this->once())->method('findById')->with(5)->willReturn($this->makeUser('alice', 'alice@example.com'));
|
||||||
$this->userRepository->expects($this->once())->method('delete')->with(5);
|
$this->userRepository->expects($this->once())->method('delete')->with(5);
|
||||||
|
|
||||||
$this->service->delete(5);
|
$this->service->delete(5);
|
||||||
@@ -213,7 +214,7 @@ final class UserServiceTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testUpdateRoleDelegatesToRepository(): void
|
public function testUpdateRoleDelegatesToRepository(): void
|
||||||
{
|
{
|
||||||
$this->userRepository->method('findById')->with(3)->willReturn($this->makeUser('alice', 'alice@example.com'));
|
$this->userRepository->expects($this->once())->method('findById')->with(3)->willReturn($this->makeUser('alice', 'alice@example.com'));
|
||||||
$this->userRepository->expects($this->once())
|
$this->userRepository->expects($this->once())
|
||||||
->method('updateRole')
|
->method('updateRole')
|
||||||
->with(3, User::ROLE_EDITOR);
|
->with(3, User::ROLE_EDITOR);
|
||||||
@@ -239,7 +240,7 @@ final class UserServiceTest extends TestCase
|
|||||||
#[\PHPUnit\Framework\Attributes\DataProvider('validRolesProvider')]
|
#[\PHPUnit\Framework\Attributes\DataProvider('validRolesProvider')]
|
||||||
public function testUpdateRoleAcceptsAllValidRoles(string $role): void
|
public function testUpdateRoleAcceptsAllValidRoles(string $role): void
|
||||||
{
|
{
|
||||||
$this->userRepository->method('findById')->with(1)->willReturn($this->makeUser('alice', 'alice@example.com'));
|
$this->userRepository->expects($this->once())->method('findById')->with(1)->willReturn($this->makeUser('alice', 'alice@example.com'));
|
||||||
$this->userRepository->expects($this->once())->method('updateRole')->with(1, $role);
|
$this->userRepository->expects($this->once())->method('updateRole')->with(1, $role);
|
||||||
|
|
||||||
$this->service->updateRole(1, $role);
|
$this->service->updateRole(1, $role);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
* Vérifie la construction, la validation, les accesseurs
|
* Vérifie la construction, la validation, les accesseurs
|
||||||
* et l'hydratation depuis un tableau de base de données.
|
* et l'hydratation depuis un tableau de base de données.
|
||||||
*/
|
*/
|
||||||
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||||
final class UserTest extends TestCase
|
final class UserTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user