Files
Starseed/tests/Module/Catalog/Api/StorageUniquenessTest.php
T

122 lines
4.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Module\Catalog\Api;
use App\Module\Catalog\Domain\Entity\Storage;
use App\Module\Catalog\Domain\Entity\StorageType;
use App\Module\Sites\Domain\Entity\Site;
use DateTimeImmutable;
use function count;
/**
* RG-7.01 : unicite metier du triplet (site, storageType, numero) parmi les ACTIFS.
* RG-7.08 : le PATCH applique les memes regles que le POST.
*
* Couvre :
* - 409 sur doublon de triplet actif (pre-check deterministe du Processor) ;
* - meme numero accepte sur un AUTRE site, ou sur un AUTRE type (unicite portee
* par le triplet complet, pas le seul numero) ;
* - reutilisation possible d'un triplet porte par un stockage soft-deleted (l'index
* partiel uq_storage_site_type_numero_active ne contraint que les actifs) ;
* - PATCH d'un numero vers un triplet deja pris -> 409 (RG-7.08).
*
* @internal
*/
final class StorageUniquenessTest extends AbstractStorageApiTestCase
{
public function testDuplicateActiveTripletReturns409(): void
{
$site = $this->firstSite();
$type = $this->seedStorageType();
$this->seedStorageEntity('A1', site: $site, storageType: $type);
$client = $this->createAdminClient();
$client->request('POST', '/api/storages', [
'headers' => ['Content-Type' => self::LD],
'json' => $this->tripletPayload($site, $type, 'A1'),
]);
self::assertResponseStatusCodeSame(409);
}
public function testSameNumeroOnAnotherTypeIsAccepted(): void
{
$site = $this->firstSite();
$typeA = $this->seedStorageType();
$typeB = $this->seedStorageType();
$this->seedStorageEntity('A1', site: $site, storageType: $typeA);
$client = $this->createAdminClient();
$client->request('POST', '/api/storages', [
'headers' => ['Content-Type' => self::LD],
'json' => $this->tripletPayload($site, $typeB, 'A1'),
]);
self::assertResponseStatusCodeSame(201);
}
public function testSameNumeroOnAnotherSiteIsAccepted(): void
{
$sites = $this->getEm()->getRepository(Site::class)->findAll();
if (count($sites) < 2) {
self::markTestSkipped('Au moins 2 sites fixtures requis pour ce cas.');
}
$type = $this->seedStorageType();
$this->seedStorageEntity('A1', site: $sites[0], storageType: $type);
$client = $this->createAdminClient();
$client->request('POST', '/api/storages', [
'headers' => ['Content-Type' => self::LD],
'json' => $this->tripletPayload($sites[1], $type, 'A1'),
]);
self::assertResponseStatusCodeSame(201);
}
public function testSoftDeletedTripletCanBeReused(): void
{
$site = $this->firstSite();
$type = $this->seedStorageType();
$this->seedStorageEntity('B2', deletedAt: new DateTimeImmutable(), site: $site, storageType: $type);
$client = $this->createAdminClient();
$client->request('POST', '/api/storages', [
'headers' => ['Content-Type' => self::LD],
'json' => $this->tripletPayload($site, $type, 'B2'),
]);
self::assertResponseStatusCodeSame(201);
}
public function testPatchToExistingTripletReturns409(): void
{
$site = $this->firstSite();
$type = $this->seedStorageType();
$this->seedStorageEntity('A1', site: $site, storageType: $type);
$target = $this->seedStorageEntity('B2', site: $site, storageType: $type);
// RG-7.08 : PATCH du numero B2 -> A1 (meme site+type) collisionne -> 409.
$client = $this->createAdminClient();
$client->request('PATCH', '/api/storages/'.$target->getId(), [
'headers' => ['Content-Type' => self::MERGE],
'json' => ['numero' => 'A1'],
]);
self::assertResponseStatusCodeSame(409);
}
/**
* Payload POST minimal pour un triplet (site, type, numero) donne.
*
* @return array<string, mixed>
*/
private function tripletPayload(Site $site, StorageType $type, string $numero): array
{
return [
'site' => $this->iri('sites', (int) $site->getId()),
'storageType' => $this->iri('storage_types', (int) $type->getId()),
'numero' => $numero,
'states' => [Storage::STATE_RECEPTION],
];
}
}