mivita/tests/Unit/Incentive/IncentiveTrackerTest.php
2026-04-10 17:15:27 +02:00

115 lines
3.4 KiB
PHP

<?php
use App\Models\Incentive;
use App\Models\IncentiveParticipant;
use App\Models\IncentivePointsLog;
use App\Services\Incentive\IncentiveTracker;
// === Ranking Logic ===
it('ranking logic assigns ranks by total_points descending', function () {
$p1 = new IncentiveParticipant(['total_points' => 1000]);
$p2 = new IncentiveParticipant(['total_points' => 3000]);
$p3 = new IncentiveParticipant(['total_points' => 2000]);
// Simulate the ranking logic from IncentiveTracker::updateRanking
$participants = collect([$p1, $p2, $p3])->sortByDesc('total_points')->values();
$rank = 1;
foreach ($participants as $p) {
$p->rank = $rank;
$rank++;
}
expect($p2->rank)->toBe(1)
->and($p3->rank)->toBe(2)
->and($p1->rank)->toBe(3);
});
// === Determine Log Type ===
it('determines abo type for shop orders', function () {
$method = new ReflectionMethod(IncentiveTracker::class, 'determineLogType');
$usv = new \App\Models\UserSalesVolume([
'status_turnover' => 2,
'status_points' => 1,
]);
expect($method->invoke(null, $usv))->toBe('abo');
});
it('determines abo type for customer-only points', function () {
$method = new ReflectionMethod(IncentiveTracker::class, 'determineLogType');
$usv = new \App\Models\UserSalesVolume([
'status_turnover' => 1,
'status_points' => 2,
]);
expect($method->invoke(null, $usv))->toBe('abo');
});
it('determines partner type for advisor orders', function () {
$method = new ReflectionMethod(IncentiveTracker::class, 'determineLogType');
$usv = new \App\Models\UserSalesVolume([
'status_turnover' => 1,
'status_points' => 1,
]);
expect($method->invoke(null, $usv))->toBe('partner');
});
// === Edge Cases ===
it('qualification changes correctly on increment/decrement', function () {
$incentive = new Incentive(['min_direct_partners' => 4, 'min_customer_abos' => 6]);
$participant = new IncentiveParticipant([
'qualified_partners' => 3,
'qualified_abos' => 6,
]);
$participant->setRelation('incentive', $incentive);
// Not qualified yet
expect($participant->checkQualification())->toBeFalse();
// Add one more partner
$participant->qualified_partners = 4;
expect($participant->checkQualification())->toBeTrue();
// Storno removes a partner
$participant->qualified_partners = 3;
expect($participant->checkQualification())->toBeFalse();
});
it('handles zero max_winners edge case', function () {
$incentive = new Incentive(['max_winners' => 1]);
$winner = new IncentiveParticipant(['is_qualified' => true, 'rank' => 1]);
$winner->setRelation('incentive', $incentive);
$loser = new IncentiveParticipant(['is_qualified' => true, 'rank' => 2]);
$loser->setRelation('incentive', $incentive);
expect($winner->isWinner())->toBeTrue()
->and($loser->isWinner())->toBeFalse();
});
it('points log total handles negative values for storno', function () {
$log = new IncentivePointsLog([
'points_onetime' => -600,
'points_accumulated' => 0,
]);
expect($log->getTotalPoints())->toBe(-600);
});
it('points log total handles mixed values', function () {
$log = new IncentivePointsLog([
'points_onetime' => 0,
'points_accumulated' => 250,
]);
expect($log->getTotalPoints())->toBe(250);
});