seed(RoleSeeder::class); $this->organisation = Organisation::factory()->create(); $this->otherOrganisation = Organisation::factory()->create(); $this->festival = Event::factory()->festival()->create([ 'organisation_id' => $this->organisation->id, ]); $this->subEvent = Event::factory()->subEvent($this->festival)->create(); $this->sectionA = FestivalSection::factory()->create([ 'event_id' => $this->subEvent->id, 'name' => 'Horeca', ]); $this->sectionB = FestivalSection::factory()->create([ 'event_id' => $this->subEvent->id, 'name' => 'Backstage', ]); $this->sectionC = FestivalSection::factory()->create([ 'event_id' => $this->subEvent->id, 'name' => 'Techniek', ]); $this->crossEventSection = FestivalSection::factory()->crossEvent()->create([ 'event_id' => $this->festival->id, 'name' => 'Security', ]); $crowdType = CrowdType::factory()->create(['organisation_id' => $this->organisation->id]); $this->person = Person::factory()->create([ 'event_id' => $this->subEvent->id, 'crowd_type_id' => $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_replace_preferences_happy_path(): void { Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->subEvent->id}/persons/{$this->person->id}/section-preferences", [ 'preferences' => [ ['festival_section_id' => $this->sectionA->id, 'priority' => 1], ['festival_section_id' => $this->sectionB->id, 'priority' => 2], ['festival_section_id' => $this->sectionC->id, 'priority' => 3], ], ]); $response->assertOk(); $this->assertDatabaseHas('person_section_preferences', [ 'person_id' => $this->person->id, 'festival_section_id' => $this->sectionA->id, 'priority' => 1, ]); $this->assertDatabaseHas('person_section_preferences', [ 'person_id' => $this->person->id, 'festival_section_id' => $this->sectionB->id, 'priority' => 2, ]); $this->assertDatabaseHas('person_section_preferences', [ 'person_id' => $this->person->id, 'festival_section_id' => $this->sectionC->id, 'priority' => 3, ]); } public function test_replace_deletes_old_and_creates_new(): void { Sanctum::actingAs($this->orgAdmin); // First: set 2 preferences $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->subEvent->id}/persons/{$this->person->id}/section-preferences", [ 'preferences' => [ ['festival_section_id' => $this->sectionA->id, 'priority' => 1], ['festival_section_id' => $this->sectionB->id, 'priority' => 2], ], ])->assertOk(); $this->assertEquals(2, PersonSectionPreference::where('person_id', $this->person->id)->count()); // Second: replace with 1 different preference $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->subEvent->id}/persons/{$this->person->id}/section-preferences", [ 'preferences' => [ ['festival_section_id' => $this->sectionC->id, 'priority' => 1], ], ])->assertOk(); $this->assertEquals(1, PersonSectionPreference::where('person_id', $this->person->id)->count()); $this->assertDatabaseHas('person_section_preferences', [ 'person_id' => $this->person->id, 'festival_section_id' => $this->sectionC->id, 'priority' => 1, ]); $this->assertDatabaseMissing('person_section_preferences', [ 'person_id' => $this->person->id, 'festival_section_id' => $this->sectionA->id, ]); } public function test_priority_must_be_unique(): void { Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->subEvent->id}/persons/{$this->person->id}/section-preferences", [ 'preferences' => [ ['festival_section_id' => $this->sectionA->id, 'priority' => 1], ['festival_section_id' => $this->sectionB->id, 'priority' => 1], ], ]); $response->assertUnprocessable(); } public function test_priority_range_1_to_5(): void { Sanctum::actingAs($this->orgAdmin); // Priority 0 should fail $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->subEvent->id}/persons/{$this->person->id}/section-preferences", [ 'preferences' => [ ['festival_section_id' => $this->sectionA->id, 'priority' => 0], ], ]); $response->assertUnprocessable(); // Priority 6 should fail $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->subEvent->id}/persons/{$this->person->id}/section-preferences", [ 'preferences' => [ ['festival_section_id' => $this->sectionA->id, 'priority' => 6], ], ]); $response->assertUnprocessable(); } public function test_max_5_preferences(): void { // Create 3 more sections to have 6 total $sectionD = FestivalSection::factory()->create(['event_id' => $this->subEvent->id, 'name' => 'Catering']); $sectionE = FestivalSection::factory()->create(['event_id' => $this->subEvent->id, 'name' => 'Logistiek']); $sectionF = FestivalSection::factory()->create(['event_id' => $this->subEvent->id, 'name' => 'Opbouw']); Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->subEvent->id}/persons/{$this->person->id}/section-preferences", [ 'preferences' => [ ['festival_section_id' => $this->sectionA->id, 'priority' => 1], ['festival_section_id' => $this->sectionB->id, 'priority' => 2], ['festival_section_id' => $this->sectionC->id, 'priority' => 3], ['festival_section_id' => $sectionD->id, 'priority' => 4], ['festival_section_id' => $sectionE->id, 'priority' => 5], ['festival_section_id' => $sectionF->id, 'priority' => 6], ], ]); $response->assertUnprocessable(); } public function test_section_must_belong_to_event(): void { $otherEvent = Event::factory()->create(['organisation_id' => $this->organisation->id]); $otherSection = FestivalSection::factory()->create([ 'event_id' => $otherEvent->id, 'name' => 'Andere sectie', ]); Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->subEvent->id}/persons/{$this->person->id}/section-preferences", [ 'preferences' => [ ['festival_section_id' => $otherSection->id, 'priority' => 1], ], ]); $response->assertUnprocessable(); } public function test_cross_event_section_from_parent_accepted(): void { Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->subEvent->id}/persons/{$this->person->id}/section-preferences", [ 'preferences' => [ ['festival_section_id' => $this->crossEventSection->id, 'priority' => 1], ], ]); $response->assertOk(); $this->assertDatabaseHas('person_section_preferences', [ 'person_id' => $this->person->id, 'festival_section_id' => $this->crossEventSection->id, 'priority' => 1, ]); } public function test_index_returns_preferences(): void { PersonSectionPreference::create([ 'person_id' => $this->person->id, 'festival_section_id' => $this->sectionA->id, 'priority' => 1, ]); PersonSectionPreference::create([ 'person_id' => $this->person->id, 'festival_section_id' => $this->sectionB->id, 'priority' => 2, ]); Sanctum::actingAs($this->orgAdmin); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->subEvent->id}/persons/{$this->person->id}/section-preferences"); $response->assertOk(); $this->assertCount(2, $response->json('data')); } public function test_cross_org_returns_403(): void { Sanctum::actingAs($this->outsider); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->subEvent->id}/persons/{$this->person->id}/section-preferences"); $response->assertForbidden(); } public function test_unauthenticated_returns_401(): void { $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->subEvent->id}/persons/{$this->person->id}/section-preferences"); $response->assertUnauthorized(); } }