test(catalog) : M7 — tests RG-7.01→7.08 + contrat de sérialisation stockage (ERP-215)
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
<?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],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user