feat(form-builder): add ApplyStatus, DismissalReasonType, BindingTargetType enums (WS-6)

DismissalReasonType has six values; manually_resolved is intentionally
absent because Resolve and Dismiss are separate workflows (RFC V2).

Refs: RFC-WS-6.md §3 (Q4 partial-status separation), §4 (V2 dismiss enum)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-25 22:36:10 +02:00
parent c033dc6cd2
commit 447511634d
6 changed files with 213 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Enums\FormBuilder;
use App\Enums\FormBuilder\ApplyStatus;
use PHPUnit\Framework\TestCase;
final class ApplyStatusTest extends TestCase
{
public function test_has_four_cases_with_stable_values(): void
{
$values = array_map(fn (ApplyStatus $case) => $case->value, ApplyStatus::cases());
sort($values);
$this->assertSame(['completed', 'failed', 'partial', 'pending'], $values);
}
public function test_is_terminal_truth_table(): void
{
$this->assertFalse(ApplyStatus::PENDING->isTerminal());
$this->assertTrue(ApplyStatus::COMPLETED->isTerminal());
$this->assertTrue(ApplyStatus::PARTIAL->isTerminal());
$this->assertTrue(ApplyStatus::FAILED->isTerminal());
}
public function test_is_open_truth_table(): void
{
$this->assertTrue(ApplyStatus::PENDING->isOpen());
$this->assertFalse(ApplyStatus::COMPLETED->isOpen());
$this->assertTrue(ApplyStatus::PARTIAL->isOpen());
$this->assertTrue(ApplyStatus::FAILED->isOpen());
}
public function test_label_returns_non_empty_dutch_string_for_each_case(): void
{
foreach (ApplyStatus::cases() as $case) {
$this->assertNotSame('', $case->label());
}
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Enums\FormBuilder;
use App\Enums\FormBuilder\BindingTargetType;
use PHPUnit\Framework\TestCase;
final class BindingTargetTypeTest extends TestCase
{
public function test_has_three_cases_with_stable_values(): void
{
$values = array_map(fn (BindingTargetType $case) => $case->value, BindingTargetType::cases());
sort($values);
$this->assertSame(['collection', 'relation', 'scalar'], $values);
}
public function test_from_string_round_trip(): void
{
$this->assertSame(BindingTargetType::SCALAR, BindingTargetType::from('scalar'));
$this->assertSame(BindingTargetType::COLLECTION, BindingTargetType::from('collection'));
$this->assertSame(BindingTargetType::RELATION, BindingTargetType::from('relation'));
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Enums\FormBuilder;
use App\Enums\FormBuilder\DismissalReasonType;
use PHPUnit\Framework\TestCase;
final class DismissalReasonTypeTest extends TestCase
{
public function test_has_six_cases_with_stable_values(): void
{
$values = array_map(fn (DismissalReasonType $case) => $case->value, DismissalReasonType::cases());
sort($values);
$this->assertSame([
'binding_removed',
'data_quality_issue',
'duplicate_submission',
'other',
'schema_deleted',
'target_entity_deleted',
], $values);
}
public function test_manually_resolved_is_intentionally_absent(): void
{
$values = array_map(fn (DismissalReasonType $case) => $case->value, DismissalReasonType::cases());
$this->assertNotContains('manually_resolved', $values);
}
public function test_requires_note_only_for_other(): void
{
$this->assertTrue(DismissalReasonType::OTHER->requiresNote());
foreach (DismissalReasonType::cases() as $case) {
if ($case === DismissalReasonType::OTHER) {
continue;
}
$this->assertFalse($case->requiresNote(), "{$case->value} must not require a note");
}
}
public function test_label_returns_non_empty_dutch_string_for_each_case(): void
{
foreach (DismissalReasonType::cases() as $case) {
$this->assertNotSame('', $case->label());
}
}
}