seed(RoleSeeder::class); $this->admin = User::factory()->create(); $this->admin->assignRole('super_admin'); $this->organisation = Organisation::factory()->create(); $this->orgAdmin = User::factory()->create(); $this->organisation->users()->attach($this->orgAdmin, ['role' => 'org_admin']); $this->orgMember = User::factory()->create(); $this->organisation->users()->attach($this->orgMember, ['role' => 'org_member']); $this->outsider = User::factory()->create(); } // --- INDEX --- public function test_org_admin_can_list_events(): void { Event::factory()->count(3)->create(['organisation_id' => $this->organisation->id]); Sanctum::actingAs($this->orgAdmin); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events"); $response->assertOk(); $this->assertCount(3, $response->json('data')); } public function test_org_member_can_list_events(): void { Event::factory()->count(2)->create(['organisation_id' => $this->organisation->id]); Sanctum::actingAs($this->orgMember); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events"); $response->assertOk(); $this->assertCount(2, $response->json('data')); } public function test_outsider_cannot_list_events(): void { Sanctum::actingAs($this->outsider); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events"); $response->assertForbidden(); } public function test_unauthenticated_user_cannot_list_events(): void { $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events"); $response->assertUnauthorized(); } // --- SHOW --- public function test_org_admin_can_view_event(): 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}"); $response->assertOk() ->assertJson(['data' => ['id' => $event->id]]); } public function test_outsider_cannot_view_event(): void { $event = Event::factory()->create(['organisation_id' => $this->organisation->id]); Sanctum::actingAs($this->outsider); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events/{$event->id}"); $response->assertForbidden(); } // --- STORE --- public function test_org_admin_can_create_event(): void { Sanctum::actingAs($this->orgAdmin); $response = $this->postJson("/api/v1/organisations/{$this->organisation->id}/events", [ 'name' => 'Summer Festival 2026', 'slug' => 'summer-festival-2026', 'start_date' => '2026-07-01', 'end_date' => '2026-07-03', ]); $response->assertCreated() ->assertJson(['data' => ['name' => 'Summer Festival 2026']]); $this->assertDatabaseHas('events', [ 'organisation_id' => $this->organisation->id, 'slug' => 'summer-festival-2026', ]); } public function test_org_member_cannot_create_event(): void { Sanctum::actingAs($this->orgMember); $response = $this->postJson("/api/v1/organisations/{$this->organisation->id}/events", [ 'name' => 'Member Event', 'slug' => 'member-event', 'start_date' => '2026-07-01', 'end_date' => '2026-07-03', ]); $response->assertForbidden(); } public function test_outsider_cannot_create_event(): void { Sanctum::actingAs($this->outsider); $response = $this->postJson("/api/v1/organisations/{$this->organisation->id}/events", [ 'name' => 'Hacked Event', 'slug' => 'hacked-event', 'start_date' => '2026-07-01', 'end_date' => '2026-07-03', ]); $response->assertForbidden(); } public function test_unauthenticated_user_cannot_create_event(): void { $response = $this->postJson("/api/v1/organisations/{$this->organisation->id}/events", [ 'name' => 'Anon Event', 'slug' => 'anon-event', 'start_date' => '2026-07-01', 'end_date' => '2026-07-03', ]); $response->assertUnauthorized(); } public function test_store_with_end_date_before_start_date_returns_422(): void { Sanctum::actingAs($this->orgAdmin); $response = $this->postJson("/api/v1/organisations/{$this->organisation->id}/events", [ 'name' => 'Bad Dates', 'slug' => 'bad-dates', 'start_date' => '2026-07-05', 'end_date' => '2026-07-01', ]); $response->assertUnprocessable() ->assertJsonValidationErrors(['end_date']); } // --- UPDATE --- public function test_org_admin_can_update_event(): void { $event = Event::factory()->create(['organisation_id' => $this->organisation->id]); Sanctum::actingAs($this->orgAdmin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$event->id}", [ 'name' => 'Updated Festival', ]); $response->assertOk() ->assertJson(['data' => ['name' => 'Updated Festival']]); } public function test_event_manager_can_update_event(): void { $event = Event::factory()->create(['organisation_id' => $this->organisation->id]); $eventManager = User::factory()->create(); $this->organisation->users()->attach($eventManager, ['role' => 'org_member']); $event->users()->attach($eventManager, ['role' => 'event_manager']); Sanctum::actingAs($eventManager); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$event->id}", [ 'name' => 'Manager Updated', ]); $response->assertOk() ->assertJson(['data' => ['name' => 'Manager Updated']]); } public function test_org_member_cannot_update_event(): void { $event = Event::factory()->create(['organisation_id' => $this->organisation->id]); Sanctum::actingAs($this->orgMember); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$event->id}", [ 'name' => 'Hacked', ]); $response->assertForbidden(); } public function test_outsider_cannot_update_event(): void { $event = Event::factory()->create(['organisation_id' => $this->organisation->id]); Sanctum::actingAs($this->outsider); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$event->id}", [ 'name' => 'Hacked', ]); $response->assertForbidden(); } // --- CROSS-ORG --- public function test_show_event_from_other_org_is_blocked(): void { $otherOrg = Organisation::factory()->create(); $event = Event::factory()->create(['organisation_id' => $otherOrg->id]); Sanctum::actingAs($this->admin); $response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/events/{$event->id}"); $response->assertForbidden(); } public function test_update_event_from_other_org_is_blocked(): void { $otherOrg = Organisation::factory()->create(); $event = Event::factory()->create(['organisation_id' => $otherOrg->id]); Sanctum::actingAs($this->admin); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$event->id}", [ 'name' => 'Cross-org hack', ]); $response->assertForbidden(); } // --- UNAUTHENTICATED --- public function test_unauthenticated_user_cannot_update_event(): void { $event = Event::factory()->create(['organisation_id' => $this->organisation->id]); $response = $this->putJson("/api/v1/organisations/{$this->organisation->id}/events/{$event->id}", [ 'name' => 'Anon Update', ]); $response->assertUnauthorized(); } }