Files
Verde-Web/tests/Feature/Api/V1/Admin/AdminUserCrudTest.php
admin c8404564fe feat(backend): complete Modules 2 + 3 (geo + user management)
Module 2 — Geographic Data: PSGC tables (regions/provinces/cities/
barangays) with native geometry(polygon|point, 4326) columns; cascading
dropdown endpoints; ST_Contains-based GPS resolution; service-area CRUD
with barangay attach/detach; SamplePsgcSeeder + psgc:import command.

Module 3 — User Management: 5 role-specific profile tables (driver/
helper/scanner/store_partner with verification_status + rejection
reason); profile auto-created on register; admin user CRUD with
filters/pagination, suspend/activate, profile approve/reject;
self-service /me + /me/profile with role-aware validation that blocks
self-approval and resets rejected profiles to pending on resubmit.

69 feature tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 23:59:56 +08:00

171 lines
5.5 KiB
PHP

<?php
namespace Tests\Feature\Api\V1\Admin;
use App\Models\DriverProfile;
use App\Models\ResidentProfile;
use App\Models\User;
use Database\Seeders\RoleSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Sanctum\Sanctum;
use Tests\TestCase;
class AdminUserCrudTest extends TestCase
{
use RefreshDatabase;
private User $admin;
protected function setUp(): void
{
parent::setUp();
$this->seed(RoleSeeder::class);
$this->admin = User::factory()->create([
'role' => User::ROLE_ADMIN,
'status' => User::STATUS_ACTIVE,
]);
}
public function test_admin_can_list_users_with_filters(): void
{
Sanctum::actingAs($this->admin);
User::factory()->count(3)->create(['role' => User::ROLE_RESIDENT]);
User::factory()->count(2)->create(['role' => User::ROLE_DRIVER]);
$response = $this->getJson('/api/v1/admin/users?role=resident');
$response->assertOk();
$items = $response->json('data');
$this->assertGreaterThanOrEqual(3, count($items));
foreach ($items as $u) {
$this->assertSame('resident', $u['role']);
}
}
public function test_admin_can_search_users_by_name_or_email(): void
{
Sanctum::actingAs($this->admin);
User::factory()->create(['email' => 'unique-find@example.com']);
$response = $this->getJson('/api/v1/admin/users?q=unique-find');
$response->assertOk()
->assertJsonFragment(['email' => 'unique-find@example.com']);
}
public function test_admin_can_show_user_with_profile(): void
{
Sanctum::actingAs($this->admin);
$user = User::factory()->create(['role' => User::ROLE_RESIDENT]);
ResidentProfile::create(['user_id' => $user->id, 'occupation' => 'Teacher']);
$response = $this->getJson("/api/v1/admin/users/{$user->uuid}");
$response->assertOk()
->assertJsonPath('data.id', $user->uuid)
->assertJsonPath('data.profile.occupation', 'Teacher');
}
public function test_admin_can_update_user(): void
{
Sanctum::actingAs($this->admin);
$user = User::factory()->create();
$response = $this->patchJson("/api/v1/admin/users/{$user->uuid}", [
'first_name' => 'Updated',
'preferred_language' => 'tl',
]);
$response->assertOk()
->assertJsonPath('data.first_name', 'Updated')
->assertJsonPath('data.preferred_language', 'tl');
}
public function test_admin_can_suspend_and_activate_user(): void
{
Sanctum::actingAs($this->admin);
$user = User::factory()->create(['status' => User::STATUS_ACTIVE]);
$this->postJson("/api/v1/admin/users/{$user->uuid}/suspend")
->assertOk()
->assertJsonPath('data.status', User::STATUS_SUSPENDED);
$this->postJson("/api/v1/admin/users/{$user->uuid}/activate")
->assertOk()
->assertJsonPath('data.status', User::STATUS_ACTIVE);
}
public function test_admin_cannot_suspend_other_admin(): void
{
Sanctum::actingAs($this->admin);
$otherAdmin = User::factory()->create(['role' => User::ROLE_ADMIN]);
$response = $this->postJson("/api/v1/admin/users/{$otherAdmin->uuid}/suspend");
$response->assertStatus(422);
}
public function test_admin_can_soft_delete_user(): void
{
Sanctum::actingAs($this->admin);
$user = User::factory()->create(['role' => User::ROLE_RESIDENT]);
$response = $this->deleteJson("/api/v1/admin/users/{$user->uuid}");
$response->assertOk();
$this->assertSoftDeleted('users', ['id' => $user->id]);
}
public function test_admin_can_approve_driver_profile(): void
{
Sanctum::actingAs($this->admin);
$driver = User::factory()->create([
'role' => User::ROLE_DRIVER,
'status' => User::STATUS_PENDING,
]);
DriverProfile::create(['user_id' => $driver->id, 'license_number' => 'D-1234']);
$response = $this->postJson("/api/v1/admin/users/{$driver->uuid}/approve-profile");
$response->assertOk()
->assertJsonPath('data.status', User::STATUS_ACTIVE)
->assertJsonPath('data.profile.verification_status', 'approved');
$this->assertDatabaseHas('driver_profiles', [
'user_id' => $driver->id,
'verification_status' => 'approved',
]);
}
public function test_admin_can_reject_driver_profile_with_reason(): void
{
Sanctum::actingAs($this->admin);
$driver = User::factory()->create(['role' => User::ROLE_DRIVER]);
DriverProfile::create(['user_id' => $driver->id, 'license_number' => 'D-9999']);
$response = $this->postJson("/api/v1/admin/users/{$driver->uuid}/reject-profile", [
'reason' => 'License photo unreadable',
]);
$response->assertOk()
->assertJsonPath('data.profile.verification_status', 'rejected')
->assertJsonPath('data.profile.rejection_reason', 'License photo unreadable');
}
public function test_resident_blocked_from_admin_users(): void
{
$resident = User::factory()->create([
'role' => User::ROLE_RESIDENT,
'status' => User::STATUS_ACTIVE,
]);
Sanctum::actingAs($resident);
$this->getJson('/api/v1/admin/users')->assertStatus(403);
}
public function test_unauthed_blocked_from_admin_users(): void
{
$this->getJson('/api/v1/admin/users')->assertStatus(401);
}
}