buckets/tests/Unit/Actions/CreateBucketActionTest.php
2025-12-29 23:32:05 +01:00

269 lines
No EOL
8.8 KiB
PHP

<?php
namespace Tests\Unit\Actions;
use App\Actions\CreateBucketAction;
use App\Models\Bucket;
use App\Models\Scenario;
use Illuminate\Foundation\Testing\RefreshDatabase;
use InvalidArgumentException;
use Tests\TestCase;
class CreateBucketActionTest extends TestCase
{
use RefreshDatabase;
private CreateBucketAction $action;
private Scenario $scenario;
protected function setUp(): void
{
parent::setUp();
$this->action = new CreateBucketAction();
$this->scenario = Scenario::factory()->create();
}
public function test_can_create_fixed_limit_bucket(): void
{
$bucket = $this->action->execute(
$this->scenario,
'Test Bucket',
Bucket::TYPE_FIXED_LIMIT,
1000.00
);
$this->assertInstanceOf(Bucket::class, $bucket);
$this->assertEquals('Test Bucket', $bucket->name);
$this->assertEquals(Bucket::TYPE_FIXED_LIMIT, $bucket->allocation_type);
$this->assertEquals(1000.00, $bucket->allocation_value);
$this->assertEquals(1, $bucket->priority);
$this->assertEquals(1, $bucket->sort_order);
$this->assertEquals($this->scenario->id, $bucket->scenario_id);
}
public function test_can_create_percentage_bucket(): void
{
$bucket = $this->action->execute(
$this->scenario,
'Percentage Bucket',
Bucket::TYPE_PERCENTAGE,
25.5
);
$this->assertEquals(Bucket::TYPE_PERCENTAGE, $bucket->allocation_type);
$this->assertEquals(25.5, $bucket->allocation_value);
}
public function test_can_create_unlimited_bucket(): void
{
$bucket = $this->action->execute(
$this->scenario,
'Unlimited Bucket',
Bucket::TYPE_UNLIMITED
);
$this->assertEquals(Bucket::TYPE_UNLIMITED, $bucket->allocation_type);
$this->assertNull($bucket->allocation_value);
}
public function test_unlimited_bucket_ignores_allocation_value(): void
{
$bucket = $this->action->execute(
$this->scenario,
'Unlimited Bucket',
Bucket::TYPE_UNLIMITED,
999.99 // This should be ignored and set to null
);
$this->assertEquals(Bucket::TYPE_UNLIMITED, $bucket->allocation_type);
$this->assertNull($bucket->allocation_value);
}
public function test_priority_auto_increments_when_not_specified(): void
{
$bucket1 = $this->action->execute(
$this->scenario,
'First Bucket',
Bucket::TYPE_FIXED_LIMIT,
100
);
$bucket2 = $this->action->execute(
$this->scenario,
'Second Bucket',
Bucket::TYPE_FIXED_LIMIT,
200
);
$this->assertEquals(1, $bucket1->priority);
$this->assertEquals(2, $bucket2->priority);
}
public function test_can_specify_custom_priority(): void
{
$bucket = $this->action->execute(
$this->scenario,
'Priority Bucket',
Bucket::TYPE_FIXED_LIMIT,
100,
5
);
$this->assertEquals(5, $bucket->priority);
}
public function test_existing_priorities_are_shifted_when_inserting(): void
{
// Create initial buckets
$bucket1 = $this->action->execute($this->scenario, 'Bucket 1', Bucket::TYPE_FIXED_LIMIT, 100, 1);
$bucket2 = $this->action->execute($this->scenario, 'Bucket 2', Bucket::TYPE_FIXED_LIMIT, 200, 2);
$bucket3 = $this->action->execute($this->scenario, 'Bucket 3', Bucket::TYPE_FIXED_LIMIT, 300, 3);
// Insert a bucket at priority 2
$newBucket = $this->action->execute($this->scenario, 'New Bucket', Bucket::TYPE_FIXED_LIMIT, 150, 2);
// Refresh models from database
$bucket1->refresh();
$bucket2->refresh();
$bucket3->refresh();
// Check that priorities were shifted correctly
$this->assertEquals(1, $bucket1->priority);
$this->assertEquals(2, $newBucket->priority);
$this->assertEquals(3, $bucket2->priority); // Shifted from 2 to 3
$this->assertEquals(4, $bucket3->priority); // Shifted from 3 to 4
}
public function test_throws_exception_for_invalid_allocation_type(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid allocation type: invalid_type');
$this->action->execute(
$this->scenario,
'Test Bucket',
'invalid_type',
100
);
}
public function test_throws_exception_for_fixed_limit_without_allocation_value(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Fixed limit buckets require an allocation value');
$this->action->execute(
$this->scenario,
'Test Bucket',
Bucket::TYPE_FIXED_LIMIT,
null
);
}
public function test_throws_exception_for_negative_fixed_limit_value(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Fixed limit allocation value must be non-negative');
$this->action->execute(
$this->scenario,
'Test Bucket',
Bucket::TYPE_FIXED_LIMIT,
-100
);
}
public function test_throws_exception_for_percentage_without_allocation_value(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Percentage buckets require an allocation value');
$this->action->execute(
$this->scenario,
'Test Bucket',
Bucket::TYPE_PERCENTAGE,
null
);
}
public function test_throws_exception_for_percentage_below_minimum(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Percentage allocation value must be between 0.01 and 100');
$this->action->execute(
$this->scenario,
'Test Bucket',
Bucket::TYPE_PERCENTAGE,
0.005
);
}
public function test_throws_exception_for_percentage_above_maximum(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Percentage allocation value must be between 0.01 and 100');
$this->action->execute(
$this->scenario,
'Test Bucket',
Bucket::TYPE_PERCENTAGE,
101
);
}
public function test_throws_exception_for_negative_priority(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Priority must be at least 1');
$this->action->execute(
$this->scenario,
'Test Bucket',
Bucket::TYPE_FIXED_LIMIT,
100,
0
);
}
public function test_create_default_buckets(): void
{
$this->action->createDefaultBuckets($this->scenario);
$buckets = $this->scenario->buckets()->orderBy('priority')->get();
$this->assertCount(3, $buckets);
// Monthly Expenses
$this->assertEquals('Monthly Expenses', $buckets[0]->name);
$this->assertEquals(1, $buckets[0]->priority);
$this->assertEquals(Bucket::TYPE_FIXED_LIMIT, $buckets[0]->allocation_type);
$this->assertEquals(0, $buckets[0]->allocation_value);
// Emergency Fund
$this->assertEquals('Emergency Fund', $buckets[1]->name);
$this->assertEquals(2, $buckets[1]->priority);
$this->assertEquals(Bucket::TYPE_FIXED_LIMIT, $buckets[1]->allocation_type);
$this->assertEquals(0, $buckets[1]->allocation_value);
// Investments
$this->assertEquals('Investments', $buckets[2]->name);
$this->assertEquals(3, $buckets[2]->priority);
$this->assertEquals(Bucket::TYPE_UNLIMITED, $buckets[2]->allocation_type);
$this->assertNull($buckets[2]->allocation_value);
}
public function test_creates_buckets_in_database_transaction(): void
{
// This test ensures database consistency by creating multiple buckets
// and verifying they all exist with correct priorities
$this->action->execute($this->scenario, 'Bucket 1', Bucket::TYPE_FIXED_LIMIT, 100, 1);
$this->action->execute($this->scenario, 'Bucket 2', Bucket::TYPE_FIXED_LIMIT, 200, 1); // Insert at priority 1
// Both buckets should exist with correct priorities
$buckets = $this->scenario->buckets()->orderBy('priority')->get();
$this->assertCount(2, $buckets);
$this->assertEquals('Bucket 2', $buckets[0]->name); // New bucket at priority 1
$this->assertEquals('Bucket 1', $buckets[1]->name); // Original bucket shifted to priority 2
}
}