Files
oauth/tests/Feature/Admin/UserCrudTest.php
2026-07-27 17:20:02 +02:00

208 lines
7.7 KiB
PHP

<?php
namespace Tests\Feature\Admin;
use App\Models\User;
use Database\Seeders\RolesAndPermissionsSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class UserCrudTest extends TestCase
{
use RefreshDatabase;
private User $admin;
protected function setUp(): void
{
parent::setUp();
$this->seed(RolesAndPermissionsSeeder::class);
$this->admin = User::factory()->approved()->create();
$this->admin->assignRole('admin');
}
public function test_admin_can_list_approved_users(): void
{
$other = User::factory()->approved()->create();
User::factory()->create(['approved_at' => null]);
$response = $this->actingAs($this->admin)->getJson('/web-api/admin/users');
$response->assertOk();
$emails = collect($response->json())->pluck('email');
$this->assertTrue($emails->contains($this->admin->email));
$this->assertTrue($emails->contains($other->email));
}
public function test_non_admin_cannot_list_users(): void
{
$user = User::factory()->approved()->create();
$this->actingAs($user)->getJson('/web-api/admin/users')->assertForbidden();
}
public function test_admin_can_create_a_user(): void
{
$response = $this->actingAs($this->admin)->postJson('/web-api/admin/users', [
'name' => 'New Person',
'email' => 'newperson@example.com',
'password' => 'password123',
'role' => 'user',
]);
$response->assertCreated();
$response->assertJsonPath('email', 'newperson@example.com');
$response->assertJsonPath('roles.0', 'user');
$created = User::where('email', 'newperson@example.com')->firstOrFail();
$this->assertNotNull($created->approved_at, 'un usuario creado por un admin queda aprobado automáticamente');
$this->assertDatabaseHas('audit_logs', ['action' => 'user.created', 'subject_id' => (string) $created->id]);
}
public function test_create_validates_unique_email_and_known_role(): void
{
$this->actingAs($this->admin)->postJson('/web-api/admin/users', [
'name' => 'Dup',
'email' => $this->admin->email,
'password' => 'password123',
'role' => 'user',
])->assertUnprocessable()->assertJsonValidationErrors('email');
$this->actingAs($this->admin)->postJson('/web-api/admin/users', [
'name' => 'Bad Role',
'email' => 'badrole@example.com',
'password' => 'password123',
'role' => 'superadmin',
])->assertUnprocessable()->assertJsonValidationErrors('role');
}
public function test_admin_can_update_a_users_name_email_role_and_active_state(): void
{
$target = User::factory()->approved()->create(['is_active' => true]);
$target->assignRole('user');
$response = $this->actingAs($this->admin)->putJson("/web-api/admin/users/{$target->id}", [
'name' => 'Renamed',
'email' => 'renamed@example.com',
'is_active' => false,
'role' => 'admin',
]);
$response->assertOk();
$response->assertJsonPath('name', 'Renamed');
$response->assertJsonPath('is_active', false);
$response->assertJsonPath('roles.0', 'admin');
$this->assertDatabaseHas('audit_logs', ['action' => 'user.updated', 'subject_id' => (string) $target->id]);
}
public function test_deactivating_a_user_dispatches_the_user_deactivated_event(): void
{
\Illuminate\Support\Facades\Event::fake([\App\Events\UserDeactivated::class]);
$target = User::factory()->approved()->create(['is_active' => true]);
$target->assignRole('user');
$this->actingAs($this->admin)->putJson("/web-api/admin/users/{$target->id}", [
'name' => $target->name,
'email' => $target->email,
'is_active' => false,
'role' => 'user',
])->assertOk();
\Illuminate\Support\Facades\Event::assertDispatched(\App\Events\UserDeactivated::class);
}
public function test_admin_cannot_remove_their_own_admin_role(): void
{
$response = $this->actingAs($this->admin)->putJson("/web-api/admin/users/{$this->admin->id}", [
'name' => $this->admin->name,
'email' => $this->admin->email,
'role' => 'user',
]);
$response->assertStatus(422);
$this->assertTrue($this->admin->fresh()->hasRole('admin'));
}
public function test_admin_cannot_delete_themselves(): void
{
$this->actingAs($this->admin)
->deleteJson("/web-api/admin/users/{$this->admin->id}")
->assertStatus(422);
$this->assertNull($this->admin->fresh()->deleted_at);
}
public function test_cannot_delete_the_last_admin(): void
{
$secondAdmin = User::factory()->approved()->create();
$secondAdmin->assignRole('admin');
// Con dos admins, borrar uno debe andar.
$this->actingAs($this->admin)
->deleteJson("/web-api/admin/users/{$secondAdmin->id}")
->assertNoContent();
// Ahora $this->admin es el único admin — ni siquiera otro usuario con
// permiso users.delete (pero sin rol admin) puede borrarlo.
$viewer = User::factory()->approved()->create();
$viewer->givePermissionTo('users.delete');
$this->actingAs($viewer)
->deleteJson("/web-api/admin/users/{$this->admin->id}")
->assertStatus(422);
$this->assertNull($this->admin->fresh()->deleted_at);
}
public function test_admin_can_soft_delete_restore_and_force_delete_a_user(): void
{
$target = User::factory()->approved()->create();
$this->actingAs($this->admin)
->deleteJson("/web-api/admin/users/{$target->id}")
->assertNoContent();
$this->assertSoftDeleted('users', ['id' => $target->id]);
$this->assertDatabaseHas('audit_logs', ['action' => 'user.deleted', 'subject_id' => (string) $target->id]);
$this->actingAs($this->admin)
->getJson('/web-api/admin/users/trashed')
->assertOk()
->assertJsonFragment(['email' => $target->email]);
$this->actingAs($this->admin)
->postJson("/web-api/admin/users/{$target->id}/restore")
->assertOk();
$this->assertDatabaseHas('users', ['id' => $target->id, 'deleted_at' => null]);
$this->assertDatabaseHas('audit_logs', ['action' => 'user.restored', 'subject_id' => (string) $target->id]);
$this->actingAs($this->admin)->deleteJson("/web-api/admin/users/{$target->id}")->assertNoContent();
$this->actingAs($this->admin)
->deleteJson("/web-api/admin/users/{$target->id}/force")
->assertNoContent();
$this->assertDatabaseMissing('users', ['id' => $target->id]);
$this->assertDatabaseHas('audit_logs', ['action' => 'user.force_deleted', 'subject_id' => (string) $target->id]);
}
public function test_admin_can_list_and_approve_pending_registrations(): void
{
$pending = User::factory()->create(['approved_at' => null]);
$this->actingAs($this->admin)
->getJson('/web-api/admin/users/pending')
->assertOk()
->assertJsonFragment(['email' => $pending->email]);
$this->actingAs($this->admin)
->postJson("/web-api/admin/users/{$pending->id}/approve")
->assertOk()
->assertJsonPath('email', $pending->email);
$this->assertNotNull($pending->fresh()->approved_at);
$this->assertDatabaseHas('audit_logs', ['action' => 'user.approved', 'subject_id' => (string) $pending->id]);
}
}