test(form-builder): cover purpose registry and morph-map alignment

- PurposeRegistryTest: all seven purposes load with expected shape;
  `get()` throws PurposeNotFoundException on unknown slug;
  `allSubjectTypes()` returns exactly [artist, company, person, user];
  `publicAccessibleSlugs()` is only `[event_registration]`.
- PurposeSchemaLifecycleTest: data-provider-driven create → publish
  for all seven purposes; negative tests for event_registration (three
  missing bindings) and supplier_intake (company.name missing); partial
  binding test reports only the missing subset.
- CustomPurposeEscapeRemovedTest: column gone, config file gone,
  FormPurpose::CUSTOM gone, store endpoint rejects `'custom'`, resource
  payload omits the field.
- SubjectTypeRegistryConsolidationTest: submission validation accepts
  registry subject types, rejects everything else including the legacy
  `event` alias that used to be allowed.
- MorphMapAlignmentTest: compile-time guard that every
  PurposeRegistry::allSubjectTypes() alias appears in the morph-map and
  in AppServiceProvider::PURPOSE_SUBJECT_FQCN.
- FormPurposeTest rewritten to cover the seven v1.0 cases and the
  registry-delegation helpers (now extends Tests\TestCase for the
  container).
- Public/listener tests swap the removed PUBLIC_RSVP / PUBLIC_COMPLAINT
  / FEEDBACK references for valid v1.0 purposes, preserving their
  negative-path assertions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 14:36:09 +02:00
parent a71201f4d3
commit 55ba4f24c0
13 changed files with 555 additions and 82 deletions

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace Tests\Feature\FormBuilder;
use App\FormBuilder\Purposes\PurposeRegistry;
use Illuminate\Database\Eloquent\Relations\Relation;
use Tests\TestCase;
/**
* Compile-time guard: PurposeRegistry subject types MUST be registered in
* the morph-map; otherwise polymorphic writes to form_submissions would
* blow up at runtime. See ARCH-CONSOLIDATION-ADDENDUM-2026-04-24.md Q6.
*
* The reverse direction is NOT asserted end-to-end: the morph-map also
* contains non-purpose domain types (owner_types, activity-log subjects)
* and framework entries those are intentionally broader.
*/
final class MorphMapAlignmentTest extends TestCase
{
public function test_every_purpose_subject_type_is_in_morph_map(): void
{
$registry = $this->app->make(PurposeRegistry::class);
$morphMap = Relation::morphMap();
foreach ($registry->allSubjectTypes() as $alias) {
$this->assertArrayHasKey(
$alias,
$morphMap,
"Purpose subject_type '{$alias}' is declared in PurposeRegistry but "
."missing from Relation::morphMap() in AppServiceProvider. "
."Add it to the 'Domain subject types' block."
);
}
}
public function test_domain_subject_types_block_matches_registry_exactly(): void
{
$registry = $this->app->make(PurposeRegistry::class);
$registryAliases = $registry->allSubjectTypes();
// AppServiceProvider::PURPOSE_SUBJECT_FQCN is the single source of
// truth for which aliases the domain-subject-types block can emit.
// If the registry adds an alias not present in that lookup, boot()
// would throw — but we also assert it here for fast feedback.
$reflection = new \ReflectionClass(\App\Providers\AppServiceProvider::class);
$lookup = $reflection->getConstant('PURPOSE_SUBJECT_FQCN');
$this->assertIsArray($lookup);
foreach ($registryAliases as $alias) {
$this->assertArrayHasKey(
$alias,
$lookup,
"Purpose subject_type '{$alias}' has no FQCN in "
."AppServiceProvider::PURPOSE_SUBJECT_FQCN. Register it there."
);
}
}
}