175 lines
5.7 KiB
PHP
175 lines
5.7 KiB
PHP
<?php
|
|
|
|
namespace App\Tests\Entity;
|
|
|
|
use App\Entity\Bucket;
|
|
use App\Entity\Scenario;
|
|
use App\Entity\Stream;
|
|
use App\Entity\User;
|
|
use App\Enum\BucketAllocationType;
|
|
use App\Enum\BucketType;
|
|
use App\Enum\StreamFrequency;
|
|
use App\Enum\StreamType;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
|
|
|
final class StreamPersistenceTest extends KernelTestCase
|
|
{
|
|
private EntityManagerInterface $em;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
self::bootKernel();
|
|
$this->em = self::getContainer()->get(EntityManagerInterface::class);
|
|
}
|
|
|
|
private function persistOwner(): User
|
|
{
|
|
static $counter = 0;
|
|
++$counter;
|
|
|
|
$owner = new User();
|
|
$owner->setEmail(\sprintf('stream-persistence-test-%d@example.com', $counter));
|
|
$owner->setPassword('irrelevant-hash');
|
|
|
|
$this->em->persist($owner);
|
|
|
|
return $owner;
|
|
}
|
|
|
|
public function testItPersistsAStreamLinkedToItsScenarioAndBucket(): void
|
|
{
|
|
$scenario = new Scenario();
|
|
$scenario->setName('Household Budget');
|
|
$scenario->setOwner($this->persistOwner());
|
|
$this->em->persist($scenario);
|
|
|
|
$bucket = new Bucket();
|
|
$bucket->setScenario($scenario);
|
|
$bucket->setType(BucketType::NEED);
|
|
$bucket->setName('Rent');
|
|
$bucket->setPriority(1);
|
|
$bucket->setSortOrder(0);
|
|
$bucket->setAllocationType(BucketAllocationType::FIXED_LIMIT);
|
|
$bucket->setAllocationValue(150000);
|
|
$bucket->setStartingAmount(0);
|
|
$bucket->setBufferMultiplier('0.00');
|
|
$this->em->persist($bucket);
|
|
|
|
$this->em->flush();
|
|
|
|
$scenarioId = $scenario->getId();
|
|
$bucketId = $bucket->getId();
|
|
|
|
$stream = new Stream();
|
|
$stream->setScenario($scenario);
|
|
$stream->setBucket($bucket);
|
|
$stream->setName('Paycheck');
|
|
$stream->setAmount(250000);
|
|
$stream->setType(StreamType::INCOME);
|
|
$stream->setFrequency(StreamFrequency::BIWEEKLY);
|
|
$stream->setStartDate(new \DateTimeImmutable('2026-01-01'));
|
|
$stream->setEndDate(new \DateTimeImmutable('2026-12-31'));
|
|
$stream->setIsActive(true);
|
|
$stream->setDescription('Primary employer income.');
|
|
|
|
$this->em->persist($stream);
|
|
$this->em->flush();
|
|
|
|
$streamId = $stream->getId();
|
|
|
|
$this->em->clear();
|
|
|
|
$reloaded = $this->em->find(Stream::class, $streamId);
|
|
|
|
self::assertNotNull($reloaded, 'Stream must be retrievable from the database after an identity-map clear.');
|
|
|
|
self::assertInstanceOf(Scenario::class, $reloaded->getScenario());
|
|
self::assertSame(
|
|
(string) $scenarioId,
|
|
(string) $reloaded->getScenario()->getId(),
|
|
'Reloaded stream must be linked to the same scenario it was persisted with.',
|
|
);
|
|
|
|
self::assertInstanceOf(Bucket::class, $reloaded->getBucket());
|
|
self::assertSame(
|
|
(string) $bucketId,
|
|
(string) $reloaded->getBucket()->getId(),
|
|
'Reloaded stream must be linked to the same bucket it was persisted with.',
|
|
);
|
|
|
|
self::assertSame('Paycheck', $reloaded->getName());
|
|
|
|
self::assertSame(
|
|
250000,
|
|
$reloaded->getAmount(),
|
|
'amount must round-trip as an int (cents), not a string or float.',
|
|
);
|
|
|
|
self::assertInstanceOf(
|
|
StreamType::class,
|
|
$reloaded->getType(),
|
|
'type must hydrate as a StreamType enum instance, not a raw string.',
|
|
);
|
|
self::assertSame(StreamType::INCOME, $reloaded->getType());
|
|
|
|
self::assertInstanceOf(
|
|
StreamFrequency::class,
|
|
$reloaded->getFrequency(),
|
|
'frequency must hydrate as a StreamFrequency enum instance, not a raw string.',
|
|
);
|
|
self::assertSame(StreamFrequency::BIWEEKLY, $reloaded->getFrequency());
|
|
|
|
self::assertInstanceOf(\DateTimeImmutable::class, $reloaded->getStartDate());
|
|
self::assertSame('2026-01-01', $reloaded->getStartDate()->format('Y-m-d'));
|
|
|
|
self::assertInstanceOf(\DateTimeImmutable::class, $reloaded->getEndDate());
|
|
self::assertSame('2026-12-31', $reloaded->getEndDate()->format('Y-m-d'));
|
|
|
|
self::assertTrue($reloaded->isActive());
|
|
|
|
self::assertSame('Primary employer income.', $reloaded->getDescription());
|
|
}
|
|
|
|
public function testItPersistsAStreamWithoutABucket(): void
|
|
{
|
|
$scenario = new Scenario();
|
|
$scenario->setName('Household Budget');
|
|
$scenario->setOwner($this->persistOwner());
|
|
$this->em->persist($scenario);
|
|
$this->em->flush();
|
|
|
|
$stream = new Stream();
|
|
$stream->setScenario($scenario);
|
|
$stream->setName('Freelance Income');
|
|
$stream->setAmount(50000);
|
|
$stream->setType(StreamType::INCOME);
|
|
$stream->setFrequency(StreamFrequency::MONTHLY);
|
|
$stream->setStartDate(new \DateTimeImmutable('2026-02-01'));
|
|
|
|
$this->em->persist($stream);
|
|
$this->em->flush();
|
|
|
|
$streamId = $stream->getId();
|
|
|
|
$this->em->clear();
|
|
|
|
$reloaded = $this->em->find(Stream::class, $streamId);
|
|
|
|
self::assertNotNull($reloaded, 'Stream must be retrievable from the database after an identity-map clear.');
|
|
self::assertNull(
|
|
$reloaded->getBucket(),
|
|
'bucket must be nullable — a stream is not required to link to a bucket.',
|
|
);
|
|
|
|
self::assertNull(
|
|
$reloaded->getEndDate(),
|
|
'end_date must be nullable when not explicitly set.',
|
|
);
|
|
|
|
self::assertTrue(
|
|
$reloaded->isActive(),
|
|
'is_active must default to true when setIsActive() is never called.',
|
|
);
|
|
}
|
|
}
|