Files
Inventory/tests/Api/Service/ComposantSyncStrategyTest.php
2026-03-23 15:14:33 +01:00

179 lines
6.7 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Api\Service;
use App\DTO\SyncConfirmation;
use App\Entity\SkeletonPieceRequirement;
use App\Enum\ModelCategory;
use App\Service\Sync\ComposantSyncStrategy;
use App\Tests\AbstractApiTestCase;
/**
* @internal
*/
class ComposantSyncStrategyTest extends AbstractApiTestCase
{
private ComposantSyncStrategy $strategy;
protected function setUp(): void
{
parent::setUp();
$this->strategy = static::getContainer()->get(ComposantSyncStrategy::class);
}
public function testSupportsComponentCategory(): void
{
$mt = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
$this->assertTrue($this->strategy->supports($mt));
}
public function testPreviewNoImpactWhenNoComposants(): void
{
$mt = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
$result = $this->strategy->preview($mt, ['pieces' => [], 'products' => [], 'subcomponents' => [], 'customFields' => []]);
$this->assertSame(0, $result->itemCount);
$this->assertFalse($result->hasImpact());
}
public function testPreviewDetectsNewPieceSlot(): void
{
$mt = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
$pieceType = $this->createModelType('Piece Type', 'PT-001', ModelCategory::PIECE);
$this->createComposant('C1', null, $mt);
$result = $this->strategy->preview($mt, [
'pieces' => [['typePieceId' => $pieceType->getId(), 'position' => 0]],
'products' => [],
'subcomponents' => [],
'customFields' => [],
]);
$this->assertSame(1, $result->itemCount);
$this->assertSame(1, $result->additions['pieceSlots']);
}
public function testPreviewDetectsSlotDeletion(): void
{
$mt = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
$pieceType = $this->createModelType('Piece Type', 'PT-001', ModelCategory::PIECE);
$composant = $this->createComposant('C1', null, $mt);
$this->createComposantPieceSlot($composant, $pieceType, null, 1, 0);
$result = $this->strategy->preview($mt, [
'pieces' => [],
'products' => [],
'subcomponents' => [],
'customFields' => [],
]);
$this->assertSame(1, $result->deletions['pieceSlots']);
}
public function testPreviewNoImpactWhenSlotsMatch(): void
{
$mt = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
$pieceType = $this->createModelType('Piece Type', 'PT-001', ModelCategory::PIECE);
$composant = $this->createComposant('C1', null, $mt);
$this->createComposantPieceSlot($composant, $pieceType, null, 1, 0);
$result = $this->strategy->preview($mt, [
'pieces' => [['typePieceId' => $pieceType->getId(), 'position' => 0]],
'products' => [],
'subcomponents' => [],
'customFields' => [],
]);
$this->assertFalse($result->hasImpact());
}
public function testExecuteAddsMissingSlots(): void
{
$mt = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
$pieceType = $this->createModelType('Piece Type', 'PT-001', ModelCategory::PIECE);
$composant = $this->createComposant('C1', null, $mt);
$em = $this->getEntityManager();
$req = new SkeletonPieceRequirement();
$req->setModelType($mt);
$req->setTypePiece($pieceType);
$req->setPosition(0);
$em->persist($req);
$em->flush();
$result = $this->strategy->execute($mt, new SyncConfirmation());
$this->assertSame(1, $result->itemsUpdated);
$this->assertSame(1, $result->additions['pieceSlots']);
$em->refresh($composant);
$this->assertSame(2, $composant->getVersion());
}
public function testExecutePreservesExistingSelections(): void
{
$mt = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
$pieceType = $this->createModelType('Piece Type', 'PT-001', ModelCategory::PIECE);
$composant = $this->createComposant('C1', null, $mt);
$piece = $this->createPiece('P1', 'P1-REF', $pieceType);
$slot = $this->createComposantPieceSlot($composant, $pieceType, $piece, 5, 0);
$em = $this->getEntityManager();
$req = new SkeletonPieceRequirement();
$req->setModelType($mt);
$req->setTypePiece($pieceType);
$req->setPosition(0);
$em->persist($req);
$em->flush();
$result = $this->strategy->execute($mt, new SyncConfirmation());
// No changes — slot already matches
$this->assertSame(0, $result->itemsUpdated);
// Selection and quantity preserved
$em->refresh($slot);
$this->assertSame($piece->getId(), $slot->getSelectedPiece()->getId());
$this->assertSame(5, $slot->getQuantity());
}
public function testExecuteDeletesSlotsOnlyWithConfirmation(): void
{
$mt = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
$pieceType = $this->createModelType('Piece Type', 'PT-001', ModelCategory::PIECE);
$composant = $this->createComposant('C1', null, $mt);
$this->createComposantPieceSlot($composant, $pieceType, null, 1, 0);
// No skeleton requirements -> slot should be deleted
$result = $this->strategy->execute($mt, new SyncConfirmation(confirmDeletions: false));
$this->assertSame(0, $result->deletions['pieceSlots']);
// With confirmation
$result = $this->strategy->execute($mt, new SyncConfirmation(confirmDeletions: true));
$this->assertSame(1, $result->deletions['pieceSlots']);
}
public function testExecuteIsIdempotent(): void
{
$mt = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
$pieceType = $this->createModelType('Piece Type', 'PT-001', ModelCategory::PIECE);
$composant = $this->createComposant('C1', null, $mt);
$em = $this->getEntityManager();
$req = new SkeletonPieceRequirement();
$req->setModelType($mt);
$req->setTypePiece($pieceType);
$req->setPosition(0);
$em->persist($req);
$em->flush();
$result1 = $this->strategy->execute($mt, new SyncConfirmation());
$this->assertSame(1, $result1->additions['pieceSlots']);
$em->refresh($composant);
$result2 = $this->strategy->execute($mt, new SyncConfirmation());
$this->assertSame(0, $result2->itemsUpdated);
}
}