first commit
This commit is contained in:
203
tests/Taxonomy/TaxonomyServiceTest.php
Normal file
203
tests/Taxonomy/TaxonomyServiceTest.php
Normal file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Taxonomy;
|
||||
|
||||
use Netig\Netslim\Taxonomy\Application\TaxonomyApplicationService;
|
||||
use Netig\Netslim\Taxonomy\Application\UseCase\CreateTaxon;
|
||||
use Netig\Netslim\Taxonomy\Application\UseCase\DeleteTaxon;
|
||||
use Netig\Netslim\Taxonomy\Contracts\TaxonUsageCheckerInterface;
|
||||
use Netig\Netslim\Taxonomy\Domain\Entity\Taxon;
|
||||
use Netig\Netslim\Taxonomy\Domain\Repository\TaxonRepositoryInterface;
|
||||
use Netig\Netslim\Taxonomy\Domain\Service\TaxonSlugGenerator;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Tests unitaires pour TaxonomyApplicationService.
|
||||
*
|
||||
* Vérifie la création (génération de slug, unicité du nom, validation du modèle)
|
||||
* et la suppression (blocage si le terme est encore utilisé).
|
||||
* Le repository est remplacé par un mock pour isoler le service.
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
||||
final class TaxonomyServiceTest extends TestCase
|
||||
{
|
||||
/** @var TaxonRepositoryInterface&MockObject */
|
||||
private TaxonRepositoryInterface $repository;
|
||||
|
||||
/** @var TaxonUsageCheckerInterface&MockObject */
|
||||
private TaxonUsageCheckerInterface $taxonUsageChecker;
|
||||
|
||||
private TaxonomyApplicationService $service;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->repository = $this->createMock(TaxonRepositoryInterface::class);
|
||||
$this->taxonUsageChecker = $this->createMock(TaxonUsageCheckerInterface::class);
|
||||
$this->service = new TaxonomyApplicationService(
|
||||
$this->repository,
|
||||
new CreateTaxon($this->repository, new TaxonSlugGenerator()),
|
||||
new DeleteTaxon($this->repository, $this->taxonUsageChecker),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// ── create ─────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* create() doit générer le slug depuis le nom et persister le terme.
|
||||
*/
|
||||
public function testCreateGeneratesSlugAndPersists(): void
|
||||
{
|
||||
$this->repository->method('nameExists')->willReturn(false);
|
||||
$this->repository->expects($this->once())
|
||||
->method('create')
|
||||
->with($this->callback(
|
||||
fn (Taxon $c) =>
|
||||
$c->getName() === 'Développement web'
|
||||
&& $c->getSlug() === 'developpement-web',
|
||||
))
|
||||
->willReturn(1);
|
||||
|
||||
$id = $this->service->create('Développement web');
|
||||
|
||||
$this->assertSame(1, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* create() doit trimmer le nom avant de générer le slug.
|
||||
*/
|
||||
public function testCreateTrimsName(): void
|
||||
{
|
||||
$this->repository->method('nameExists')->willReturn(false);
|
||||
$this->repository->expects($this->once())
|
||||
->method('create')
|
||||
->with($this->callback(fn (Taxon $c) => $c->getName() === 'PHP'))
|
||||
->willReturn(2);
|
||||
|
||||
$this->service->create(' PHP ');
|
||||
}
|
||||
|
||||
/**
|
||||
* create() doit lever InvalidArgumentException si le slug généré est vide.
|
||||
*/
|
||||
public function testCreateNonAsciiNameThrowsException(): void
|
||||
{
|
||||
$this->repository->expects($this->never())->method('create');
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('slug URL valide');
|
||||
|
||||
$this->service->create('日本語');
|
||||
}
|
||||
|
||||
/**
|
||||
* create() doit lever InvalidArgumentException si le nom est existe déjà.
|
||||
*/
|
||||
public function testCreateDuplicateNameThrowsException(): void
|
||||
{
|
||||
$this->repository->method('nameExists')->willReturn(true);
|
||||
$this->repository->expects($this->never())->method('create');
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('existe déjà');
|
||||
|
||||
$this->service->create('PHP');
|
||||
}
|
||||
|
||||
/**
|
||||
* create() doit lever InvalidArgumentException si le nom est vide.
|
||||
*/
|
||||
public function testCreateEmptyNameThrowsException(): void
|
||||
{
|
||||
$this->repository->method('nameExists')->willReturn(false);
|
||||
$this->repository->expects($this->never())->method('create');
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
|
||||
$this->service->create('');
|
||||
}
|
||||
|
||||
/**
|
||||
* create() doit lever InvalidArgumentException si le nom dépasse 100 caractères.
|
||||
*/
|
||||
public function testCreateNameTooLongThrowsException(): void
|
||||
{
|
||||
$longName = str_repeat('a', 101);
|
||||
$this->repository->method('nameExists')->willReturn(false);
|
||||
$this->repository->expects($this->never())->method('create');
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
|
||||
$this->service->create($longName);
|
||||
}
|
||||
|
||||
|
||||
// ── delete ─────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* delete() doit supprimer le terme s'il n'est pas utilisé.
|
||||
*/
|
||||
public function testDeleteSucceedsWhenTaxonIsUnused(): void
|
||||
{
|
||||
$taxon = new Taxon(5, 'PHP', 'php');
|
||||
|
||||
$this->taxonUsageChecker->expects($this->once())->method('isTaxonInUse')->with(5)->willReturn(false);
|
||||
$this->repository->expects($this->once())->method('delete')->with(5);
|
||||
|
||||
$this->service->delete($taxon);
|
||||
}
|
||||
|
||||
/**
|
||||
* delete() doit lever InvalidArgumentException si le terme est encore utilisé.
|
||||
*/
|
||||
public function testDeleteBlockedWhenTaxonIsStillUsed(): void
|
||||
{
|
||||
$taxon = new Taxon(5, 'PHP', 'php');
|
||||
|
||||
$this->taxonUsageChecker->expects($this->once())->method('isTaxonInUse')->with(5)->willReturn(true);
|
||||
$this->repository->expects($this->never())->method('delete');
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('est encore utilisé');
|
||||
|
||||
$this->service->delete($taxon);
|
||||
}
|
||||
|
||||
|
||||
// ── Lectures déléguées ─────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* findAll() doit déléguer au repository et retourner son résultat.
|
||||
*/
|
||||
public function testFindAllDelegatesToRepository(): void
|
||||
{
|
||||
$cats = [new Taxon(1, 'PHP', 'php'), new Taxon(2, 'CSS', 'css')];
|
||||
$this->repository->method('findAll')->willReturn($cats);
|
||||
|
||||
$this->assertSame($cats, $this->service->findAll());
|
||||
}
|
||||
|
||||
/**
|
||||
* findById() doit retourner null si le terme n'existe pas.
|
||||
*/
|
||||
public function testFindByIdReturnsNullWhenMissing(): void
|
||||
{
|
||||
$this->repository->method('findById')->willReturn(null);
|
||||
|
||||
$this->assertNull($this->service->findById(99));
|
||||
}
|
||||
|
||||
/**
|
||||
* findBySlug() doit retourner le terme correspondant.
|
||||
*/
|
||||
public function testFindBySlugReturnsTaxonWhenFound(): void
|
||||
{
|
||||
$taxon = new Taxon(3, 'PHP', 'php');
|
||||
$this->repository->expects($this->once())->method('findBySlug')->with('php')->willReturn($taxon);
|
||||
|
||||
$this->assertSame($taxon, $this->service->findBySlug('php'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user