| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- <?php
- namespace Tests\Unit;
- use App\Services\WorldCup\Repositories\WorldCupReviewRepositoryInterface;
- use App\Services\WorldCup\WorldCupReviewService;
- use Tests\TestCase;
- class WorldCupReviewServiceTest extends TestCase
- {
- public function test_approve_sends_claim_mail_and_writes_audit()
- {
- $repository = new InMemoryWorldCupReviewRepository();
- $service = new WorldCupReviewService($repository);
- $result = $service->approve(1, 'admin-01');
- $this->assertTrue($result['success']);
- $this->assertSame('approved', $repository->rewards[1]['status']);
- $this->assertSame(6000, $repository->mails[1001][0]['amount']);
- $this->assertSame(6000, $repository->mails[1002][0]['amount']);
- $this->assertSame('Claim your $60 referral reward', $repository->mails[1001][0]['title']);
- $this->assertSame('Claim your $60 welcome reward', $repository->mails[1002][0]['title']);
- $this->assertStringContainsString('Reward: $60', $repository->mails[1001][0]['text']);
- $this->assertStringContainsString('first deposit of $120', $repository->mails[1002][0]['text']);
- $this->assertSame('approve', $repository->audits[0]['action']);
- }
- public function test_reject_requires_reason_and_writes_audit()
- {
- $repository = new InMemoryWorldCupReviewRepository();
- $service = new WorldCupReviewService($repository);
- $missingReason = $service->reject(1, 'admin-01', '');
- $result = $service->reject(1, 'admin-01', 'same_device');
- $this->assertFalse($missingReason['success']);
- $this->assertSame('Reason is required', $missingReason['message']);
- $this->assertTrue($result['success']);
- $this->assertSame('rejected', $repository->rewards[1]['status']);
- $this->assertSame(WorldCupReviewService::REJECT_REASON, $repository->rewards[1]['reason_code']);
- $this->assertSame('Your referral reward could not be approved', $repository->mails[1001][0]['title']);
- $this->assertSame('Welcome reward not approved', $repository->mails[1002][0]['title']);
- $this->assertStringContainsString(WorldCupReviewService::REJECT_REASON, $repository->mails[1001][0]['text']);
- $this->assertStringContainsString('Your welcome reward could not be approved.', $repository->mails[1002][0]['text']);
- $this->assertSame('reject', $repository->audits[0]['action']);
- }
- public function test_hold_moves_reviewing_reward_to_on_hold()
- {
- $repository = new InMemoryWorldCupReviewRepository();
- $service = new WorldCupReviewService($repository);
- $result = $service->hold(1, 'admin-01');
- $this->assertTrue($result['success']);
- $this->assertSame('on_hold', $repository->rewards[1]['status']);
- $this->assertSame('hold', $repository->audits[0]['action']);
- }
- public function test_clawback_only_allows_approved_reward()
- {
- $repository = new InMemoryWorldCupReviewRepository();
- $service = new WorldCupReviewService($repository);
- $notApproved = $service->clawback(1, 'admin-01', 'fraud', true);
- $approved = $service->clawback(3, 'admin-01', 'fraud', true);
- $this->assertFalse($notApproved['success']);
- $this->assertSame('Only approved rewards can be clawed back', $notApproved['message']);
- $this->assertTrue($approved['success']);
- $this->assertSame('clawed_back', $repository->rewards[3]['status']);
- $this->assertSame(1001, $repository->banned[0]);
- $this->assertSame(1004, $repository->banned[1]);
- $this->assertSame(10000, $repository->clawedBack[1001]);
- $this->assertSame(10000, $repository->clawedBack[1004]);
- }
- public function test_batch_approve_rejects_high_risk_rewards()
- {
- $repository = new InMemoryWorldCupReviewRepository();
- $service = new WorldCupReviewService($repository);
- $result = $service->batchApprove([1, 2], 'admin-01');
- $this->assertFalse($result['success']);
- $this->assertSame('High risk rewards must be reviewed one by one', $result['message']);
- $this->assertSame('reviewing', $repository->rewards[1]['status']);
- $this->assertSame('reviewing', $repository->rewards[2]['status']);
- }
- public function test_batch_reject_requires_reason()
- {
- $repository = new InMemoryWorldCupReviewRepository();
- $service = new WorldCupReviewService($repository);
- $missingReason = $service->batchReject([1, 2], 'admin-01', '');
- $result = $service->batchReject([1, 2], 'admin-01', 'bulk_signup');
- $this->assertFalse($missingReason['success']);
- $this->assertSame('Reason is required', $missingReason['message']);
- $this->assertTrue($result['success']);
- $this->assertSame('rejected', $repository->rewards[1]['status']);
- $this->assertSame('rejected', $repository->rewards[2]['status']);
- }
- }
- class InMemoryWorldCupReviewRepository implements WorldCupReviewRepositoryInterface
- {
- public $rewards = [
- 1 => [
- 'reward_id' => 1,
- 'referrer_id' => 1001,
- 'invitee_id' => 1002,
- 'first_deposit_amt' => 12000,
- 'reward_each' => 6000,
- 'total_liability' => 12000,
- 'risk_level' => 'low',
- 'status' => 'reviewing',
- ],
- 2 => [
- 'reward_id' => 2,
- 'referrer_id' => 1001,
- 'invitee_id' => 1003,
- 'first_deposit_amt' => 30000,
- 'reward_each' => 10000,
- 'total_liability' => 20000,
- 'risk_level' => 'high',
- 'status' => 'reviewing',
- ],
- 3 => [
- 'reward_id' => 3,
- 'referrer_id' => 1001,
- 'invitee_id' => 1004,
- 'first_deposit_amt' => 30000,
- 'reward_each' => 10000,
- 'total_liability' => 20000,
- 'risk_level' => 'low',
- 'status' => 'approved',
- ],
- ];
- public $mails = [];
- public $clawedBack = [];
- public $banned = [];
- public $audits = [];
- public function findReward(int $rewardId): ?array
- {
- return $this->rewards[$rewardId] ?? null;
- }
- public function findRewards(array $rewardIds): array
- {
- return array_values(array_filter($this->rewards, function (array $reward) use ($rewardIds) {
- return in_array((int)$reward['reward_id'], $rewardIds, true);
- }));
- }
- public function updateRewardStatus(
- int $rewardId,
- string $status,
- string $actor,
- string $reasonCode = null
- ): void {
- $this->rewards[$rewardId]['status'] = $status;
- $this->rewards[$rewardId]['review_by'] = $actor;
- $this->rewards[$rewardId]['reason_code'] = $reasonCode;
- }
- public function payReward(array $reward): void
- {
- $this->mails[$reward['referrer_id']][] = [
- 'title' => 'Claim your $60 referral reward',
- 'text' => 'Reward: $60',
- 'amount' => $reward['reward_each'],
- ];
- $this->mails[$reward['invitee_id']][] = [
- 'title' => 'Claim your $60 welcome reward',
- 'text' => 'Welcome! Your referral reward is ready to claim. '
- . 'You signed up with an invite and made your first deposit of $120. '
- . 'Reward: $60 (50% of your first deposit). '
- . 'Tap Claim to add it to your balance.',
- 'amount' => $reward['reward_each'],
- ];
- }
- public function sendRejectMail(array $reward, string $reason): void
- {
- $this->mails[$reward['referrer_id']][] = [
- 'title' => 'Your referral reward could not be approved',
- 'text' => 'Reason: ' . $reason,
- 'amount' => 0,
- ];
- $this->mails[$reward['invitee_id']][] = [
- 'title' => 'Welcome reward not approved',
- 'text' => 'Your welcome reward could not be approved.' . "\n"
- . 'Reason: ' . $reason . '.' . "\n"
- . 'If you think this is a mistake, contact support.',
- 'amount' => 0,
- ];
- }
- public function clawbackReward(array $reward, bool $banUsers): void
- {
- $this->clawedBack[$reward['referrer_id']] = ($this->clawedBack[$reward['referrer_id']] ?? 0) + $reward['reward_each'];
- $this->clawedBack[$reward['invitee_id']] = ($this->clawedBack[$reward['invitee_id']] ?? 0) + $reward['reward_each'];
- if ($banUsers) {
- $this->banned[] = $reward['referrer_id'];
- $this->banned[] = $reward['invitee_id'];
- }
- }
- public function writeAudit(
- int $rewardId,
- string $actor,
- string $action,
- ?string $reasonCode,
- ?string $beforeStatus,
- ?string $afterStatus,
- array $payload = []
- ): void {
- $this->audits[] = compact(
- 'rewardId',
- 'actor',
- 'action',
- 'reasonCode',
- 'beforeStatus',
- 'afterStatus',
- 'payload'
- );
- }
- public function queue(array $filters): array
- {
- return [];
- }
- public function kpi(): array
- {
- return [];
- }
- public function auditLogs($filters): array
- {
- return [];
- }
- }
|