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