136 lines
4.6 KiB
PHP
136 lines
4.6 KiB
PHP
<?php
|
|
|
|
namespace App\Tests\Validator;
|
|
|
|
use App\Entity\Bucket;
|
|
use App\Entity\Scenario;
|
|
use App\Enum\BucketAllocationType;
|
|
use App\Enum\BucketType;
|
|
use App\Validator\SingleOverflowPerScenario;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
|
use Symfony\Component\Validator\ConstraintViolationListInterface;
|
|
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
|
|
|
final class SingleOverflowPerScenarioTest extends KernelTestCase
|
|
{
|
|
private EntityManagerInterface $em;
|
|
private ValidatorInterface $validator;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
self::bootKernel();
|
|
$this->em = self::getContainer()->get(EntityManagerInterface::class);
|
|
$this->validator = self::getContainer()->get(ValidatorInterface::class);
|
|
}
|
|
|
|
public function testSecondOverflowBucketOnTheSameScenarioRaisesAViolation(): void
|
|
{
|
|
$scenario = new Scenario();
|
|
$scenario->setName('Household Budget');
|
|
$this->em->persist($scenario);
|
|
$this->em->flush();
|
|
|
|
$existingOverflow = $this->makeBucket($scenario, BucketType::OVERFLOW, 1);
|
|
$this->em->persist($existingOverflow);
|
|
$this->em->flush();
|
|
|
|
$secondOverflow = $this->makeBucket($scenario, BucketType::OVERFLOW, 2);
|
|
|
|
$violations = $this->validator->validate($secondOverflow);
|
|
|
|
$matching = $this->violationsAt($violations, 'type');
|
|
|
|
self::assertNotCount(
|
|
0,
|
|
$matching,
|
|
'A second OVERFLOW bucket on a scenario that already has one must raise a violation on type. '
|
|
.'The existing overflow bucket is persisted-but-uncommitted in this test\'s DAMA transaction — '
|
|
.'the validator\'s repository query must still see it within the same transaction.',
|
|
);
|
|
}
|
|
|
|
public function testTheSoleExistingOverflowBucketValidatesWithNoSingleOverflowViolation(): void
|
|
{
|
|
$scenario = new Scenario();
|
|
$scenario->setName('Household Budget');
|
|
$this->em->persist($scenario);
|
|
$this->em->flush();
|
|
|
|
$overflow = $this->makeBucket($scenario, BucketType::OVERFLOW, 1);
|
|
$this->em->persist($overflow);
|
|
$this->em->flush();
|
|
|
|
$violations = $this->validator->validate($overflow);
|
|
|
|
$matching = $this->violationsAt($violations, 'type');
|
|
|
|
self::assertCount(
|
|
0,
|
|
$matching,
|
|
'The single existing OVERFLOW bucket must not raise this constraint against itself '
|
|
.'(exclude-self-by-id must be working).',
|
|
);
|
|
}
|
|
|
|
public function testFirstOverflowBucketOnAScenarioWithNoOverflowBucketsIsValid(): void
|
|
{
|
|
$scenario = new Scenario();
|
|
$scenario->setName('Household Budget');
|
|
$this->em->persist($scenario);
|
|
$this->em->flush();
|
|
|
|
$needBucket = $this->makeBucket($scenario, BucketType::NEED, 1);
|
|
$this->em->persist($needBucket);
|
|
$this->em->flush();
|
|
|
|
$firstOverflow = $this->makeBucket($scenario, BucketType::OVERFLOW, 2);
|
|
|
|
$violations = $this->validator->validate($firstOverflow);
|
|
|
|
$matching = $this->violationsAt($violations, 'type');
|
|
|
|
self::assertCount(
|
|
0,
|
|
$matching,
|
|
'A brand-new, unpersisted OVERFLOW bucket on a scenario with zero existing OVERFLOW buckets '
|
|
.'must not raise this constraint — this is the common create-the-first-overflow-bucket flow.',
|
|
);
|
|
}
|
|
|
|
private function makeBucket(Scenario $scenario, BucketType $type, int $priority): Bucket
|
|
{
|
|
$bucket = new Bucket();
|
|
$bucket->setScenario($scenario);
|
|
$bucket->setType($type);
|
|
$bucket->setName('Bucket '.$priority);
|
|
$bucket->setPriority($priority);
|
|
$bucket->setSortOrder($priority);
|
|
$bucket->setAllocationType(
|
|
BucketType::OVERFLOW === $type ? BucketAllocationType::UNLIMITED : BucketAllocationType::FIXED_LIMIT,
|
|
);
|
|
$bucket->setAllocationValue(BucketType::OVERFLOW === $type ? null : 50000);
|
|
$bucket->setStartingAmount(0);
|
|
$bucket->setBufferMultiplier('0.00');
|
|
|
|
return $bucket;
|
|
}
|
|
|
|
/**
|
|
* @return list<\Symfony\Component\Validator\ConstraintViolationInterface>
|
|
*/
|
|
private function violationsAt(ConstraintViolationListInterface $violations, string $propertyPath): array
|
|
{
|
|
$matching = [];
|
|
|
|
foreach ($violations as $violation) {
|
|
if ($violation->getPropertyPath() === $propertyPath
|
|
&& $violation->getConstraint() instanceof SingleOverflowPerScenario
|
|
) {
|
|
$matching[] = $violation;
|
|
}
|
|
}
|
|
|
|
return $matching;
|
|
}
|
|
}
|