Compare commits
2 commits
473b88b54e
...
e563816cb3
| Author | SHA1 | Date | |
|---|---|---|---|
| e563816cb3 | |||
| e0e2f255ac |
8 changed files with 129 additions and 89 deletions
29
migrations/Version20260627065959.php
Normal file
29
migrations/Version20260627065959.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Drop the unused distribution_mode column from scenario (#61).
|
||||
*/
|
||||
final class Version20260627065959 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Drop the unused distribution_mode column from scenario (no consumer; #61).';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE scenario DROP distribution_mode');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// Assumes an empty table (the column was always empty/unused); the
|
||||
// NOT NULL re-add would fail on any pre-existing populated rows.
|
||||
$this->addSql('ALTER TABLE scenario ADD distribution_mode VARCHAR(255) NOT NULL');
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ use ApiPlatform\Metadata\ApiResource;
|
|||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Enum\DistributionMode;
|
||||
use App\Repository\ScenarioRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
|
@ -33,9 +32,6 @@ class Scenario
|
|||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
private User $owner;
|
||||
|
||||
#[ORM\Column(enumType: DistributionMode::class)]
|
||||
private DistributionMode $distributionMode = DistributionMode::EVEN;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Assert\NotBlank]
|
||||
private string $name;
|
||||
|
|
@ -58,11 +54,6 @@ class Scenario
|
|||
return $this->owner;
|
||||
}
|
||||
|
||||
public function getDistributionMode(): DistributionMode
|
||||
{
|
||||
return $this->distributionMode;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
|
|
@ -80,13 +71,6 @@ class Scenario
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setDistributionMode(DistributionMode $distributionMode): static
|
||||
{
|
||||
$this->distributionMode = $distributionMode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setName(string $name): static
|
||||
{
|
||||
$this->name = $name;
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enum;
|
||||
|
||||
enum DistributionMode: string
|
||||
{
|
||||
case EVEN = 'even';
|
||||
case PRIORITY = 'priority';
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
namespace App\Tests\Entity;
|
||||
|
||||
use App\Entity\Scenario;
|
||||
use App\Enum\DistributionMode;
|
||||
use App\Tests\Concerns\CreatesUsers;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
|
|
@ -42,58 +41,6 @@ final class ScenarioPersistenceTest extends KernelTestCase
|
|||
self::assertSame('Household Budget', $reloaded->getName());
|
||||
}
|
||||
|
||||
public function testDistributionModeDefaultsToEvenWhenNotExplicitlySet(): void
|
||||
{
|
||||
$scenario = new Scenario();
|
||||
$scenario->setName('Default Mode Scenario');
|
||||
$scenario->setOwner($this->persistOwner());
|
||||
|
||||
$this->em->persist($scenario);
|
||||
$this->em->flush();
|
||||
|
||||
$id = $scenario->getId();
|
||||
|
||||
$this->em->clear();
|
||||
|
||||
$reloaded = $this->em->find(Scenario::class, $id);
|
||||
|
||||
self::assertNotNull($reloaded);
|
||||
self::assertSame(
|
||||
DistributionMode::EVEN,
|
||||
$reloaded->getDistributionMode(),
|
||||
'distribution_mode must default to EVEN when not explicitly set.',
|
||||
);
|
||||
}
|
||||
|
||||
public function testItRoundTripsAnExplicitlySetPriorityDistributionMode(): void
|
||||
{
|
||||
$scenario = new Scenario();
|
||||
$scenario->setName('Priority Mode Scenario');
|
||||
$scenario->setDistributionMode(DistributionMode::PRIORITY);
|
||||
$scenario->setOwner($this->persistOwner());
|
||||
|
||||
$this->em->persist($scenario);
|
||||
$this->em->flush();
|
||||
|
||||
$id = $scenario->getId();
|
||||
|
||||
$this->em->clear();
|
||||
|
||||
$reloaded = $this->em->find(Scenario::class, $id);
|
||||
|
||||
self::assertNotNull($reloaded);
|
||||
self::assertInstanceOf(
|
||||
DistributionMode::class,
|
||||
$reloaded->getDistributionMode(),
|
||||
'distribution_mode must hydrate as a DistributionMode enum instance, not a raw string.',
|
||||
);
|
||||
self::assertSame(
|
||||
DistributionMode::PRIORITY,
|
||||
$reloaded->getDistributionMode(),
|
||||
'An explicitly set PRIORITY distribution_mode must survive a flush + clear + reload round-trip.',
|
||||
);
|
||||
}
|
||||
|
||||
public function testDescriptionRoundTripsWhenSet(): void
|
||||
{
|
||||
$scenario = new Scenario();
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
namespace App\Tests\Enum;
|
||||
|
||||
use App\Enum\BucketAllocationType;
|
||||
use App\Enum\DistributionMode;
|
||||
use App\Enum\StreamFrequency;
|
||||
use App\Enum\StreamType;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
|
@ -22,9 +21,6 @@ final class RemainingEnumsTest extends TestCase
|
|||
|
||||
$this->assertSame('income', StreamType::INCOME->value);
|
||||
$this->assertSame('expense', StreamType::EXPENSE->value);
|
||||
|
||||
$this->assertSame('even', DistributionMode::EVEN->value);
|
||||
$this->assertSame('priority', DistributionMode::PRIORITY->value);
|
||||
}
|
||||
|
||||
public function testBucketAllocationTypeBackingValues(): void
|
||||
|
|
|
|||
|
|
@ -212,6 +212,103 @@ final class ScenarioApiTest extends WebTestCase
|
|||
);
|
||||
}
|
||||
|
||||
public function testPostThenGetOnReturnedIriRoundTripsForTheOwner(): void
|
||||
{
|
||||
$this->login();
|
||||
|
||||
$this->client->request(
|
||||
'POST',
|
||||
'/api/scenarios',
|
||||
server: [
|
||||
'CONTENT_TYPE' => 'application/ld+json',
|
||||
'HTTP_ACCEPT' => 'application/ld+json',
|
||||
],
|
||||
content: json_encode(['name' => 'New Scenario From UI']),
|
||||
);
|
||||
|
||||
$created = json_decode((string) $this->client->getResponse()->getContent(), true);
|
||||
|
||||
self::assertIsArray($created, 'The created resource response must be a JSON-LD object.');
|
||||
self::assertArrayHasKey(
|
||||
'@id',
|
||||
$created,
|
||||
'POST must return the created scenario IRI so the SPA can redirect to its show page.',
|
||||
);
|
||||
|
||||
$iri = $created['@id'];
|
||||
|
||||
$this->client->request('GET', $iri, server: [
|
||||
'HTTP_ACCEPT' => 'application/ld+json',
|
||||
]);
|
||||
|
||||
$response = $this->client->getResponse();
|
||||
|
||||
self::assertSame(
|
||||
200,
|
||||
$response->getStatusCode(),
|
||||
'A GET on the just-created IRI by the same owner must return 200 — the create->redirect->show flow must never land on an owner-scope 404.',
|
||||
);
|
||||
|
||||
$body = json_decode((string) $response->getContent(), true);
|
||||
|
||||
self::assertIsArray($body, 'The item response must be a JSON-LD object.');
|
||||
self::assertSame(
|
||||
$iri,
|
||||
$body['@id'] ?? null,
|
||||
'The show page must resolve the same IRI the create response returned.',
|
||||
);
|
||||
self::assertSame('New Scenario From UI', $body['name'] ?? null);
|
||||
}
|
||||
|
||||
public function testPostWithDescriptionPersistsAndEchoesIt(): void
|
||||
{
|
||||
$this->login();
|
||||
|
||||
$this->client->request(
|
||||
'POST',
|
||||
'/api/scenarios',
|
||||
server: [
|
||||
'CONTENT_TYPE' => 'application/ld+json',
|
||||
'HTTP_ACCEPT' => 'application/ld+json',
|
||||
],
|
||||
content: json_encode([
|
||||
'name' => 'Documented Fund',
|
||||
'description' => 'Money set aside for the kitchen renovation.',
|
||||
]),
|
||||
);
|
||||
|
||||
$response = $this->client->getResponse();
|
||||
|
||||
self::assertSame(
|
||||
201,
|
||||
$response->getStatusCode(),
|
||||
'POST /api/scenarios with name + optional description must return 201 Created.',
|
||||
);
|
||||
|
||||
$body = json_decode((string) $response->getContent(), true);
|
||||
|
||||
self::assertIsArray($body, 'The created resource response must be a JSON-LD object.');
|
||||
self::assertSame(
|
||||
'Money set aside for the kitchen renovation.',
|
||||
$body['description'] ?? null,
|
||||
'The create response must echo the supplied optional description back to the SPA.',
|
||||
);
|
||||
|
||||
$this->em->clear();
|
||||
|
||||
$persisted = $this->em->getRepository(Scenario::class)->findOneBy(['name' => 'Documented Fund']);
|
||||
|
||||
self::assertNotNull(
|
||||
$persisted,
|
||||
'A successful POST with a description must persist the new Scenario.',
|
||||
);
|
||||
self::assertSame(
|
||||
'Money set aside for the kitchen renovation.',
|
||||
$persisted->getDescription(),
|
||||
'The optional description must survive persistence.',
|
||||
);
|
||||
}
|
||||
|
||||
public function testPostWithMissingNameIsRejectedAsUnprocessable(): void
|
||||
{
|
||||
$this->login();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use App\Entity\Bucket;
|
|||
use App\Entity\Scenario;
|
||||
use App\Enum\BucketAllocationType;
|
||||
use App\Enum\BucketType;
|
||||
use App\Enum\DistributionMode;
|
||||
use App\Service\Allocation\AllocateIncome;
|
||||
use App\Service\Allocation\Result;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
|
@ -14,7 +13,7 @@ use PHPUnit\Framework\TestCase;
|
|||
|
||||
/**
|
||||
* Tests for the redesigned allocation engine — single strategy, no distribution
|
||||
* modes. `preview()` no longer accepts a `DistributionMode` parameter at all —
|
||||
* modes. `preview()` does not accept any distribution-mode parameter —
|
||||
* Model C's even-split-on-tie behaviour is intrinsic to the engine, not a mode.
|
||||
*
|
||||
* THE MODEL (type-phased macro-order, priority tiers within each phase):
|
||||
|
|
@ -476,8 +475,7 @@ final class AllocateIncomeTest extends TestCase
|
|||
private function makeScenario(): Scenario
|
||||
{
|
||||
return (new Scenario())
|
||||
->setName('Household Budget')
|
||||
->setDistributionMode(DistributionMode::PRIORITY);
|
||||
->setName('Household Budget');
|
||||
}
|
||||
|
||||
private function makeFixedLimitBucket(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use App\Entity\Bucket;
|
|||
use App\Entity\Scenario;
|
||||
use App\Enum\BucketAllocationType;
|
||||
use App\Enum\BucketType;
|
||||
use App\Enum\DistributionMode;
|
||||
use App\Service\Allocation\Result;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
|
|
@ -78,8 +77,7 @@ final class ResultTest extends TestCase
|
|||
private function makeScenario(): Scenario
|
||||
{
|
||||
return (new Scenario())
|
||||
->setName('Household Budget')
|
||||
->setDistributionMode(DistributionMode::PRIORITY);
|
||||
->setName('Household Budget');
|
||||
}
|
||||
|
||||
private function makeBucket(Scenario $scenario, int $priority, string $name): Bucket
|
||||
|
|
|
|||
Loading…
Reference in a new issue