4 - Harden overflow bucket invariants with server-side guards
This commit is contained in:
parent
faff18f82b
commit
d06b859652
4 changed files with 41 additions and 1 deletions
|
|
@ -22,6 +22,11 @@ public function execute(
|
|||
// Validate type + allocation type constraints
|
||||
$this->validateTypeConstraints($type, $allocationType);
|
||||
|
||||
// Enforce one overflow bucket per scenario
|
||||
if ($type === BucketTypeEnum::OVERFLOW && $scenario->buckets()->where('type', BucketTypeEnum::OVERFLOW->value)->exists()) {
|
||||
throw new InvalidArgumentException('A scenario can only have one overflow bucket');
|
||||
}
|
||||
|
||||
// Validate allocation value based on type
|
||||
$this->validateAllocationValue($allocationType, $allocationValue);
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,15 @@ public function update(Request $request, Bucket $bucket): JsonResponse
|
|||
$type = BucketTypeEnum::from($validated['type']);
|
||||
$allocationType = BucketAllocationTypeEnum::from($validated['allocation_type']);
|
||||
|
||||
// Prevent changing overflow bucket's type away from overflow
|
||||
// (changing TO overflow is handled by validateBucketTypeConstraints below)
|
||||
if ($bucket->type === BucketTypeEnum::OVERFLOW && $type !== BucketTypeEnum::OVERFLOW) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => ['type' => ['The overflow bucket\'s type cannot be changed.']],
|
||||
], 422);
|
||||
}
|
||||
|
||||
$constraintError = $this->validateBucketTypeConstraints($type, $allocationType, $bucket->scenario, $bucket);
|
||||
if ($constraintError) {
|
||||
return $constraintError;
|
||||
|
|
@ -115,6 +124,12 @@ public function update(Request $request, Bucket $bucket): JsonResponse
|
|||
*/
|
||||
public function destroy(Bucket $bucket): JsonResponse
|
||||
{
|
||||
if ($bucket->type === BucketTypeEnum::OVERFLOW) {
|
||||
return response()->json([
|
||||
'message' => 'The overflow bucket cannot be deleted.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$scenarioId = $bucket->scenario_id;
|
||||
$deletedPriority = $bucket->priority;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ class BucketFactory extends Factory
|
|||
{
|
||||
public function definition(): array
|
||||
{
|
||||
// Unlimited excluded — use ->overflow() state modifier
|
||||
$allocationType = $this->faker->randomElement([
|
||||
BucketAllocationTypeEnum::FIXED_LIMIT,
|
||||
BucketAllocationTypeEnum::PERCENTAGE,
|
||||
BucketAllocationTypeEnum::UNLIMITED,
|
||||
]);
|
||||
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -151,6 +151,26 @@ public function test_non_overflow_bucket_cannot_use_unlimited_allocation(): void
|
|||
);
|
||||
}
|
||||
|
||||
public function test_cannot_create_second_overflow_bucket(): void
|
||||
{
|
||||
$this->action->execute(
|
||||
$this->scenario,
|
||||
'First Overflow',
|
||||
BucketAllocationTypeEnum::UNLIMITED,
|
||||
BucketTypeEnum::OVERFLOW
|
||||
);
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('A scenario can only have one overflow bucket');
|
||||
|
||||
$this->action->execute(
|
||||
$this->scenario,
|
||||
'Second Overflow',
|
||||
BucketAllocationTypeEnum::UNLIMITED,
|
||||
BucketTypeEnum::OVERFLOW
|
||||
);
|
||||
}
|
||||
|
||||
public function test_want_bucket_cannot_use_unlimited_allocation(): void
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
|
|
|||
Loading…
Reference in a new issue