Create tests for app/actions/tabulation/TotalEntryScore

This commit is contained in:
Matt Young 2025-07-02 10:29:21 -05:00
parent 53ccc5a7a3
commit c67c1ad79c
3 changed files with 216 additions and 9 deletions

View File

@ -19,7 +19,6 @@ class TotalEntryScores
public function __invoke(Entry $entry, bool $force_recalculation = false): void
{
// TODO Verify accuracy of calculations, particularly for olympic scoring
if ($force_recalculation) {
EntryTotalScore::where('entry_id', $entry->id)->delete();
}
@ -34,17 +33,19 @@ class TotalEntryScores
// deal with seating scores
// TODO: Consider a rewrite to pull the scoreSheets from the entry model so they may be preloaded
$scoreSheets = ScoreSheet::where('entry_id', $entry->id)->orderBy('seating_total', 'desc')->get();
// bail out if there are not enough score sheets
$assignedJudges = $entry->audition->judges()->count();
if ($scoreSheets->count() == 0 || $scoreSheets->count() < $assignedJudges) {
return;
}
if (auditionSetting('olympic_scoring' && $scoreSheets->count() > 2)) {
if (auditionSetting('olympic_scoring') && $scoreSheets->count() > 2) {
// under olympic scoring, drop the first and last element
$scoreSheets->shift();
$scoreSheets->pop();
}
$newTotaledScore->seating_total = $scoreSheets->avg('seating_total');
$newTotaledScore->seating_total = round($scoreSheets->avg('seating_total'), 6);
$seatingSubscores = $requiredSubscores
->filter(fn ($subscore) => $subscore->for_seating == true)
->sortBy('tiebreak_order');
@ -54,18 +55,18 @@ class TotalEntryScores
foreach ($scoreSheets as $scoreSheet) {
$runningTotal += $scoreSheet->subscores[$subscore->id]['score'];
}
$total_seating_subscores[] = $runningTotal / $scoreSheets->count();
$total_seating_subscores[] = round($runningTotal / $scoreSheets->count(), 4);
}
$newTotaledScore->seating_subscore_totals = $total_seating_subscores;
// deal with advancement scores
$scoreSheets = ScoreSheet::where('entry_id', $entry->id)->orderBy('advancement_total', 'desc')->get();
if (auditionSetting('olympic_scoring' && $scoreSheets->count() > 2)) {
if (auditionSetting('olympic_scoring') && $scoreSheets->count() > 2) {
// under olympic scoring, drop the first and last element
$scoreSheets->shift();
$scoreSheets->pop();
}
$newTotaledScore->advancement_total = $scoreSheets->avg('advancement_total');
$newTotaledScore->advancement_total = round($scoreSheets->avg('advancement_total'), 6);
$advancement_subscores = $requiredSubscores
->filter(fn ($subscore) => $subscore->for_advance == true)
->sortBy('tiebreak_order');
@ -75,7 +76,7 @@ class TotalEntryScores
foreach ($scoreSheets as $scoreSheet) {
$runningTotal += $scoreSheet->subscores[$subscore->id]['score'];
}
$total_advancement_subscores[] = $runningTotal / $scoreSheets->count();
$total_advancement_subscores[] = round($runningTotal / $scoreSheets->count(), 4);
}
$newTotaledScore->advancement_subscore_totals = $total_advancement_subscores;

View File

@ -0,0 +1,206 @@
<?php
/** @noinspection PhpUnhandledExceptionInspection */
use App\Actions\Tabulation\EnterScore;
use App\Actions\Tabulation\TotalEntryScores;
use App\Models\Audition;
use App\Models\BonusScore;
use App\Models\Entry;
use App\Models\EntryTotalScore;
use App\Models\ScoreSheet;
use App\Models\SubscoreDefinition;
use App\Models\User;
use App\Settings;
use Database\Seeders\AuditionWithScoringGuideAndRoom;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
beforeEach(function () {
(new AuditionWithScoringGuideAndRoom)->run();
SubscoreDefinition::where('id', '<', 900)->delete();
$this->audition = Audition::first();
$this->judge1 = User::factory()->create();
$this->judge2 = User::factory()->create();
$this->judge3 = User::factory()->create();
$this->judge4 = User::factory()->create();
$this->judge5 = User::factory()->create();
$this->audition->judges()->attach([
$this->judge1->id,
$this->judge2->id,
$this->judge3->id,
$this->judge4->id,
$this->judge5->id,
]);
$this->entry = Entry::factory()->create(['audition_id' => $this->audition->id]);
$this->judge1Subscores = [
1001 => 45,
1002 => 87,
1003 => 34,
1004 => 86,
1005 => 75,
];
$this->judge2Subscores = [
1001 => 75,
1002 => 69,
1003 => 56,
1004 => 89,
1005 => 45,
];
$this->judge3Subscores = [
1001 => 78,
1002 => 56,
1003 => 98,
1004 => 34,
1005 => 56,
];
$this->judge4Subscores = [
1001 => 67,
1002 => 45,
1003 => 98,
1004 => 43,
1005 => 89,
];
$this->judge5Subscores = [
1001 => 45,
1002 => 97,
1003 => 34,
1004 => 97,
1005 => 78,
];
$this->scribe = app(EnterScore::class);
$this->calculator = app(TotalEntryScores::class);
});
test('scores correctly for non-olympic scoring', function () {
Settings::set('olympic_scoring', false);
($this->scribe)($this->judge1, $this->entry, $this->judge1Subscores);
($this->scribe)($this->judge2, $this->entry, $this->judge2Subscores);
($this->scribe)($this->judge3, $this->entry, $this->judge3Subscores);
($this->scribe)($this->judge4, $this->entry, $this->judge4Subscores);
($this->scribe)($this->judge5, $this->entry, $this->judge5Subscores);
$judges = [
$this->judge1,
$this->judge2,
$this->judge3,
$this->judge4,
$this->judge5,
];
$expectedSeatingTotals = [
1 => 68.125,
2 => 74,
3 => 61,
4 => 60.25,
5 => 74.75,
];
$expectedAdvancementTotals = [
1 => 71.875,
2 => 70.25,
3 => 58.25,
4 => 63,
5 => 78.875,
];
foreach ($judges as $judge) {
$scoreSheet = ScoreSheet::where('entry_id', $this->entry->id)->where('user_id', $judge->id)->first();
expect($scoreSheet->seating_total)->toBe($expectedSeatingTotals[$judge->id])
->and($scoreSheet->advancement_total)->toBe($expectedAdvancementTotals[$judge->id]);
}
($this->calculator)($this->entry);
$totalScore = EntryTotalScore::where('entry_id', $this->entry->id)->first();
expect($totalScore->seating_subscore_totals)->toBe([69.8, 70.8, 64, 62])
->and($totalScore->advancement_subscore_totals)->toBe([68.6, 69.8, 70.8, 64])
->and($totalScore->seating_total)->toBe(67.625)
->and($totalScore->advancement_total)->toBe(68.45);
});
test('scores correctly for olympic scoring', function () {
Settings::set('olympic_scoring', true);
($this->scribe)($this->judge1, $this->entry, $this->judge1Subscores);
($this->scribe)($this->judge2, $this->entry, $this->judge2Subscores);
($this->scribe)($this->judge3, $this->entry, $this->judge3Subscores);
($this->scribe)($this->judge4, $this->entry, $this->judge4Subscores);
($this->scribe)($this->judge5, $this->entry, $this->judge5Subscores);
$judges = [
$this->judge1,
$this->judge2,
$this->judge3,
$this->judge4,
$this->judge5,
];
$expectedSeatingTotals = [
1 => 68.125,
2 => 74,
3 => 61,
4 => 60.25,
5 => 74.75,
];
$expectedAdvancementTotals = [
1 => 71.875,
2 => 70.25,
3 => 58.25,
4 => 63,
5 => 78.875,
];
foreach ($judges as $judge) {
$scoreSheet = ScoreSheet::where('entry_id', $this->entry->id)->where('user_id', $judge->id)->first();
expect($scoreSheet->seating_total)->toBe($expectedSeatingTotals[$judge->id])
->and($scoreSheet->advancement_total)->toBe($expectedAdvancementTotals[$judge->id]);
}
($this->calculator)($this->entry);
$totalScore = EntryTotalScore::where('entry_id', $this->entry->id)->first();
expect($totalScore->seating_subscore_totals)->toBe([69.6667, 70.6667, 62.6667, 66])
->and($totalScore->advancement_subscore_totals)->toBe([69.6667, 72.6667, 67, 62.6667])
->and($totalScore->seating_total)->toBe(67.708333)
->and($totalScore->advancement_total)->toBe(68.375);
});
test('it correctly brings in bonus scores', function () {
Settings::set('olympic_scoring', false);
($this->scribe)($this->judge1, $this->entry, $this->judge1Subscores);
($this->scribe)($this->judge2, $this->entry, $this->judge2Subscores);
($this->scribe)($this->judge3, $this->entry, $this->judge3Subscores);
($this->scribe)($this->judge4, $this->entry, $this->judge4Subscores);
($this->scribe)($this->judge5, $this->entry, $this->judge5Subscores);
$judges = [
$this->judge1,
$this->judge2,
$this->judge3,
$this->judge4,
$this->judge5,
];
$expectedSeatingTotals = [
1 => 68.125,
2 => 74,
3 => 61,
4 => 60.25,
5 => 74.75,
];
$expectedAdvancementTotals = [
1 => 71.875,
2 => 70.25,
3 => 58.25,
4 => 63,
5 => 78.875,
];
foreach ($judges as $judge) {
$scoreSheet = ScoreSheet::where('entry_id', $this->entry->id)->where('user_id', $judge->id)->first();
expect($scoreSheet->seating_total)->toBe($expectedSeatingTotals[$judge->id])
->and($scoreSheet->advancement_total)->toBe($expectedAdvancementTotals[$judge->id]);
}
$bonusScore = BonusScore::create([
'entry_id' => $this->entry->id,
'user_id' => $this->judge1,
'originally_scored_entry' => $this->entry->id,
'score' => 6,
]);
($this->calculator)($this->entry);
$totalScore = EntryTotalScore::where('entry_id', $this->entry->id)->first();
expect($totalScore->seating_subscore_totals)->toBe([69.8, 70.8, 64, 62])
->and($totalScore->advancement_subscore_totals)->toBe([68.6, 69.8, 70.8, 64])
->and($totalScore->seating_total)->toBe(67.625)
->and($totalScore->advancement_total)->toBe(68.45)
->and($totalScore->bonus_total)->toBe(6)
->and($totalScore->seating_total_with_bonus)->toBe(73.625);
});

View File

@ -13,7 +13,7 @@
use App\Models\User;
use App\Settings;
use Illuminate\Foundation\Testing\TestCase;
use function Pest\Laravel\actingAs;
use function Pest\Laravel\artisan;
@ -80,5 +80,5 @@ uses()->beforeEach(function () {
Settings::set('payment_city', 'Washington');
Settings::set('payment_state', 'DC');
Settings::set('payment_zip', '20500');
Settings::set('olympic_scoring', 1);
})->in('Feature');