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>
171 lines
5.5 KiB
PHP
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);
|
|
}
|
|
}
|