em = self::getContainer()->get(EntityManagerInterface::class); } public function testItPersistsABucketLinkedToItsScenario(): void { $scenario = new Scenario(); $scenario->setName('Household Budget'); $this->em->persist($scenario); $this->em->flush(); $scenarioId = $scenario->getId(); $bucket = new Bucket(); $bucket->setScenario($scenario); $bucket->setType(BucketType::NEED); $bucket->setName('Rent'); $bucket->setPriority(3); $bucket->setSortOrder(2); $bucket->setAllocationType(BucketAllocationType::FIXED_LIMIT); $bucket->setAllocationValue(150000); $bucket->setStartingAmount(5000); $bucket->setBufferMultiplier('1.50'); $this->em->persist($bucket); $this->em->flush(); $bucketId = $bucket->getId(); $this->em->clear(); $reloaded = $this->em->find(Bucket::class, $bucketId); self::assertNotNull($reloaded, 'Bucket 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 bucket must be linked to the same scenario it was persisted with.', ); self::assertInstanceOf( BucketType::class, $reloaded->getType(), 'type must hydrate as a BucketType enum instance, not a raw string.', ); self::assertSame(BucketType::NEED, $reloaded->getType()); self::assertSame('Rent', $reloaded->getName()); self::assertSame(3, $reloaded->getPriority()); self::assertSame(2, $reloaded->getSortOrder()); self::assertInstanceOf( BucketAllocationType::class, $reloaded->getAllocationType(), 'allocation_type must hydrate as a BucketAllocationType enum instance, not a raw string.', ); self::assertSame(BucketAllocationType::FIXED_LIMIT, $reloaded->getAllocationType()); self::assertSame( 150000, $reloaded->getAllocationValue(), 'allocation_value must round-trip as an int (cents), not a string or float.', ); self::assertSame(5000, $reloaded->getStartingAmount()); self::assertSame( '1.50', $reloaded->getBufferMultiplier(), 'buffer_multiplier is a Doctrine decimal column — it hydrates as a numeric string, not a float (precision-safe).', ); } public function testScenarioPriorityUniquenessIsEnforced(): void { $scenario = new Scenario(); $scenario->setName('Household Budget'); $this->em->persist($scenario); $this->em->flush(); $first = new Bucket(); $first->setScenario($scenario); $first->setType(BucketType::NEED); $first->setName('Rent'); $first->setPriority(3); $first->setSortOrder(1); $first->setAllocationType(BucketAllocationType::FIXED_LIMIT); $first->setAllocationValue(150000); $first->setStartingAmount(5000); $first->setBufferMultiplier('0.00'); $this->em->persist($first); $this->em->flush(); $second = new Bucket(); $second->setScenario($scenario); $second->setType(BucketType::NEED); $second->setName('Utilities'); $second->setPriority(3); $second->setSortOrder(2); $second->setAllocationType(BucketAllocationType::FIXED_LIMIT); $second->setAllocationValue(50000); $second->setStartingAmount(3000); $second->setBufferMultiplier('0.00'); $this->em->persist($second); $this->expectException(UniqueConstraintViolationException::class); $this->em->flush(); } }