buckets/tests/Validator/SingleOverflowPerScenarioTest.php

142 lines
4.8 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\Tests\Concerns\CreatesUsers;
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
{
use CreatesUsers;
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');
$scenario->setOwner($this->persistOwner());
$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');
$scenario->setOwner($this->persistOwner());
$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');
$scenario->setOwner($this->persistOwner());
$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;
}
}