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)
This commit is contained in:
+133
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Module\Logistique\Infrastructure\ApiPlatform\State\Processor;
|
||||
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Module\Logistique\Application\Service\DsdAllocatorInterface;
|
||||
use App\Module\Logistique\Domain\Contract\WeighbridgeReaderInterface;
|
||||
use App\Module\Logistique\Domain\Exception\WeighbridgeUnavailableException;
|
||||
use App\Module\Logistique\Domain\Weighbridge\WeighbridgeReading;
|
||||
use App\Module\Logistique\Infrastructure\ApiPlatform\Resource\WeighbridgeReadingResource;
|
||||
use App\Module\Logistique\Infrastructure\ApiPlatform\State\Processor\WeighbridgeReadingProcessor;
|
||||
use App\Module\Sites\Application\Service\CurrentSiteProviderInterface;
|
||||
use App\Module\Sites\Domain\Entity\Site;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
|
||||
|
||||
/**
|
||||
* Processor de l'action `POST /api/weighbridge_readings` (§ 4.2).
|
||||
*
|
||||
* Couvre les 4 chemins sans BDD ni HTTP (stubs purs) : AUTO (lecture pont),
|
||||
* MANUAL (allocation DSD seule), indisponibilite → 503 (RG-5.06) et absence de
|
||||
* site courant → 400.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class WeighbridgeReadingProcessorTest extends TestCase
|
||||
{
|
||||
private function site(): Site
|
||||
{
|
||||
// getId() reste null (non persiste) — sans incidence : reader et allocator
|
||||
// sont stubbes dans ces tests unitaires.
|
||||
return new Site('Châtellerault', 'Rue du Pont', null, '86000', 'Châtellerault', '#112233');
|
||||
}
|
||||
|
||||
public function testAutoModeFillsWeightAndDsdFromReader(): void
|
||||
{
|
||||
$siteProvider = $this->createStub(CurrentSiteProviderInterface::class);
|
||||
$siteProvider->method('get')->willReturn($this->site());
|
||||
|
||||
$reader = $this->createStub(WeighbridgeReaderInterface::class);
|
||||
$reader->method('read')->willReturn(new WeighbridgeReading(23000, 42));
|
||||
|
||||
$processor = new WeighbridgeReadingProcessor(
|
||||
$siteProvider,
|
||||
$reader,
|
||||
$this->createStub(DsdAllocatorInterface::class),
|
||||
);
|
||||
|
||||
$resource = new WeighbridgeReadingResource();
|
||||
$resource->mode = 'AUTO';
|
||||
|
||||
$result = $processor->process($resource, new Post());
|
||||
|
||||
self::assertSame(23000, $result->weight);
|
||||
self::assertSame(42, $result->dsd);
|
||||
self::assertNull($result->manualNumber);
|
||||
self::assertSame('AUTO', $result->mode);
|
||||
}
|
||||
|
||||
public function testManualModeKeepsWeightAndAllocatesDsd(): void
|
||||
{
|
||||
$siteProvider = $this->createStub(CurrentSiteProviderInterface::class);
|
||||
$siteProvider->method('get')->willReturn($this->site());
|
||||
|
||||
$allocator = $this->createStub(DsdAllocatorInterface::class);
|
||||
$allocator->method('next')->willReturn(43);
|
||||
|
||||
$processor = new WeighbridgeReadingProcessor(
|
||||
$siteProvider,
|
||||
$this->createStub(WeighbridgeReaderInterface::class),
|
||||
$allocator,
|
||||
);
|
||||
|
||||
$resource = new WeighbridgeReadingResource();
|
||||
$resource->mode = 'MANUAL';
|
||||
$resource->weight = 23187;
|
||||
$resource->manualNumber = 'PAP-555';
|
||||
|
||||
$result = $processor->process($resource, new Post());
|
||||
|
||||
self::assertSame(23187, $result->weight, 'Le poids saisi est conserve en manuel.');
|
||||
self::assertSame(43, $result->dsd);
|
||||
self::assertSame('PAP-555', $result->manualNumber);
|
||||
self::assertSame('MANUAL', $result->mode);
|
||||
}
|
||||
|
||||
public function testWeighbridgeUnavailableIsMappedTo503(): void
|
||||
{
|
||||
$siteProvider = $this->createStub(CurrentSiteProviderInterface::class);
|
||||
$siteProvider->method('get')->willReturn($this->site());
|
||||
|
||||
$reader = $this->createStub(WeighbridgeReaderInterface::class);
|
||||
$reader->method('read')->willThrowException(new WeighbridgeUnavailableException());
|
||||
|
||||
$processor = new WeighbridgeReadingProcessor(
|
||||
$siteProvider,
|
||||
$reader,
|
||||
$this->createStub(DsdAllocatorInterface::class),
|
||||
);
|
||||
|
||||
$resource = new WeighbridgeReadingResource();
|
||||
$resource->mode = 'AUTO';
|
||||
|
||||
try {
|
||||
$processor->process($resource, new Post());
|
||||
self::fail('Une ServiceUnavailableHttpException (503) etait attendue.');
|
||||
} catch (ServiceUnavailableHttpException $e) {
|
||||
self::assertSame(503, $e->getStatusCode());
|
||||
self::assertStringContainsString('pesée manuelle', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function testMissingCurrentSiteIsRejected(): void
|
||||
{
|
||||
$siteProvider = $this->createStub(CurrentSiteProviderInterface::class);
|
||||
$siteProvider->method('get')->willReturn(null);
|
||||
|
||||
$processor = new WeighbridgeReadingProcessor(
|
||||
$siteProvider,
|
||||
$this->createStub(WeighbridgeReaderInterface::class),
|
||||
$this->createStub(DsdAllocatorInterface::class),
|
||||
);
|
||||
|
||||
$resource = new WeighbridgeReadingResource();
|
||||
$resource->mode = 'AUTO';
|
||||
|
||||
$this->expectException(BadRequestHttpException::class);
|
||||
$processor->process($resource, new Post());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user