seed(RoleSeeder::class); } public function test_unauthenticated_request_returns_401(): void { $org = Organisation::factory()->create(); $response = $this->getJson("/api/v1/organisations/{$org->id}/dashboard-stats"); $response->assertUnauthorized(); } public function test_member_from_other_org_returns_403(): void { $user = User::factory()->create(); $otherOrg = Organisation::factory()->create(); Sanctum::actingAs($user); $response = $this->getJson("/api/v1/organisations/{$otherOrg->id}/dashboard-stats"); $response->assertForbidden(); } public function test_authenticated_member_can_fetch_dashboard_stats(): void { $user = User::factory()->create(); $org = Organisation::factory()->create(); $org->users()->attach($user, ['role' => 'org_member']); Sanctum::actingAs($user); $response = $this->getJson("/api/v1/organisations/{$org->id}/dashboard-stats"); $response->assertOk() ->assertJsonStructure([ 'data' => [ 'members_count', 'events_count', 'events_by_status', 'active_events_count', 'persons_count', 'top_members', 'recent_activity', ], ]); } public function test_stats_return_correct_counts(): void { $user = User::factory()->create(); $org = Organisation::factory()->create(); $org->users()->attach($user, ['role' => 'org_admin']); // 2 extra members -> 3 total $org->users()->attach(User::factory()->create(), ['role' => 'org_member']); $org->users()->attach(User::factory()->create(), ['role' => 'org_member']); // Events: 2 draft, 1 published, 1 buildup, 1 closed = 5 total, 2 active (published + buildup) Event::factory()->count(2)->create(['organisation_id' => $org->id, 'status' => 'draft']); $published = Event::factory()->create(['organisation_id' => $org->id, 'status' => 'published']); Event::factory()->create(['organisation_id' => $org->id, 'status' => 'buildup']); Event::factory()->create(['organisation_id' => $org->id, 'status' => 'closed']); // 3 persons in the published event Person::factory()->count(3)->create(['event_id' => $published->id]); // Noise: another org with its own data — should be filtered out $otherOrg = Organisation::factory()->create(); $otherOrg->users()->attach(User::factory()->create(), ['role' => 'org_admin']); $otherEvent = Event::factory()->create(['organisation_id' => $otherOrg->id, 'status' => 'published']); Person::factory()->create(['event_id' => $otherEvent->id]); Sanctum::actingAs($user); $response = $this->getJson("/api/v1/organisations/{$org->id}/dashboard-stats"); $response->assertOk(); $data = $response->json('data'); $this->assertSame(3, $data['members_count']); $this->assertSame(5, $data['events_count']); $this->assertSame(2, $data['active_events_count']); $this->assertSame(3, $data['persons_count']); $this->assertSame(2, $data['events_by_status']['draft']); $this->assertSame(1, $data['events_by_status']['published']); $this->assertSame(1, $data['events_by_status']['buildup']); $this->assertSame(1, $data['events_by_status']['closed']); $this->assertSame(0, $data['events_by_status']['registration_open']); } public function test_top_members_limited_to_five_and_ordered_by_joined_at(): void { $user = User::factory()->create(); $org = Organisation::factory()->create(); // Attach user first with the earliest join timestamp $org->users()->attach($user, ['role' => 'org_admin']); // Attach 6 more members (total 7) for ($i = 0; $i < 6; $i++) { $org->users()->attach(User::factory()->create(), ['role' => 'org_member']); } Sanctum::actingAs($user); $response = $this->getJson("/api/v1/organisations/{$org->id}/dashboard-stats"); $response->assertOk(); $members = $response->json('data.top_members'); $this->assertCount(5, $members); $this->assertSame($user->id, $members[0]['id']); $this->assertSame('org_admin', $members[0]['role']); } public function test_recent_activity_limited_to_five_entries(): void { $user = User::factory()->create(); $org = Organisation::factory()->create(); $org->users()->attach($user, ['role' => 'org_admin']); Activity::query()->delete(); // Trigger 7 activity log entries on the organisation for ($i = 0; $i < 7; $i++) { $org->update(['name' => "Changed {$i}"]); } Sanctum::actingAs($user); $response = $this->getJson("/api/v1/organisations/{$org->id}/dashboard-stats"); $response->assertOk(); $this->assertCount(5, $response->json('data.recent_activity')); } }