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('findPaginated')->willReturn(new PaginatedResult([], 0, 1, 20)); $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'], ); } }