Initial commit
This commit is contained in:
230
tests/Unit/MembershipPaymentTest.php
Normal file
230
tests/Unit/MembershipPaymentTest.php
Normal file
@@ -0,0 +1,230 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Models\Member;
|
||||
use App\Models\MembershipPayment;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Tests\TestCase;
|
||||
|
||||
class MembershipPaymentTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Storage::fake('private');
|
||||
$this->artisan('db:seed', ['--class' => 'RoleSeeder']);
|
||||
}
|
||||
|
||||
public function test_payment_belongs_to_member(): void
|
||||
{
|
||||
$member = Member::factory()->create();
|
||||
$payment = MembershipPayment::factory()->create(['member_id' => $member->id]);
|
||||
|
||||
$this->assertInstanceOf(Member::class, $payment->member);
|
||||
$this->assertEquals($member->id, $payment->member->id);
|
||||
}
|
||||
|
||||
public function test_payment_belongs_to_submitted_by_user(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$payment = MembershipPayment::factory()->create(['submitted_by_user_id' => $user->id]);
|
||||
|
||||
$this->assertInstanceOf(User::class, $payment->submittedBy);
|
||||
$this->assertEquals($user->id, $payment->submittedBy->id);
|
||||
}
|
||||
|
||||
public function test_payment_has_verifier_relationships(): void
|
||||
{
|
||||
$cashier = User::factory()->create();
|
||||
$accountant = User::factory()->create();
|
||||
$chair = User::factory()->create();
|
||||
|
||||
$payment = MembershipPayment::factory()->create([
|
||||
'verified_by_cashier_id' => $cashier->id,
|
||||
'verified_by_accountant_id' => $accountant->id,
|
||||
'verified_by_chair_id' => $chair->id,
|
||||
]);
|
||||
|
||||
$this->assertInstanceOf(User::class, $payment->verifiedByCashier);
|
||||
$this->assertInstanceOf(User::class, $payment->verifiedByAccountant);
|
||||
$this->assertInstanceOf(User::class, $payment->verifiedByChair);
|
||||
|
||||
$this->assertEquals($cashier->id, $payment->verifiedByCashier->id);
|
||||
$this->assertEquals($accountant->id, $payment->verifiedByAccountant->id);
|
||||
$this->assertEquals($chair->id, $payment->verifiedByChair->id);
|
||||
}
|
||||
|
||||
public function test_is_pending_returns_true_when_status_is_pending(): void
|
||||
{
|
||||
$payment = MembershipPayment::factory()->create(['status' => MembershipPayment::STATUS_PENDING]);
|
||||
$this->assertTrue($payment->isPending());
|
||||
}
|
||||
|
||||
public function test_is_approved_by_cashier_returns_true_when_status_is_approved_cashier(): void
|
||||
{
|
||||
$payment = MembershipPayment::factory()->create(['status' => MembershipPayment::STATUS_APPROVED_CASHIER]);
|
||||
$this->assertTrue($payment->isApprovedByCashier());
|
||||
}
|
||||
|
||||
public function test_is_approved_by_accountant_returns_true_when_status_is_approved_accountant(): void
|
||||
{
|
||||
$payment = MembershipPayment::factory()->create(['status' => MembershipPayment::STATUS_APPROVED_ACCOUNTANT]);
|
||||
$this->assertTrue($payment->isApprovedByAccountant());
|
||||
}
|
||||
|
||||
public function test_is_fully_approved_returns_true_when_status_is_approved_chair(): void
|
||||
{
|
||||
$payment = MembershipPayment::factory()->create(['status' => MembershipPayment::STATUS_APPROVED_CHAIR]);
|
||||
$this->assertTrue($payment->isFullyApproved());
|
||||
}
|
||||
|
||||
public function test_is_rejected_returns_true_when_status_is_rejected(): void
|
||||
{
|
||||
$payment = MembershipPayment::factory()->create(['status' => MembershipPayment::STATUS_REJECTED]);
|
||||
$this->assertTrue($payment->isRejected());
|
||||
}
|
||||
|
||||
public function test_can_be_approved_by_cashier_validates_correctly(): void
|
||||
{
|
||||
$pendingPayment = MembershipPayment::factory()->create(['status' => MembershipPayment::STATUS_PENDING]);
|
||||
$this->assertTrue($pendingPayment->canBeApprovedByCashier());
|
||||
|
||||
$approvedPayment = MembershipPayment::factory()->create(['status' => MembershipPayment::STATUS_APPROVED_CASHIER]);
|
||||
$this->assertFalse($approvedPayment->canBeApprovedByCashier());
|
||||
}
|
||||
|
||||
public function test_can_be_approved_by_accountant_validates_correctly(): void
|
||||
{
|
||||
$cashierApprovedPayment = MembershipPayment::factory()->create(['status' => MembershipPayment::STATUS_APPROVED_CASHIER]);
|
||||
$this->assertTrue($cashierApprovedPayment->canBeApprovedByAccountant());
|
||||
|
||||
$pendingPayment = MembershipPayment::factory()->create(['status' => MembershipPayment::STATUS_PENDING]);
|
||||
$this->assertFalse($pendingPayment->canBeApprovedByAccountant());
|
||||
}
|
||||
|
||||
public function test_can_be_approved_by_chair_validates_correctly(): void
|
||||
{
|
||||
$accountantApprovedPayment = MembershipPayment::factory()->create(['status' => MembershipPayment::STATUS_APPROVED_ACCOUNTANT]);
|
||||
$this->assertTrue($accountantApprovedPayment->canBeApprovedByChair());
|
||||
|
||||
$cashierApprovedPayment = MembershipPayment::factory()->create(['status' => MembershipPayment::STATUS_APPROVED_CASHIER]);
|
||||
$this->assertFalse($cashierApprovedPayment->canBeApprovedByChair());
|
||||
}
|
||||
|
||||
public function test_workflow_validation_prevents_skipping_tiers(): void
|
||||
{
|
||||
$payment = MembershipPayment::factory()->create(['status' => MembershipPayment::STATUS_PENDING]);
|
||||
|
||||
// Cannot skip to accountant approval
|
||||
$this->assertFalse($payment->canBeApprovedByAccountant());
|
||||
|
||||
// Cannot skip to chair approval
|
||||
$this->assertFalse($payment->canBeApprovedByChair());
|
||||
|
||||
// Must go through cashier first
|
||||
$this->assertTrue($payment->canBeApprovedByCashier());
|
||||
}
|
||||
|
||||
public function test_status_label_returns_chinese_text(): void
|
||||
{
|
||||
$payment = MembershipPayment::factory()->create(['status' => MembershipPayment::STATUS_PENDING]);
|
||||
$this->assertEquals('待審核', $payment->status_label);
|
||||
|
||||
$payment->status = MembershipPayment::STATUS_APPROVED_CASHIER;
|
||||
$this->assertEquals('出納已審', $payment->status_label);
|
||||
|
||||
$payment->status = MembershipPayment::STATUS_APPROVED_ACCOUNTANT;
|
||||
$this->assertEquals('會計已審', $payment->status_label);
|
||||
|
||||
$payment->status = MembershipPayment::STATUS_APPROVED_CHAIR;
|
||||
$this->assertEquals('主席已審', $payment->status_label);
|
||||
|
||||
$payment->status = MembershipPayment::STATUS_REJECTED;
|
||||
$this->assertEquals('已拒絕', $payment->status_label);
|
||||
}
|
||||
|
||||
public function test_payment_method_label_returns_chinese_text(): void
|
||||
{
|
||||
$payment = MembershipPayment::factory()->create(['payment_method' => MembershipPayment::METHOD_BANK_TRANSFER]);
|
||||
$this->assertEquals('銀行轉帳', $payment->payment_method_label);
|
||||
|
||||
$payment->payment_method = MembershipPayment::METHOD_CONVENIENCE_STORE;
|
||||
$this->assertEquals('超商繳費', $payment->payment_method_label);
|
||||
|
||||
$payment->payment_method = MembershipPayment::METHOD_CASH;
|
||||
$this->assertEquals('現金', $payment->payment_method_label);
|
||||
|
||||
$payment->payment_method = MembershipPayment::METHOD_CREDIT_CARD;
|
||||
$this->assertEquals('信用卡', $payment->payment_method_label);
|
||||
}
|
||||
|
||||
public function test_receipt_file_cleanup_on_deletion(): void
|
||||
{
|
||||
Storage::fake('private');
|
||||
|
||||
$payment = MembershipPayment::factory()->create([
|
||||
'receipt_path' => 'payment-receipts/test-receipt.pdf'
|
||||
]);
|
||||
|
||||
// Create fake file
|
||||
Storage::disk('private')->put('payment-receipts/test-receipt.pdf', 'test content');
|
||||
|
||||
$this->assertTrue(Storage::disk('private')->exists('payment-receipts/test-receipt.pdf'));
|
||||
|
||||
// Delete payment should delete file
|
||||
$payment->delete();
|
||||
|
||||
$this->assertFalse(Storage::disk('private')->exists('payment-receipts/test-receipt.pdf'));
|
||||
}
|
||||
|
||||
public function test_rejection_tracking_works(): void
|
||||
{
|
||||
$rejector = User::factory()->create();
|
||||
$payment = MembershipPayment::factory()->create([
|
||||
'status' => MembershipPayment::STATUS_REJECTED,
|
||||
'rejected_by_user_id' => $rejector->id,
|
||||
'rejected_at' => now(),
|
||||
'rejection_reason' => 'Invalid receipt',
|
||||
]);
|
||||
|
||||
$this->assertTrue($payment->isRejected());
|
||||
$this->assertInstanceOf(User::class, $payment->rejectedBy);
|
||||
$this->assertEquals($rejector->id, $payment->rejectedBy->id);
|
||||
$this->assertEquals('Invalid receipt', $payment->rejection_reason);
|
||||
$this->assertNotNull($payment->rejected_at);
|
||||
}
|
||||
|
||||
public function test_payment_workflow_complete_sequence(): void
|
||||
{
|
||||
$payment = MembershipPayment::factory()->create(['status' => MembershipPayment::STATUS_PENDING]);
|
||||
|
||||
// Step 1: Pending - can only be approved by cashier
|
||||
$this->assertTrue($payment->canBeApprovedByCashier());
|
||||
$this->assertFalse($payment->canBeApprovedByAccountant());
|
||||
$this->assertFalse($payment->canBeApprovedByChair());
|
||||
|
||||
// Step 2: Cashier approved - can only be approved by accountant
|
||||
$payment->status = MembershipPayment::STATUS_APPROVED_CASHIER;
|
||||
$this->assertFalse($payment->canBeApprovedByCashier());
|
||||
$this->assertTrue($payment->canBeApprovedByAccountant());
|
||||
$this->assertFalse($payment->canBeApprovedByChair());
|
||||
|
||||
// Step 3: Accountant approved - can only be approved by chair
|
||||
$payment->status = MembershipPayment::STATUS_APPROVED_ACCOUNTANT;
|
||||
$this->assertFalse($payment->canBeApprovedByCashier());
|
||||
$this->assertFalse($payment->canBeApprovedByAccountant());
|
||||
$this->assertTrue($payment->canBeApprovedByChair());
|
||||
|
||||
// Step 4: Chair approved - workflow complete
|
||||
$payment->status = MembershipPayment::STATUS_APPROVED_CHAIR;
|
||||
$this->assertFalse($payment->canBeApprovedByCashier());
|
||||
$this->assertFalse($payment->canBeApprovedByAccountant());
|
||||
$this->assertFalse($payment->canBeApprovedByChair());
|
||||
$this->assertTrue($payment->isFullyApproved());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user