seed(RoleSeeder::class); $this->organisation = Organisation::factory()->create(); $this->orgAdmin = User::factory()->create(); $this->organisation->users()->attach($this->orgAdmin, ['role' => 'org_admin']); } // --- INDEX: top-level only --- public function test_index_shows_only_top_level_events(): void { $festival = Event::factory()->festival()->create([ 'organisation_id' => $this->organisation->id, ]); Event::factory()->subEvent($festival)->create(); Event::factory()->create(['organisation_id' => $this->organisation->id]); Sanctum::actingAs($this->orgAdmin); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events"); $response->assertOk(); $this->assertCount(2, $response->json('data')); $ids = collect($response->json('data'))->pluck('parent_event_id'); $this->assertTrue($ids->every(fn ($v) => $v === null)); } // --- INDEX: include_children --- public function test_index_with_include_children_shows_nested_children(): void { $festival = Event::factory()->festival()->create([ 'organisation_id' => $this->organisation->id, ]); Event::factory()->subEvent($festival)->count(2)->create(); Sanctum::actingAs($this->orgAdmin); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events?include_children=true"); $response->assertOk(); $this->assertCount(1, $response->json('data')); $this->assertCount(2, $response->json('data.0.children')); } // --- INDEX: type filter --- public function test_index_type_filter_works(): void { Event::factory()->festival()->create([ 'organisation_id' => $this->organisation->id, ]); Event::factory()->create(['organisation_id' => $this->organisation->id]); Sanctum::actingAs($this->orgAdmin); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events?type=festival"); $response->assertOk(); $this->assertCount(1, $response->json('data')); $this->assertEquals('festival', $response->json('data.0.event_type')); } // --- STORE: sub-event --- public function test_store_with_parent_event_id_creates_sub_event(): void { $festival = Event::factory()->festival()->create([ 'organisation_id' => $this->organisation->id, ]); Sanctum::actingAs($this->orgAdmin); $response = $this->postJson("/api/v1/organisations/{$this->organisation->id}/events", [ 'name' => 'Dag 1', 'slug' => 'dag-1', 'start_date' => '2026-07-01', 'end_date' => '2026-07-01', 'parent_event_id' => $festival->id, ]); $response->assertCreated(); $this->assertEquals($festival->id, $response->json('data.parent_event_id')); $this->assertTrue($response->json('data.is_sub_event')); } // --- STORE: sub-event cross-org → 422 --- public function test_store_sub_event_of_other_org_returns_422(): void { $otherOrg = Organisation::factory()->create(); $otherEvent = Event::factory()->festival()->create([ 'organisation_id' => $otherOrg->id, ]); Sanctum::actingAs($this->orgAdmin); $response = $this->postJson("/api/v1/organisations/{$this->organisation->id}/events", [ 'name' => 'Cross-org sub', 'slug' => 'cross-org-sub', 'start_date' => '2026-07-01', 'end_date' => '2026-07-01', 'parent_event_id' => $otherEvent->id, ]); $response->assertStatus(422); } // --- CHILDREN endpoint --- public function test_children_endpoint_returns_sub_events(): void { $festival = Event::factory()->festival()->create([ 'organisation_id' => $this->organisation->id, ]); Event::factory()->subEvent($festival)->count(3)->create(); Sanctum::actingAs($this->orgAdmin); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events/{$festival->id}/children"); $response->assertOk(); $this->assertCount(3, $response->json('data')); } public function test_children_of_flat_event_returns_empty_list(): void { $event = Event::factory()->create([ 'organisation_id' => $this->organisation->id, ]); Sanctum::actingAs($this->orgAdmin); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events/{$event->id}/children"); $response->assertOk(); $this->assertCount(0, $response->json('data')); } // --- SHOW: festival with children --- public function test_show_festival_contains_children(): void { $festival = Event::factory()->festival()->create([ 'organisation_id' => $this->organisation->id, ]); Event::factory()->subEvent($festival)->count(2)->create(); Sanctum::actingAs($this->orgAdmin); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events/{$festival->id}"); $response->assertOk(); $this->assertCount(2, $response->json('data.children')); $this->assertEquals(2, $response->json('data.children_count')); } // --- SHOW: sub-event with parent --- public function test_show_sub_event_contains_parent(): void { $festival = Event::factory()->festival()->create([ 'organisation_id' => $this->organisation->id, ]); $subEvent = Event::factory()->subEvent($festival)->create(); Sanctum::actingAs($this->orgAdmin); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events/{$subEvent->id}"); $response->assertOk(); $this->assertEquals($festival->id, $response->json('data.parent.id')); } // --- Helper methods --- public function test_is_festival_helper(): void { $festival = Event::factory()->festival()->create([ 'organisation_id' => $this->organisation->id, ]); $this->assertTrue($festival->isFestival()); $this->assertFalse($festival->isSubEvent()); } public function test_is_sub_event_helper(): void { $festival = Event::factory()->festival()->create([ 'organisation_id' => $this->organisation->id, ]); $subEvent = Event::factory()->subEvent($festival)->create(); $this->assertTrue($subEvent->isSubEvent()); $this->assertFalse($subEvent->isFestival()); } public function test_is_flat_event_helper(): void { $event = Event::factory()->create([ 'organisation_id' => $this->organisation->id, ]); $this->assertTrue($event->isFlatEvent()); $this->assertFalse($event->isFestival()); $this->assertFalse($event->isSubEvent()); } }