This commit is contained in:
Saufi
2026-06-03 08:51:22 +08:00
commit a14d43fe34
347 changed files with 38197 additions and 0 deletions

View File

@@ -0,0 +1,211 @@
<?php
namespace Tests\Feature;
use App\Models\Application;
use App\Models\ApplicationDocument;
use App\Models\Position;
use App\Models\PusatMengundi;
use App\Models\StaffAssignment;
use App\Models\User;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;
class DocumentDownloadAuthorizationTest extends TestCase
{
use RefreshDatabase;
// ── Admin path ──────────────────────────────────────────────────────
public function test_admin_can_download_ic_document(): void
{
Storage::fake('local');
$this->seed(DatabaseSeeder::class);
$admin = User::query()->where('email', 'admin@prn.local')->firstOrFail();
$this->actingAs($admin)
->get(route('admin.documents.download', $this->document('ic_document')))
->assertOk();
}
public function test_admin_can_download_bank_statement(): void
{
Storage::fake('local');
$this->seed(DatabaseSeeder::class);
$admin = User::query()->where('email', 'admin@prn.local')->firstOrFail();
$this->actingAs($admin)
->get(route('admin.documents.download', $this->document('bank_statement')))
->assertOk();
}
public function test_unauthorized_role_cannot_access_admin_download_route(): void
{
Storage::fake('local');
$this->seed(DatabaseSeeder::class);
$ktm = User::query()->where('email', 'ktm@prn.local')->firstOrFail();
$this->actingAs($ktm)
->get(route('admin.documents.download', $this->document('bank_statement')))
->assertForbidden();
}
// ── Finance path ─────────────────────────────────────────────────────
public function test_finance_can_download_bank_statement(): void
{
Storage::fake('local');
$this->seed(DatabaseSeeder::class);
$finance = User::query()->where('email', 'kewangan@prn.local')->firstOrFail();
$this->actingAs($finance)
->get(route('kewangan.documents.download', $this->document('bank_statement')))
->assertOk();
}
public function test_finance_cannot_download_ic_document_via_finance_route(): void
{
Storage::fake('local');
$this->seed(DatabaseSeeder::class);
$finance = User::query()->where('email', 'kewangan@prn.local')->firstOrFail();
$this->actingAs($finance)
->get(route('kewangan.documents.download', $this->document('ic_document')))
->assertForbidden();
}
// ── PPM path ──────────────────────────────────────────────────────────
public function test_ppm_can_download_ic_document_from_own_pusat(): void
{
Storage::fake('local');
$this->seed(DatabaseSeeder::class);
$ppm = User::query()->where('email', 'ppm@prn.local')->firstOrFail();
$pusat = $this->ppmPusat($ppm);
$document = $this->documentForPusat($pusat, 'ic_document');
$this->actingAs($ppm)
->get(route('ppm.applications.documents.download', [
'application' => $document->application->public_uuid,
'document' => $document,
]))
->assertOk();
}
public function test_ppm_can_download_bank_statement_from_own_pusat(): void
{
Storage::fake('local');
$this->seed(DatabaseSeeder::class);
$ppm = User::query()->where('email', 'ppm@prn.local')->firstOrFail();
$pusat = $this->ppmPusat($ppm);
$document = $this->documentForPusat($pusat, 'bank_statement');
$this->actingAs($ppm)
->get(route('ppm.applications.documents.download', [
'application' => $document->application->public_uuid,
'document' => $document,
]))
->assertOk();
}
public function test_ppm_cannot_download_document_from_another_pusat(): void
{
Storage::fake('local');
$this->seed(DatabaseSeeder::class);
$ppm = User::query()->where('email', 'ppm@prn.local')->firstOrFail();
// Create a document belonging to a DIFFERENT pusat than PPM's assignment
$otherPusat = PusatMengundi::factory()->create([
'election_id' => PusatMengundi::query()->where('code', 'PM001')->value('election_id'),
'daerah_mengundi_id' => PusatMengundi::query()->where('code', 'PM001')->value('daerah_mengundi_id'),
]);
$document = $this->documentForPusat($otherPusat, 'ic_document');
$this->actingAs($ppm)
->get(route('ppm.applications.documents.download', [
'application' => $document->application->public_uuid,
'document' => $document,
]))
->assertForbidden();
}
public function test_ppm_download_records_activity_log(): void
{
Storage::fake('local');
$this->seed(DatabaseSeeder::class);
$ppm = User::query()->where('email', 'ppm@prn.local')->firstOrFail();
$pusat = $this->ppmPusat($ppm);
$document = $this->documentForPusat($pusat, 'ic_document');
$this->actingAs($ppm)
->get(route('ppm.applications.documents.download', [
'application' => $document->application->public_uuid,
'document' => $document,
]));
$this->assertDatabaseHas('activity_log', [
'log_name' => 'documents',
'description' => 'document_downloaded_by_ppm',
'causer_id' => $ppm->id,
]);
}
// ── Helpers ───────────────────────────────────────────────────────────
private function document(string $type, string $fileName = 'file.pdf'): ApplicationDocument
{
$pusat = PusatMengundi::query()->where('code', 'PM001')->firstOrFail();
return $this->documentForPusat($pusat, $type, $fileName);
}
private function documentForPusat(PusatMengundi $pusat, string $type, string $fileName = 'file.pdf'): ApplicationDocument
{
$position = Position::query()->where('code', 'PAPM')->firstOrFail();
$application = Application::factory()->create([
'election_id' => $pusat->election_id,
'pusat_mengundi_id' => $pusat->id,
'requested_position_id' => $position->id,
]);
$path = 'applications/'.$application->public_uuid.'/'.$fileName;
Storage::disk('local')->put($path, 'test content');
$document = ApplicationDocument::query()->create([
'application_id' => $application->id,
'document_type' => $type,
'disk' => 'local',
'path' => $path,
'original_name' => $fileName,
'mime_type' => 'application/pdf',
'size' => 12,
]);
$document->setRelation('application', $application);
return $document;
}
private function ppmPusat(User $ppm): PusatMengundi
{
$ppmPosition = Position::query()->where('code', 'PPM')->firstOrFail();
$assignment = StaffAssignment::query()
->where('user_id', $ppm->id)
->where('position_id', $ppmPosition->id)
->where('status', 'active')
->firstOrFail();
return PusatMengundi::query()->findOrFail($assignment->pusat_mengundi_id);
}
}