seed(RoleSeeder::class); $this->superAdmin = User::factory()->create(); $this->superAdmin->assignRole('super_admin'); $this->organisation = Organisation::factory()->create(); $this->regularUser = User::factory()->create(); $this->organisation->users()->attach($this->regularUser, ['role' => 'org_admin']); } // ─── Index ─────────────────────────────────────────────── public function test_index_returns_all_users_with_organisations(): void { Sanctum::actingAs($this->superAdmin); $response = $this->getJson('/api/v1/admin/users'); $response->assertOk(); $response->assertJsonStructure([ 'data' => [['id', 'first_name', 'last_name', 'full_name', 'email', 'is_super_admin', 'organisations']], ]); // superAdmin + regularUser = 2 $this->assertCount(2, $response->json('data')); } public function test_index_denied_for_non_super_admin(): void { Sanctum::actingAs($this->regularUser); $response = $this->getJson('/api/v1/admin/users'); $response->assertForbidden(); } public function test_search_by_email(): void { $user = User::factory()->create(['email' => 'searchable@crewli.test']); Sanctum::actingAs($this->superAdmin); $response = $this->getJson('/api/v1/admin/users?search=searchable@crewli'); $response->assertOk(); $this->assertCount(1, $response->json('data')); $response->assertJsonPath('data.0.email', 'searchable@crewli.test'); } public function test_filter_by_organisation_id(): void { $otherOrg = Organisation::factory()->create(); $otherUser = User::factory()->create(); $otherOrg->users()->attach($otherUser, ['role' => 'org_member']); Sanctum::actingAs($this->superAdmin); $response = $this->getJson("/api/v1/admin/users?organisation_id={$this->organisation->id}"); $response->assertOk(); $this->assertCount(1, $response->json('data')); $response->assertJsonPath('data.0.id', $this->regularUser->id); } // ─── Show ──────────────────────────────────────────────── public function test_show_returns_user_with_org_memberships(): void { Sanctum::actingAs($this->superAdmin); $response = $this->getJson("/api/v1/admin/users/{$this->regularUser->id}"); $response->assertOk(); $response->assertJsonPath('data.id', $this->regularUser->id); $response->assertJsonPath('data.organisations.0.id', $this->organisation->id); $response->assertJsonPath('data.organisations.0.role', 'org_admin'); } // ─── Update ────────────────────────────────────────────── public function test_update_changes_name_and_email(): void { Sanctum::actingAs($this->superAdmin); $response = $this->putJson("/api/v1/admin/users/{$this->regularUser->id}", [ 'first_name' => 'Updated', 'last_name' => 'Name', 'email' => 'updated@crewli.test', ]); $response->assertOk(); $response->assertJsonPath('data.first_name', 'Updated'); $response->assertJsonPath('data.email', 'updated@crewli.test'); } public function test_update_can_assign_super_admin_role(): void { Sanctum::actingAs($this->superAdmin); $response = $this->putJson("/api/v1/admin/users/{$this->regularUser->id}", [ 'roles' => ['super_admin'], ]); $response->assertOk(); $this->assertTrue($this->regularUser->fresh()->hasRole('super_admin')); } // ─── Destroy ───────────────────────────────────────────── public function test_destroy_soft_deletes_and_revokes_tokens(): void { $this->regularUser->createToken('test-token'); $this->assertDatabaseHas('personal_access_tokens', [ 'tokenable_id' => $this->regularUser->id, ]); Sanctum::actingAs($this->superAdmin); $response = $this->deleteJson("/api/v1/admin/users/{$this->regularUser->id}"); $response->assertNoContent(); $this->assertSoftDeleted('users', ['id' => $this->regularUser->id]); $this->assertDatabaseMissing('personal_access_tokens', [ 'tokenable_id' => $this->regularUser->id, ]); } }