first commit

This commit is contained in:
julien
2026-03-16 01:47:07 +01:00
commit 8f7e61bda0
185 changed files with 27731 additions and 0 deletions

View File

@@ -0,0 +1,198 @@
<?php
declare(strict_types=1);
namespace Tests\Category;
use App\Category\Category;
use App\Category\CategoryController;
use App\Category\CategoryServiceInterface;
use App\Shared\Http\FlashServiceInterface;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\ControllerTestCase;
/**
* Tests unitaires pour CategoryController.
*
* Couvre index(), create() et delete() :
* rendu de la liste, création réussie, erreur de création,
* suppression avec catégorie introuvable, succès et erreur métier.
*/
final class CategoryControllerTest extends ControllerTestCase
{
/** @var \Slim\Views\Twig&MockObject */
private \Slim\Views\Twig $view;
/** @var CategoryServiceInterface&MockObject */
private CategoryServiceInterface $categoryService;
/** @var FlashServiceInterface&MockObject */
private FlashServiceInterface $flash;
private CategoryController $controller;
protected function setUp(): void
{
$this->view = $this->makeTwigMock();
$this->categoryService = $this->createMock(CategoryServiceInterface::class);
$this->flash = $this->createMock(FlashServiceInterface::class);
$this->controller = new CategoryController(
$this->view,
$this->categoryService,
$this->flash,
);
}
// ── index ────────────────────────────────────────────────────────
/**
* index() doit rendre la vue avec la liste des catégories.
*/
public function testIndexRendersWithCategories(): void
{
$this->categoryService->method('findAll')->willReturn([]);
$this->view->expects($this->once())
->method('render')
->with($this->anything(), 'admin/categories/index.twig', $this->anything())
->willReturnArgument(0);
$res = $this->controller->index($this->makeGet('/admin/categories'), $this->makeResponse());
$this->assertStatus($res, 200);
}
// ── create ───────────────────────────────────────────────────────
/**
* create() doit flasher un succès et rediriger en cas de création réussie.
*/
public function testCreateRedirectsWithSuccessFlash(): void
{
$this->categoryService->method('create')->willReturn(1);
$this->flash->expects($this->once())->method('set')
->with('category_success', $this->stringContains('PHP'));
$req = $this->makePost('/admin/categories/create', ['name' => 'PHP']);
$res = $this->controller->create($req, $this->makeResponse());
$this->assertRedirectTo($res, '/admin/categories');
}
/**
* create() doit flasher une erreur si le service lève une InvalidArgumentException.
*/
public function testCreateRedirectsWithErrorOnInvalidArgument(): void
{
$this->categoryService->method('create')
->willThrowException(new \InvalidArgumentException('Catégorie déjà existante'));
$this->flash->expects($this->once())->method('set')
->with('category_error', 'Catégorie déjà existante');
$req = $this->makePost('/admin/categories/create', ['name' => 'Duplicate']);
$res = $this->controller->create($req, $this->makeResponse());
$this->assertRedirectTo($res, '/admin/categories');
}
/**
* create() doit flasher une erreur générique pour toute autre exception.
*/
public function testCreateRedirectsWithGenericErrorOnUnexpectedException(): void
{
$this->categoryService->method('create')
->willThrowException(new \RuntimeException('DB error'));
$this->flash->expects($this->once())->method('set')
->with('category_error', $this->stringContains('inattendue'));
$req = $this->makePost('/admin/categories/create', ['name' => 'PHP']);
$res = $this->controller->create($req, $this->makeResponse());
$this->assertRedirectTo($res, '/admin/categories');
}
// ── delete ───────────────────────────────────────────────────────
/**
* delete() doit flasher une erreur et rediriger si la catégorie est introuvable.
*/
public function testDeleteRedirectsWithErrorWhenNotFound(): void
{
$this->categoryService->method('findById')->willReturn(null);
$this->flash->expects($this->once())->method('set')
->with('category_error', 'Catégorie introuvable');
$res = $this->controller->delete(
$this->makePost('/admin/categories/delete/99'),
$this->makeResponse(),
['id' => '99'],
);
$this->assertRedirectTo($res, '/admin/categories');
}
/**
* delete() doit flasher un succès et rediriger en cas de suppression réussie.
*/
public function testDeleteRedirectsWithSuccessFlash(): void
{
$category = new Category(3, 'PHP', 'php');
$this->categoryService->method('findById')->willReturn($category);
$this->flash->expects($this->once())->method('set')
->with('category_success', $this->stringContains('PHP'));
$res = $this->controller->delete(
$this->makePost('/admin/categories/delete/3'),
$this->makeResponse(),
['id' => '3'],
);
$this->assertRedirectTo($res, '/admin/categories');
}
/**
* delete() doit flasher une erreur si le service refuse la suppression
* (ex: des articles sont rattachés à la catégorie).
*/
public function testDeleteRedirectsWithErrorWhenServiceRefuses(): void
{
$category = new Category(3, 'PHP', 'php');
$this->categoryService->method('findById')->willReturn($category);
$this->categoryService->method('delete')
->willThrowException(new \InvalidArgumentException('Des articles utilisent cette catégorie'));
$this->flash->expects($this->once())->method('set')
->with('category_error', 'Des articles utilisent cette catégorie');
$res = $this->controller->delete(
$this->makePost('/admin/categories/delete/3'),
$this->makeResponse(),
['id' => '3'],
);
$this->assertRedirectTo($res, '/admin/categories');
}
/**
* delete() doit passer l'identifiant de route au service findById().
*/
public function testDeletePassesCorrectIdToService(): void
{
$this->categoryService->expects($this->once())
->method('findById')
->with(7)
->willReturn(null);
$this->flash->method('set');
$this->controller->delete(
$this->makePost('/admin/categories/delete/7'),
$this->makeResponse(),
['id' => '7'],
);
}
}