Files
usher-manage-stack/tests/Feature/Roles/RolePermissionTest.php
gbanyan 42099759e8 Add phone login support and member import functionality
Features:
- Support login via phone number or email (LoginRequest)
- Add members:import-roster command for Excel roster import
- Merge survey emails with roster data

Code Quality (Phase 1-4):
- Add database locking for balance calculation
- Add self-approval checks for finance workflow
- Create service layer (FinanceDocumentApprovalService, PaymentVerificationService)
- Add HasAccountingEntries and HasApprovalWorkflow traits
- Create FormRequest classes for validation
- Add status-badge component
- Define authorization gates in AuthServiceProvider
- Add accounting config file

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 03:08:06 +08:00

181 lines
5.5 KiB
PHP

<?php
namespace Tests\Feature\Roles;
use App\Models\FinanceDocument;
use App\Models\Member;
use App\Models\MembershipPayment;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Tests\TestCase;
use Tests\Traits\CreatesFinanceData;
use Tests\Traits\CreatesMemberData;
use Tests\Traits\SeedsRolesAndPermissions;
/**
* Role Permission Tests
*
* Tests role-based access control and permissions.
* Uses new workflow: Secretary → Chair → Board
*/
class RolePermissionTest extends TestCase
{
use RefreshDatabase, SeedsRolesAndPermissions, CreatesMemberData, CreatesFinanceData;
protected function setUp(): void
{
parent::setUp();
Storage::fake('private');
Storage::fake('local');
$this->seedRolesAndPermissions();
}
/**
* Test admin can access admin dashboard
*/
public function test_admin_can_access_admin_dashboard(): void
{
$admin = $this->createAdmin();
$response = $this->actingAs($admin)->get(route('admin.dashboard'));
$response->assertStatus(200);
}
/**
* Test member cannot access admin dashboard
*/
public function test_member_cannot_access_admin_dashboard(): void
{
$user = $this->createMemberUser();
$response = $this->actingAs($user)->get(route('admin.dashboard'));
$response->assertStatus(403);
}
/**
* Test cashier can approve membership payments (first tier)
*/
public function test_cashier_can_approve_membership_payments(): void
{
$cashier = $this->createCashier();
$data = $this->createMemberWithPendingPayment();
$response = $this->actingAs($cashier)->post(
route('admin.payment-verifications.approve-cashier', $data['payment'])
);
$data['payment']->refresh();
$this->assertEquals(MembershipPayment::STATUS_APPROVED_CASHIER, $data['payment']->status);
}
/**
* Test accountant cannot approve pending payment directly (needs cashier first)
*/
public function test_accountant_cannot_approve_pending_payment_directly(): void
{
$accountant = $this->createAccountant();
$data = $this->createMemberWithPendingPayment();
$response = $this->actingAs($accountant)->post(
route('admin.payment-verifications.approve-accountant', $data['payment'])
);
// Should remain pending (workflow requires cashier first)
$data['payment']->refresh();
$this->assertEquals(MembershipPayment::STATUS_PENDING, $data['payment']->status);
}
/**
* Test chair can approve after accountant
*/
public function test_chair_can_approve_after_accountant(): void
{
$chair = $this->createChair();
$data = $this->createMemberWithPaymentAtStage('accountant_approved');
$response = $this->actingAs($chair)->post(
route('admin.payment-verifications.approve-chair', $data['payment'])
);
$data['payment']->refresh();
$this->assertEquals(MembershipPayment::STATUS_APPROVED_CHAIR, $data['payment']->status);
}
/**
* Test secretary can approve finance documents (new workflow first stage)
*/
public function test_secretary_can_approve_finance_documents(): void
{
$secretary = $this->createSecretary();
$document = $this->createFinanceDocument([
'status' => FinanceDocument::STATUS_PENDING,
]);
$response = $this->actingAs($secretary)->post(
route('admin.finance.approve', $document)
);
$document->refresh();
$this->assertEquals(FinanceDocument::STATUS_APPROVED_SECRETARY, $document->status);
}
/**
* Test unauthorized user cannot approve
*/
public function test_unauthorized_user_cannot_approve(): void
{
$user = $this->createMemberUser();
$data = $this->createMemberWithPendingPayment();
$response = $this->actingAs($user)->post(
route('admin.payment-verifications.approve-cashier', $data['payment'])
);
$response->assertStatus(403);
}
/**
* Test super admin has all permissions
*/
public function test_super_admin_has_all_permissions(): void
{
$superAdmin = $this->createSuperAdmin();
// Super admin should have various permissions
$this->assertTrue($superAdmin->hasRole('super_admin'));
$this->assertTrue($superAdmin->can('view_finance_documents'));
$this->assertTrue($superAdmin->can('approve_finance_secretary'));
}
/**
* Test role hierarchy for approvals
*/
public function test_role_hierarchy_for_approvals(): void
{
// Chair should have the finance_chair role
$chair = $this->createChair();
$this->assertTrue($chair->hasRole('finance_chair'));
// Secretary should have the secretary_general role
$secretary = $this->createSecretary();
$this->assertTrue($secretary->hasRole('secretary_general'));
}
/**
* Test finance approval roles exist
*/
public function test_finance_approval_roles_exist(): void
{
$this->assertNotNull(Role::findByName('secretary_general'));
$this->assertNotNull(Role::findByName('finance_chair'));
$this->assertNotNull(Role::findByName('finance_board_member'));
$this->assertNotNull(Role::findByName('finance_cashier'));
$this->assertNotNull(Role::findByName('finance_accountant'));
}
}