Files
travel/tests/Feature/TravelRequest/ApprovalTest.php
Tim Basten 564f78dcda
All checks were successful
linter / quality (push) Successful in 1m37s
tests / ci (8.4) (push) Successful in 2m13s
tests / ci (8.5) (push) Successful in 1m25s
initial
2026-03-05 11:41:39 +08:00

181 lines
5.9 KiB
PHP

<?php
namespace Tests\Feature\TravelRequest;
use App\Enums\ApprovalStatus;
use App\Enums\TravelStatus;
use App\Jobs\SendApprovalDecisionEmail;
use App\Jobs\SendApprovalRequestEmail;
use App\Models\ApprovalStep;
use App\Models\ApprovalWorkflow;
use App\Models\TravelRequest;
use App\Models\TravelRequestApproval;
use App\Models\User;
use App\Services\ApprovalService;
use Database\Seeders\RoleSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;
class ApprovalTest extends TestCase
{
use RefreshDatabase;
protected User $staff;
protected User $approver;
protected ApprovalWorkflow $workflow;
protected ApprovalStep $step1;
protected ApprovalStep $step2;
protected function setUp(): void
{
parent::setUp();
$this->seed(RoleSeeder::class);
$this->staff = User::factory()->create();
$this->staff->assignRole('staff');
$this->approver = User::factory()->create();
$this->approver->assignRole('travel_approver');
$this->workflow = ApprovalWorkflow::factory()->create(['is_active' => true]);
$this->step1 = ApprovalStep::factory()->create([
'workflow_id' => $this->workflow->id,
'order' => 1,
'role' => 'travel_approver',
]);
$this->step2 = ApprovalStep::factory()->create([
'workflow_id' => $this->workflow->id,
'order' => 2,
'role' => 'administrator',
]);
}
public function test_submitting_request_creates_approval_records_and_dispatches_email(): void
{
Queue::fake();
$request = TravelRequest::factory()->create([
'user_id' => $this->staff->id,
'workflow_id' => $this->workflow->id,
'status' => TravelStatus::Draft,
]);
app(ApprovalService::class)->submit($request);
$request->refresh();
$this->assertEquals(TravelStatus::Pending, $request->status);
$this->assertNotNull($request->submitted_at);
$this->assertDatabaseHas('travel_request_approvals', [
'travel_request_id' => $request->id,
'approval_step_id' => $this->step1->id,
'status' => ApprovalStatus::Pending->value,
]);
Queue::assertPushed(SendApprovalRequestEmail::class);
}
public function test_approving_first_step_advances_to_second_step(): void
{
Queue::fake();
$request = TravelRequest::factory()->create([
'user_id' => $this->staff->id,
'workflow_id' => $this->workflow->id,
'status' => TravelStatus::Pending,
'submitted_at' => now(),
]);
$approval1 = TravelRequestApproval::create([
'travel_request_id' => $request->id,
'approval_step_id' => $this->step1->id,
'status' => ApprovalStatus::Pending->value,
]);
TravelRequestApproval::create([
'travel_request_id' => $request->id,
'approval_step_id' => $this->step2->id,
'status' => null,
]);
app(ApprovalService::class)->approve($approval1, $this->approver, 'Looks good');
$approval1->refresh();
$this->assertEquals(ApprovalStatus::Approved, $approval1->status);
$this->assertEquals($this->approver->id, $approval1->approver_id);
$this->assertDatabaseHas('travel_request_approvals', [
'travel_request_id' => $request->id,
'approval_step_id' => $this->step2->id,
'status' => ApprovalStatus::Pending->value,
]);
Queue::assertPushed(SendApprovalRequestEmail::class);
}
public function test_approving_final_step_marks_request_as_approved(): void
{
Queue::fake();
$request = TravelRequest::factory()->create([
'user_id' => $this->staff->id,
'workflow_id' => $this->workflow->id,
'status' => TravelStatus::Pending,
]);
TravelRequestApproval::create([
'travel_request_id' => $request->id,
'approval_step_id' => $this->step1->id,
'status' => ApprovalStatus::Approved->value,
'approver_id' => $this->approver->id,
'acted_at' => now(),
]);
$finalApproval = TravelRequestApproval::create([
'travel_request_id' => $request->id,
'approval_step_id' => $this->step2->id,
'status' => ApprovalStatus::Pending->value,
]);
$admin = User::factory()->create();
$admin->assignRole('administrator');
app(ApprovalService::class)->approve($finalApproval, $admin, null);
$request->refresh();
$this->assertEquals(TravelStatus::Approved, $request->status);
Queue::assertPushed(SendApprovalDecisionEmail::class);
}
public function test_rejection_marks_request_as_rejected_and_dispatches_email(): void
{
Queue::fake();
$request = TravelRequest::factory()->create([
'user_id' => $this->staff->id,
'workflow_id' => $this->workflow->id,
'status' => TravelStatus::Pending,
]);
$approval = TravelRequestApproval::create([
'travel_request_id' => $request->id,
'approval_step_id' => $this->step1->id,
'status' => ApprovalStatus::Pending->value,
]);
app(ApprovalService::class)->reject($approval, $this->approver, 'Budget not available');
$request->refresh();
$this->assertEquals(TravelStatus::Rejected, $request->status);
$this->assertEquals('Budget not available', $approval->fresh()->comments);
Queue::assertPushed(SendApprovalDecisionEmail::class);
}
public function test_approve_page_requires_approver_role(): void
{
$request = TravelRequest::factory()->create(['user_id' => $this->staff->id]);
$this->actingAs($this->staff)
->get("/travel-requests/{$request->id}/approve")
->assertForbidden();
}
}