create(); $user = User::factory()->create(); $person = Person::factory()->create([ 'event_id' => $event->id, 'user_id' => $user->id, 'first_name' => 'Old', 'last_name' => 'Name', ]); $submission = $this->makeSubmission($event, $user, $person, [ 'first_name' => ['value' => 'Jan', 'trust' => 70], 'last_name' => ['value' => 'Jansen', 'trust' => 60], ]); $result = DB::transaction(fn (): BindingPassResult => resolve(FormBindingApplicator::class)->apply($submission)); $this->assertSame(ApplyStatus::COMPLETED, $result->applyStatus()); $this->assertSame('person', $result->provisionedSubjectType); $person->refresh(); $this->assertSame('Jan', $person->first_name); $this->assertSame('Jansen', $person->last_name); } public function test_conflict_resolution_picks_highest_trust(): void { $event = Event::factory()->create(); $user = User::factory()->create(); $person = Person::factory()->create([ 'event_id' => $event->id, 'user_id' => $user->id, 'first_name' => 'Initial', ]); $submission = $this->makeSubmission($event, $user, $person, [ 'first_name__low' => ['value' => 'LowTrust', 'trust' => 30, 'attribute' => 'first_name'], 'first_name__high' => ['value' => 'HighTrust', 'trust' => 90, 'attribute' => 'first_name'], ]); $result = DB::transaction(fn (): BindingPassResult => resolve(FormBindingApplicator::class)->apply($submission)); $this->assertSame(ApplyStatus::COMPLETED, $result->applyStatus()); $person->refresh(); $this->assertSame('HighTrust', $person->first_name); } public function test_missing_person_link_throws_resolution_exception(): void { // Auth user is set, but no Person row links it for this event. $event = Event::factory()->create(); $user = User::factory()->create(); $schema = FormSchema::factory()->create([ 'organisation_id' => $event->organisation_id, 'purpose' => FormPurpose::POST_EVENT_EVALUATION->value, ]); $submission = FormSubmission::factory()->forEvent($event)->create([ 'form_schema_id' => $schema->id, 'submitted_by_user_id' => $user->id, 'subject_type' => null, 'subject_id' => null, ]); $submission->schema_snapshot = ['fields' => []]; $submission->save(); try { DB::transaction(fn () => resolve(FormBindingApplicator::class)->apply($submission->fresh())); $this->fail('Expected PurposeSubjectResolutionException'); } catch (PurposeSubjectResolutionException $e) { $this->assertSame('post_event_evaluation', $e->purposeSlug); $this->assertSame('no_person_for_user', $e->reasonCode); } } public function test_missing_auth_user_throws_resolution_exception(): void { $event = Event::factory()->create(); $schema = FormSchema::factory()->create([ 'organisation_id' => $event->organisation_id, 'purpose' => FormPurpose::POST_EVENT_EVALUATION->value, ]); $submission = FormSubmission::factory()->forEvent($event)->create([ 'form_schema_id' => $schema->id, 'submitted_by_user_id' => null, 'subject_type' => null, 'subject_id' => null, ]); $submission->schema_snapshot = ['fields' => []]; $submission->save(); try { DB::transaction(fn () => resolve(FormBindingApplicator::class)->apply($submission->fresh())); $this->fail('Expected PurposeSubjectResolutionException'); } catch (PurposeSubjectResolutionException $e) { $this->assertSame('post_event_evaluation', $e->purposeSlug); $this->assertSame('no_auth', $e->reasonCode); } } /** * @param array $bindingSpecs */ private function makeSubmission(Event $event, User $user, Person $person, array $bindingSpecs): FormSubmission { $schema = FormSchema::factory()->create([ 'organisation_id' => $event->organisation_id, 'purpose' => FormPurpose::POST_EVENT_EVALUATION->value, ]); $submission = FormSubmission::factory()->forEvent($event)->create([ 'form_schema_id' => $schema->id, 'submitted_by_user_id' => $user->id, 'subject_type' => 'person', 'subject_id' => $person->id, ]); $snapshotFields = []; foreach ($bindingSpecs as $slug => $spec) { $attribute = $spec['attribute'] ?? $slug; $field = FormField::factory()->create([ 'form_schema_id' => $schema->id, 'slug' => $slug, ]); $binding = FormFieldBinding::factory()->forField($field) ->entityOwned('person', $attribute) ->create([ 'merge_strategy' => FormFieldBindingMergeStrategy::Overwrite->value, 'trust_level' => $spec['trust'], ]); $snapshotFields[] = [ 'id' => (string) $field->id, 'slug' => (string) $field->slug, 'sort_order' => (int) $field->sort_order, 'bindings' => [[ 'id' => (string) $binding->id, 'mode' => 'entity_owned', 'entity' => 'person', 'column' => $attribute, 'merge_strategy' => 'overwrite', 'trust_level' => $spec['trust'], 'is_identity_key' => false, ]], ]; $this->writeValue($submission->id, $field->id, $spec['value']); } $submission->schema_snapshot = ['fields' => $snapshotFields]; $submission->save(); return $submission->fresh(); } private function writeValue(string $submissionId, string $fieldId, mixed $value): void { $row = new FormValue; $row->form_submission_id = $submissionId; $row->form_field_id = $fieldId; $row->setAttribute('value', $value); $row->value_anonymised = false; $row->save(); } }