172 lines
6 KiB
PHP
172 lines
6 KiB
PHP
<?php
|
|
|
|
namespace App\Tests\Repository;
|
|
|
|
use App\Entity\Bucket;
|
|
use App\Entity\Scenario;
|
|
use App\Enum\BucketAllocationType;
|
|
use App\Enum\BucketType;
|
|
use App\Repository\BucketRepository;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
|
|
|
final class BucketRepositoryTest extends KernelTestCase
|
|
{
|
|
private EntityManagerInterface $em;
|
|
private BucketRepository $repository;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
self::bootKernel();
|
|
$this->em = self::getContainer()->get(EntityManagerInterface::class);
|
|
$this->repository = self::getContainer()->get(BucketRepository::class);
|
|
}
|
|
|
|
public function testCountIsZeroWhenScenarioHasNoOverflowBucket(): void
|
|
{
|
|
$scenario = $this->persistScenario();
|
|
|
|
$this->persistBucket($scenario, BucketType::NEED, 1);
|
|
$this->persistBucket($scenario, BucketType::WANT, 2);
|
|
|
|
self::assertSame(
|
|
0,
|
|
$this->repository->countOverflowBucketsForScenario($scenario, null),
|
|
'A scenario with no OVERFLOW bucket must count zero.',
|
|
);
|
|
}
|
|
|
|
public function testCountIsOneWhenScenarioHasOneOverflowBucket(): void
|
|
{
|
|
$scenario = $this->persistScenario();
|
|
|
|
$this->persistBucket($scenario, BucketType::NEED, 1);
|
|
$this->persistBucket($scenario, BucketType::OVERFLOW, 2);
|
|
|
|
self::assertSame(
|
|
1,
|
|
$this->repository->countOverflowBucketsForScenario($scenario, null),
|
|
'A scenario with exactly one OVERFLOW bucket must count one.',
|
|
);
|
|
}
|
|
|
|
public function testCountExcludesTheGivenBucketIdFromTheTally(): void
|
|
{
|
|
$scenario = $this->persistScenario();
|
|
|
|
$overflow = $this->persistBucket($scenario, BucketType::OVERFLOW, 1);
|
|
|
|
self::assertSame(
|
|
0,
|
|
$this->repository->countOverflowBucketsForScenario($scenario, $overflow->getId()),
|
|
'Excluding the existing overflow bucket\'s own id must drop the count to zero — '
|
|
.'this is what allows updating the sole overflow bucket without falsely tripping the rule.',
|
|
);
|
|
}
|
|
|
|
public function testCountIsScopedToTheGivenScenarioOnly(): void
|
|
{
|
|
$scenarioA = $this->persistScenario('Scenario A');
|
|
$scenarioB = $this->persistScenario('Scenario B');
|
|
|
|
$this->persistBucket($scenarioA, BucketType::OVERFLOW, 1);
|
|
|
|
self::assertSame(
|
|
0,
|
|
$this->repository->countOverflowBucketsForScenario($scenarioB, null),
|
|
'An OVERFLOW bucket belonging to a different scenario must not be counted.',
|
|
);
|
|
}
|
|
|
|
public function testNeedAndWantBucketsDoNotCountTowardTheOverflowTotal(): void
|
|
{
|
|
$scenario = $this->persistScenario();
|
|
|
|
$this->persistBucket($scenario, BucketType::NEED, 1);
|
|
$this->persistBucket($scenario, BucketType::WANT, 2);
|
|
|
|
self::assertSame(
|
|
0,
|
|
$this->repository->countOverflowBucketsForScenario($scenario, null),
|
|
'NEED and WANT buckets must never count toward the overflow tally.',
|
|
);
|
|
}
|
|
|
|
public function testFindByScenarioOrderedByPriorityReturnsScenarioBucketsInPriorityOrder(): void
|
|
{
|
|
$scenario = $this->persistScenario();
|
|
|
|
// Inserted out of priority order on purpose.
|
|
$third = $this->persistBucket($scenario, BucketType::NEED, 3);
|
|
$first = $this->persistBucket($scenario, BucketType::WANT, 1);
|
|
$second = $this->persistBucket($scenario, BucketType::OVERFLOW, 2);
|
|
|
|
$buckets = $this->repository->findByScenarioOrderedByPriority($scenario);
|
|
|
|
self::assertSame(
|
|
[$first->getId(), $second->getId(), $third->getId()],
|
|
array_map(static fn (Bucket $bucket) => $bucket->getId(), $buckets),
|
|
'Buckets must come back ordered by priority ascending, regardless of insertion order.',
|
|
);
|
|
}
|
|
|
|
public function testFindByScenarioOrderedByPriorityReturnsOnlyTheGivenScenariosBuckets(): void
|
|
{
|
|
$scenarioA = $this->persistScenario('Scenario A');
|
|
$scenarioB = $this->persistScenario('Scenario B');
|
|
|
|
$bucketA1 = $this->persistBucket($scenarioA, BucketType::NEED, 1);
|
|
$bucketA2 = $this->persistBucket($scenarioA, BucketType::OVERFLOW, 2);
|
|
$this->persistBucket($scenarioB, BucketType::NEED, 1);
|
|
|
|
$buckets = $this->repository->findByScenarioOrderedByPriority($scenarioA);
|
|
|
|
self::assertSame(
|
|
[$bucketA1->getId(), $bucketA2->getId()],
|
|
array_map(static fn (Bucket $bucket) => $bucket->getId(), $buckets),
|
|
'Only the queried scenario\'s buckets must be returned — another scenario\'s buckets must not leak in.',
|
|
);
|
|
}
|
|
|
|
public function testFindByScenarioOrderedByPriorityReturnsEmptyForScenarioWithNoBuckets(): void
|
|
{
|
|
$scenario = $this->persistScenario();
|
|
|
|
self::assertSame(
|
|
[],
|
|
$this->repository->findByScenarioOrderedByPriority($scenario),
|
|
'A scenario with no buckets must return an empty array.',
|
|
);
|
|
}
|
|
|
|
private function persistScenario(string $name = 'Household Budget'): Scenario
|
|
{
|
|
$scenario = new Scenario();
|
|
$scenario->setName($name);
|
|
$this->em->persist($scenario);
|
|
$this->em->flush();
|
|
|
|
return $scenario;
|
|
}
|
|
|
|
private function persistBucket(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');
|
|
|
|
$this->em->persist($bucket);
|
|
$this->em->flush();
|
|
|
|
return $bucket;
|
|
}
|
|
}
|