first
This commit is contained in:
211
tests/Feature/DocumentDownloadAuthorizationTest.php
Normal file
211
tests/Feature/DocumentDownloadAuthorizationTest.php
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user