artisan('db:seed', ['--class' => 'RoleSeeder']); } public function test_member_has_required_fillable_fields(): void { $member = Member::factory()->create([ 'full_name' => 'Test Member', 'email' => 'test@example.com', 'phone' => '0912345678', ]); $this->assertEquals('Test Member', $member->full_name); $this->assertEquals('test@example.com', $member->email); $this->assertEquals('0912345678', $member->phone); } public function test_member_belongs_to_user(): void { $user = User::factory()->create(); $member = Member::factory()->create(['user_id' => $user->id]); $this->assertInstanceOf(User::class, $member->user); $this->assertEquals($user->id, $member->user->id); } public function test_member_has_many_payments(): void { $member = Member::factory()->create(); $payment1 = MembershipPayment::factory()->create(['member_id' => $member->id]); $payment2 = MembershipPayment::factory()->create(['member_id' => $member->id]); $this->assertCount(2, $member->payments); $this->assertTrue($member->payments->contains($payment1)); $this->assertTrue($member->payments->contains($payment2)); } public function test_has_paid_membership_returns_true_when_active_with_future_expiry(): void { $member = Member::factory()->create([ 'membership_status' => Member::STATUS_ACTIVE, 'membership_started_at' => now()->subMonth(), 'membership_expires_at' => now()->addYear(), ]); $this->assertTrue($member->hasPaidMembership()); } public function test_has_paid_membership_returns_false_when_pending(): void { $member = Member::factory()->create([ 'membership_status' => Member::STATUS_PENDING, 'membership_started_at' => null, 'membership_expires_at' => null, ]); $this->assertFalse($member->hasPaidMembership()); } public function test_has_paid_membership_returns_false_when_expired(): void { $member = Member::factory()->create([ 'membership_status' => Member::STATUS_ACTIVE, 'membership_started_at' => now()->subYear()->subMonth(), 'membership_expires_at' => now()->subMonth(), ]); $this->assertFalse($member->hasPaidMembership()); } public function test_has_paid_membership_returns_false_when_suspended(): void { $member = Member::factory()->create([ 'membership_status' => Member::STATUS_SUSPENDED, 'membership_started_at' => now()->subMonth(), 'membership_expires_at' => now()->addYear(), ]); $this->assertFalse($member->hasPaidMembership()); } public function test_can_submit_payment_returns_true_when_pending_and_no_pending_payment(): void { $member = Member::factory()->create([ 'membership_status' => Member::STATUS_PENDING, ]); $this->assertTrue($member->canSubmitPayment()); } public function test_can_submit_payment_returns_false_when_already_has_pending_payment(): void { $member = Member::factory()->create([ 'membership_status' => Member::STATUS_PENDING, ]); MembershipPayment::factory()->create([ 'member_id' => $member->id, 'status' => MembershipPayment::STATUS_PENDING, ]); $this->assertFalse($member->canSubmitPayment()); } public function test_can_submit_payment_returns_false_when_active(): void { $member = Member::factory()->create([ 'membership_status' => Member::STATUS_ACTIVE, ]); $this->assertFalse($member->canSubmitPayment()); } public function test_get_pending_payment_returns_pending_payment(): void { $member = Member::factory()->create(); $pendingPayment = MembershipPayment::factory()->create([ 'member_id' => $member->id, 'status' => MembershipPayment::STATUS_PENDING, ]); $approvedPayment = MembershipPayment::factory()->create([ 'member_id' => $member->id, 'status' => MembershipPayment::STATUS_APPROVED_CHAIR, ]); $result = $member->getPendingPayment(); $this->assertNotNull($result); $this->assertEquals($pendingPayment->id, $result->id); } public function test_get_pending_payment_returns_null_when_no_pending_payments(): void { $member = Member::factory()->create(); MembershipPayment::factory()->create([ 'member_id' => $member->id, 'status' => MembershipPayment::STATUS_APPROVED_CHAIR, ]); $this->assertNull($member->getPendingPayment()); } public function test_national_id_encryption_and_decryption(): void { $member = Member::factory()->create([ 'full_name' => 'Test Member', ]); $nationalId = 'A123456789'; $member->national_id = $nationalId; $member->save(); // Refresh from database $member->refresh(); // Check encrypted value is different from plain text $this->assertNotEquals($nationalId, $member->national_id_encrypted); // Check decryption works $this->assertEquals($nationalId, $member->national_id); } public function test_national_id_hash_created_for_search(): void { $member = Member::factory()->create(); $nationalId = 'A123456789'; $member->national_id = $nationalId; $member->save(); $member->refresh(); // Check hash was created $this->assertNotNull($member->national_id_hash); // Check hash matches SHA256 $expectedHash = hash('sha256', $nationalId); $this->assertEquals($expectedHash, $member->national_id_hash); } public function test_is_pending_returns_true_when_status_is_pending(): void { $member = Member::factory()->create(['membership_status' => Member::STATUS_PENDING]); $this->assertTrue($member->isPending()); } public function test_is_active_returns_true_when_status_is_active(): void { $member = Member::factory()->create(['membership_status' => Member::STATUS_ACTIVE]); $this->assertTrue($member->isActive()); } public function test_is_expired_returns_true_when_status_is_expired(): void { $member = Member::factory()->create(['membership_status' => Member::STATUS_EXPIRED]); $this->assertTrue($member->isExpired()); } public function test_is_suspended_returns_true_when_status_is_suspended(): void { $member = Member::factory()->create(['membership_status' => Member::STATUS_SUSPENDED]); $this->assertTrue($member->isSuspended()); } public function test_membership_status_label_returns_chinese_text(): void { $member = Member::factory()->create(['membership_status' => Member::STATUS_PENDING]); $this->assertEquals('待繳費', $member->membership_status_label); $member->membership_status = Member::STATUS_ACTIVE; $this->assertEquals('已啟用', $member->membership_status_label); $member->membership_status = Member::STATUS_EXPIRED; $this->assertEquals('已過期', $member->membership_status_label); $member->membership_status = Member::STATUS_SUSPENDED; $this->assertEquals('已暫停', $member->membership_status_label); } public function test_membership_type_label_returns_chinese_text(): void { $member = Member::factory()->create(['membership_type' => Member::TYPE_REGULAR]); $this->assertEquals('一般會員', $member->membership_type_label); $member->membership_type = Member::TYPE_STUDENT; $this->assertEquals('學生會員', $member->membership_type_label); $member->membership_type = Member::TYPE_HONORARY; $this->assertEquals('榮譽會員', $member->membership_type_label); $member->membership_type = Member::TYPE_LIFETIME; $this->assertEquals('終身會員', $member->membership_type_label); } public function test_membership_status_badge_returns_correct_css_class(): void { $member = Member::factory()->create(['membership_status' => Member::STATUS_PENDING]); $badge = $member->membership_status_badge; $this->assertStringContainsString('待繳費', $badge); $this->assertStringContainsString('bg-yellow', $badge); $member->membership_status = Member::STATUS_ACTIVE; $badge = $member->membership_status_badge; $this->assertStringContainsString('bg-green', $badge); } }