Files
Starseed/tests/Module/Logistique/Infrastructure/Service/DsdAllocatorTest.php
T
Matthieu e455204bbd feat(logistique) : pesée pont bascule stub + allocateur DSD + endpoint (ERP-184)
- 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)
2026-06-17 18:09:54 +02:00

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;
}
}
}