e455204bbd
- WeighbridgeReaderInterface (contrat) + RandomWeighbridgeReader (stub,
poids aléatoire ∈ [10000,50000] kg, RG-5.06) + WeighbridgeUnavailableException
- DsdAllocator : compteur DSD par site (weighbridge_dsd_counter) incrémenté
sous verrou ligne SELECT ... FOR UPDATE (RG-5.04, § 2.7)
- endpoint POST /api/weighbridge_readings : ressource virtuelle
WeighbridgeReadingResource + WeighbridgeReadingProcessor (pas de controller)
- AUTO -> {weight, dsd, mode} ; MANUAL -> {weight, dsd, manualNumber, mode}
- WeighbridgeUnavailableException -> HTTP 503 explicite (RG-5.06)
- site courant via CurrentSiteProviderInterface (contrat Sites)
- is_granted('logistique.weighing_tickets.manage')
- dsd renvoyé prévisionnel : attribution autoritaire refaite à la création
du ticket (ERP-185)
- tests : WeighbridgeReaderStubTest, DsdAllocatorTest, processor (503/400),
WeighbridgeReadingApiTest (RBAC + AUTO/MANUAL + 422)
107 lines
3.5 KiB
PHP
107 lines
3.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Module\Logistique\Infrastructure\Service;
|
|
|
|
use App\Module\Logistique\Infrastructure\Service\DsdAllocator;
|
|
use App\Module\Sites\Domain\Entity\Site;
|
|
use Doctrine\DBAL\ArrayParameterType;
|
|
use Doctrine\DBAL\Connection;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
|
|
|
/**
|
|
* Allocateur DSD (RG-5.04 / § 2.7) — test d'integration sur la table
|
|
* `weighbridge_dsd_counter` (DBAL brut, verrou FOR UPDATE).
|
|
*
|
|
* Verifie l'increment sequentiel et l'isolation PAR SITE (un pont par site).
|
|
* Les compteurs des sites touches sont remis a zero en debut de test et purges
|
|
* en tearDown (pas de DAMA en local — nettoyage manuel obligatoire).
|
|
*
|
|
* @internal
|
|
*/
|
|
final class DsdAllocatorTest extends KernelTestCase
|
|
{
|
|
private Connection $connection;
|
|
private DsdAllocator $allocator;
|
|
private EntityManagerInterface $em;
|
|
|
|
/** @var list<int> */
|
|
private array $touchedSiteIds = [];
|
|
|
|
protected function setUp(): void
|
|
{
|
|
self::bootKernel();
|
|
$container = self::getContainer();
|
|
$this->em = $container->get('doctrine')->getManager();
|
|
$this->connection = $this->em->getConnection();
|
|
$this->allocator = $container->get(DsdAllocator::class);
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
if ([] !== $this->touchedSiteIds) {
|
|
$this->connection->executeStatement(
|
|
'DELETE FROM weighbridge_dsd_counter WHERE site_id IN (?)',
|
|
[$this->touchedSiteIds],
|
|
[ArrayParameterType::INTEGER],
|
|
);
|
|
}
|
|
|
|
parent::tearDown();
|
|
}
|
|
|
|
public function testNextIncrementsSequentiallyAndIsIsolatedPerSite(): void
|
|
{
|
|
$sites = $this->em->getRepository(Site::class)->findAll();
|
|
self::assertGreaterThanOrEqual(2, \count($sites), 'Au moins 2 sites doivent etre seedes (fixtures).');
|
|
|
|
$siteA = $sites[0];
|
|
$siteB = $sites[1];
|
|
$this->resetCounter($siteA);
|
|
$this->resetCounter($siteB);
|
|
|
|
// AUTO/MANUAL partagent le meme increment : la sequence demarre a 1.
|
|
self::assertSame(1, $this->allocator->next($siteA));
|
|
self::assertSame(2, $this->allocator->next($siteA));
|
|
self::assertSame(3, $this->allocator->next($siteA));
|
|
|
|
// Isolation par site : le compteur de B est independant de celui de A.
|
|
self::assertSame(1, $this->allocator->next($siteB));
|
|
self::assertSame(2, $this->allocator->next($siteB));
|
|
|
|
// La sequence de A reprend la ou elle en etait (4), non perturbee par B.
|
|
self::assertSame(4, $this->allocator->next($siteA));
|
|
}
|
|
|
|
public function testNextStartsAtOneWhenNoCounterRowExists(): void
|
|
{
|
|
$site = $this->em->getRepository(Site::class)->findAll()[0];
|
|
$this->resetCounter($site);
|
|
|
|
// Aucune ligne compteur pour ce site : le premier appel la cree (last=0)
|
|
// et renvoie 1 (dernier + 1).
|
|
self::assertSame(1, $this->allocator->next($site));
|
|
}
|
|
|
|
/**
|
|
* Supprime la ligne compteur du site pour repartir d'un etat connu, et
|
|
* enregistre l'id pour la purge de tearDown.
|
|
*/
|
|
private function resetCounter(Site $site): void
|
|
{
|
|
$siteId = $site->getId();
|
|
self::assertNotNull($siteId);
|
|
|
|
$this->connection->executeStatement(
|
|
'DELETE FROM weighbridge_dsd_counter WHERE site_id = :site',
|
|
['site' => $siteId],
|
|
);
|
|
|
|
if (!\in_array($siteId, $this->touchedSiteIds, true)) {
|
|
$this->touchedSiteIds[] = $siteId;
|
|
}
|
|
}
|
|
}
|