seed(RoleSeeder::class); $this->organisation = Organisation::factory()->create(); $this->otherOrganisation = Organisation::factory()->create(); $this->event = Event::factory()->create(['organisation_id' => $this->organisation->id]); $this->crowdType = CrowdType::factory()->create(['organisation_id' => $this->organisation->id]); $this->person = Person::factory()->create([ 'event_id' => $this->event->id, 'crowd_type_id' => $this->crowdType->id, ]); $this->orgAdmin = User::factory()->create(); $this->organisation->users()->attach($this->orgAdmin, ['role' => 'org_admin']); $this->outsider = User::factory()->create(); $this->otherOrganisation->users()->attach($this->outsider, ['role' => 'org_admin']); } public function test_upsert_text_value(): void { $field = RegistrationFormField::factory()->textField()->create([ 'event_id' => $this->event->id, ]); Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/persons/{$this->person->id}/field-values", [ 'values' => [ $field->slug => 'Jan Jansen', ], ]); $response->assertOk(); $this->assertDatabaseHas('person_field_values', [ 'person_id' => $this->person->id, 'registration_form_field_id' => $field->id, 'value' => 'Jan Jansen', ]); } public function test_upsert_select_value(): void { $field = RegistrationFormField::factory()->selectField()->create([ 'event_id' => $this->event->id, ]); Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/persons/{$this->person->id}/field-values", [ 'values' => [ $field->slug => 'M', ], ]); $response->assertOk(); $this->assertDatabaseHas('person_field_values', [ 'person_id' => $this->person->id, 'registration_form_field_id' => $field->id, 'value' => 'M', ]); } public function test_upsert_select_invalid_option_rejected(): void { $field = RegistrationFormField::factory()->selectField()->create([ 'event_id' => $this->event->id, ]); Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/persons/{$this->person->id}/field-values", [ 'values' => [ $field->slug => 'XXXXL', ], ]); $response->assertUnprocessable(); } public function test_upsert_multiselect_value(): void { $field = RegistrationFormField::factory()->multiselectField()->create([ 'event_id' => $this->event->id, ]); Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/persons/{$this->person->id}/field-values", [ 'values' => [ $field->slug => ['Vegetarisch', 'Glutenvrij'], ], ]); $response->assertOk(); $this->assertDatabaseHas('person_field_values', [ 'person_id' => $this->person->id, 'registration_form_field_id' => $field->id, ]); $storedValue = \App\Models\PersonFieldValue::where('person_id', $this->person->id) ->where('registration_form_field_id', $field->id) ->first(); $this->assertEquals(['Vegetarisch', 'Glutenvrij'], $storedValue->selected_options); } public function test_upsert_boolean_value(): void { $field = RegistrationFormField::factory()->booleanField()->create([ 'event_id' => $this->event->id, ]); Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/persons/{$this->person->id}/field-values", [ 'values' => [ $field->slug => true, ], ]); $response->assertOk(); $this->assertDatabaseHas('person_field_values', [ 'person_id' => $this->person->id, 'registration_form_field_id' => $field->id, 'value' => '1', ]); } public function test_upsert_required_field_rejects_empty(): void { $field = RegistrationFormField::factory()->create([ 'event_id' => $this->event->id, 'label' => 'Verplicht veld', 'slug' => 'verplicht-veld', 'field_type' => RegistrationFieldType::TEXT, 'is_required' => true, ]); Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/persons/{$this->person->id}/field-values", [ 'values' => [ $field->slug => null, ], ]); $response->assertUnprocessable(); } public function test_upsert_tag_picker_with_valid_tags(): void { $tag1 = PersonTag::factory()->create([ 'organisation_id' => $this->organisation->id, 'name' => 'Tapper', ]); $tag2 = PersonTag::factory()->create([ 'organisation_id' => $this->organisation->id, 'name' => 'EHBO', ]); $field = RegistrationFormField::factory()->tagPickerField()->create([ 'event_id' => $this->event->id, ]); Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/persons/{$this->person->id}/field-values", [ 'values' => [ $field->slug => [$tag1->id, $tag2->id], ], ]); $response->assertOk(); $storedValue = \App\Models\PersonFieldValue::where('person_id', $this->person->id) ->where('registration_form_field_id', $field->id) ->first(); $this->assertContains($tag1->id, $storedValue->selected_options); $this->assertContains($tag2->id, $storedValue->selected_options); } public function test_upsert_tag_picker_with_invalid_tag_rejected(): void { $field = RegistrationFormField::factory()->tagPickerField()->create([ 'event_id' => $this->event->id, ]); Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/persons/{$this->person->id}/field-values", [ 'values' => [ $field->slug => ['01JFAKE00000000000000TAGID'], ], ]); $response->assertUnprocessable(); } public function test_tag_sync_triggered_when_person_has_user_id(): void { $linkedUser = User::factory()->create(); $this->person->user_id = $linkedUser->id; $this->person->save(); $tag = PersonTag::factory()->create([ 'organisation_id' => $this->organisation->id, 'name' => 'Tapper', ]); $field = RegistrationFormField::factory()->tagPickerField()->create([ 'event_id' => $this->event->id, ]); Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/persons/{$this->person->id}/field-values", [ 'values' => [ $field->slug => [$tag->id], ], ]); $response->assertOk(); $this->assertDatabaseHas('user_organisation_tags', [ 'user_id' => $linkedUser->id, 'organisation_id' => $this->organisation->id, 'person_tag_id' => $tag->id, 'source' => 'self_reported', ]); } public function test_tag_sync_not_triggered_when_no_user_id(): void { $this->assertNull($this->person->user_id); $tag = PersonTag::factory()->create([ 'organisation_id' => $this->organisation->id, 'name' => 'Tapper', ]); $field = RegistrationFormField::factory()->tagPickerField()->create([ 'event_id' => $this->event->id, ]); Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/persons/{$this->person->id}/field-values", [ 'values' => [ $field->slug => [$tag->id], ], ]); $response->assertOk(); $this->assertDatabaseMissing('user_organisation_tags', [ 'person_tag_id' => $tag->id, 'source' => 'self_reported', ]); } public function test_index_returns_person_values(): void { $field = RegistrationFormField::factory()->textField()->create([ 'event_id' => $this->event->id, ]); \App\Models\PersonFieldValue::create([ 'person_id' => $this->person->id, 'registration_form_field_id' => $field->id, 'value' => 'Test waarde', ]); Sanctum::actingAs($this->orgAdmin); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/persons/{$this->person->id}/field-values"); $response->assertOk(); $this->assertCount(1, $response->json('data')); } public function test_values_persist_after_field_deleted(): void { $field = RegistrationFormField::factory()->textField()->create([ 'event_id' => $this->event->id, ]); \App\Models\PersonFieldValue::create([ 'person_id' => $this->person->id, 'registration_form_field_id' => $field->id, 'value' => 'Persisted value', ]); $fieldId = $field->id; Sanctum::actingAs($this->orgAdmin); $this->deleteJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/registration-fields/{$field->id}") ->assertNoContent(); // Value persists with FK set to null (nullOnDelete) $this->assertDatabaseHas('person_field_values', [ 'person_id' => $this->person->id, 'registration_form_field_id' => null, 'value' => 'Persisted value', ]); } public function test_cross_org_returns_403(): void { Sanctum::actingAs($this->outsider); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/persons/{$this->person->id}/field-values"); $response->assertForbidden(); } public function test_unauthenticated_returns_401(): void { $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/persons/{$this->person->id}/field-values"); $response->assertUnauthorized(); } }