create(); $schema = FormSchema::factory()->create(['organisation_id' => $org->id]); $field = FormField::factory()->create(['form_schema_id' => $schema->id]); FormFieldValidationRule::factory() ->forField($field) ->ofType(FormFieldValidationRuleType::MinLength, ['value' => 2]) ->create(); FormFieldValidationRule::factory() ->forField($field) ->ofType(FormFieldValidationRuleType::MaxLength, ['value' => 40]) ->create(); $rules = $field->fresh()->validationRules; $this->assertCount(2, $rules); $types = $rules->pluck('rule_type.value')->sort()->values()->all(); $this->assertSame(['max_length', 'min_length'], $types); } public function test_library_morph_many_validation_rules_loads_all(): void { $org = Organisation::factory()->create(); $library = FormFieldLibrary::factory()->create(['organisation_id' => $org->id]); FormFieldValidationRule::factory() ->forLibrary($library) ->ofType(FormFieldValidationRuleType::Regex, ['pattern' => '/^[0-9]+$/']) ->create(); $rules = $library->fresh()->validationRules; $this->assertCount(1, $rules); $this->assertSame(FormFieldValidationRuleType::Regex, $rules->first()->rule_type); $this->assertSame(['pattern' => '/^[0-9]+$/'], $rules->first()->parameters); } public function test_owner_morphto_returns_correct_concrete_model(): void { $org = Organisation::factory()->create(); $schema = FormSchema::factory()->create(['organisation_id' => $org->id]); $field = FormField::factory()->create(['form_schema_id' => $schema->id]); $library = FormFieldLibrary::factory()->create(['organisation_id' => $org->id]); $fieldRule = FormFieldValidationRule::factory()->forField($field)->create(); $libraryRule = FormFieldValidationRule::factory()->forLibrary($library)->create(); $this->assertInstanceOf(FormField::class, $fieldRule->fresh()->owner); $this->assertSame($field->id, $fieldRule->fresh()->owner->id); $this->assertInstanceOf(FormFieldLibrary::class, $libraryRule->fresh()->owner); $this->assertSame($library->id, $libraryRule->fresh()->owner->id); } public function test_morph_aliases_form_field_and_form_field_library_are_registered(): void { // Morph-map alignment guard — both aliases need to resolve for the // polymorphic `owner` relation on the validation-rule rows. Reuses // the aliases WS-5a registered for bindings; this test protects // against an accidental rename in AppServiceProvider. $morphMap = Relation::morphMap(); $this->assertArrayHasKey('form_field', $morphMap); $this->assertSame(FormField::class, $morphMap['form_field']); $this->assertArrayHasKey('form_field_library', $morphMap); $this->assertSame(FormFieldLibrary::class, $morphMap['form_field_library']); } public function test_parameters_roundtrip_through_json_cast(): void { $org = Organisation::factory()->create(); $schema = FormSchema::factory()->create(['organisation_id' => $org->id]); $field = FormField::factory()->create(['form_schema_id' => $schema->id]); $rule = FormFieldValidationRule::factory() ->forField($field) ->ofType(FormFieldValidationRuleType::AllowedMimeTypes, [ 'mime_types' => ['image/jpeg', 'image/png'], ]) ->create(); $fresh = $rule->fresh(); $this->assertSame( ['mime_types' => ['image/jpeg', 'image/png']], $fresh->parameters, ); $this->assertSame( FormFieldValidationRuleType::AllowedMimeTypes, $fresh->rule_type, ); } }