Create tests for app/actions/tabulation/RankAuditionEntries
This commit is contained in:
parent
157e2f496a
commit
1a3d88bfa8
|
|
@ -27,7 +27,7 @@ class RankAuditionEntries
|
|||
public function __invoke(Audition $audition, string $rank_type): Collection|Entry
|
||||
{
|
||||
if ($rank_type !== 'seating' && $rank_type !== 'advancement') {
|
||||
throw new AuditionAdminException('Invalid rank type: '.$rank_type.' (must be seating or advancement)');
|
||||
throw new AuditionAdminException('Invalid rank type (must be seating or advancement)');
|
||||
}
|
||||
|
||||
$cache_duration = 15;
|
||||
|
|
@ -87,7 +87,7 @@ class RankAuditionEntries
|
|||
|
||||
private function get_advancement_ranks(Audition $audition): Collection|Entry
|
||||
{
|
||||
return $audition->entries()
|
||||
$sortedEntries = $audition->entries()
|
||||
->whereHas('totalScore')
|
||||
->with('totalScore')
|
||||
->with('student.school')
|
||||
|
|
@ -106,5 +106,12 @@ class RankAuditionEntries
|
|||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.advancement_subscore_totals, "$[9]"), -999999) DESC')
|
||||
->select('entries.*')
|
||||
->get();
|
||||
$n = 1;
|
||||
foreach ($sortedEntries as $entry) {
|
||||
$entry->advancementRank = $n;
|
||||
$n++;
|
||||
}
|
||||
|
||||
return $sortedEntries;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ class EntryTotalScore extends Model
|
|||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
protected $casts = [
|
||||
'seating_subscore_totals' => 'json',
|
||||
'advancement_subscore_totals' => 'json',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,360 @@
|
|||
<?php
|
||||
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
|
||||
use App\Actions\Tabulation\RankAuditionEntries;
|
||||
use App\Exceptions\AuditionAdminException;
|
||||
use App\Models\Audition;
|
||||
use App\Models\BonusScoreDefinition;
|
||||
use App\Models\Entry;
|
||||
use App\Models\EntryTotalScore;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
$this->audition = Audition::factory()->create(['minimum_grade' => 1, 'maximum_grade' => 14]);
|
||||
$this->entries = Entry::factory()->count(10)->create(['audition_id' => $this->audition->id]);
|
||||
$this->ranker = app(RankAuditionEntries::class);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
cache()->flush();
|
||||
});
|
||||
|
||||
it('throws an exception if an invalid rank type is specified', function () {
|
||||
($this->ranker)($this->audition, 'bababoey');
|
||||
})->throws(AuditionAdminException::class, 'Invalid rank type (must be seating or advancement)');
|
||||
|
||||
// Test Rank for Seating
|
||||
it('ranks entries for seating if there are no ties', function () {
|
||||
// entry 0 will be second place
|
||||
$score0 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[0]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 50,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 1 will be third place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[1]->id,
|
||||
'seating_total' => 25,
|
||||
'advancement_total' => 75,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 2 will be first place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[2]->id,
|
||||
'seating_total' => 75,
|
||||
'advancement_total' => 25,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
$sortedEntries = ($this->ranker)($this->audition, 'seating');
|
||||
expect($sortedEntries[0]->id)->toEqual($this->entries[2]->id)
|
||||
->and($sortedEntries[1]->id)->toEqual($this->entries[0]->id)
|
||||
->and($sortedEntries[2]->id)->toEqual($this->entries[1]->id);
|
||||
});
|
||||
|
||||
it('makes use of bonus scores when set', function () {
|
||||
$bonusScoreDefinition = BonusScoreDefinition::create([
|
||||
'name' => 'bonus',
|
||||
'max_score' => 100,
|
||||
'weight' => 1,
|
||||
'for_seating' => 1,
|
||||
'for_attendance' => 0,
|
||||
]);
|
||||
$bonusScoreDefinition->auditions()->attach($this->audition);
|
||||
DB::table('bonus_scores')->insert([
|
||||
'entry_id' => $this->entries[2]->id,
|
||||
'user_id' => 1,
|
||||
'originally_scored_entry' => $this->entries[2]->id,
|
||||
'score' => 100,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
// entry 0 will be second place
|
||||
$score0 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[0]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 50,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 1 will be third place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[1]->id,
|
||||
'seating_total' => 25,
|
||||
'advancement_total' => 75,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 2 will be first place
|
||||
$score2 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[2]->id,
|
||||
'seating_total' => 2,
|
||||
'advancement_total' => 25,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
'bonus_total' => 100,
|
||||
]);
|
||||
$sortedEntries = ($this->ranker)($this->audition, 'seating');
|
||||
expect($sortedEntries[0]->id)->toEqual($this->entries[2]->id)
|
||||
->and($sortedEntries[1]->id)->toEqual($this->entries[0]->id)
|
||||
->and($sortedEntries[2]->id)->toEqual($this->entries[1]->id);
|
||||
});
|
||||
|
||||
it('assigns a seatingRank property to each entry that is scored', function () {
|
||||
// entry 0 will be second place
|
||||
$score0 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[0]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 50,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 1 will be third place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[1]->id,
|
||||
'seating_total' => 25,
|
||||
'advancement_total' => 75,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 2 will be first place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[2]->id,
|
||||
'seating_total' => 75,
|
||||
'advancement_total' => 25,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
$sortedEntries = ($this->ranker)($this->audition, 'seating');
|
||||
expect($sortedEntries[0]->seatingRank)->toEqual(1)
|
||||
->and($sortedEntries[1]->seatingRank)->toEqual(2)
|
||||
->and($sortedEntries[2]->seatingRank)->toEqual(3)
|
||||
->and($sortedEntries->count())->toEqual(3);
|
||||
});
|
||||
|
||||
it('skips a declined entry when assigning seatingRank properties', function () {
|
||||
// entry 0 will be second place
|
||||
$score0 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[0]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 50,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 1 will be third place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[1]->id,
|
||||
'seating_total' => 25,
|
||||
'advancement_total' => 75,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 2 will be first place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[2]->id,
|
||||
'seating_total' => 75,
|
||||
'advancement_total' => 25,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
$this->entries[0]->addFlag('declined');
|
||||
$sortedEntries = ($this->ranker)($this->audition, 'seating');
|
||||
expect($sortedEntries[0]->seatingRank)->toEqual(1)
|
||||
->and($sortedEntries[1]->seatingRank)->toEqual('declined')
|
||||
->and($sortedEntries[2]->seatingRank)->toEqual(2);
|
||||
});
|
||||
|
||||
it('uses the second subscore as a second tiebreaker for seating', function () {
|
||||
// entry 0 will be second place
|
||||
$score0 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[0]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 50,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 1 will be third place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[1]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 75,
|
||||
'seating_subscore_totals' => [5, 4],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 2 will be first place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[2]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 25,
|
||||
'seating_subscore_totals' => [5, 6],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
$sortedEntries = ($this->ranker)($this->audition, 'seating');
|
||||
expect($sortedEntries[0]->id)->toEqual($this->entries[2]->id)
|
||||
->and($sortedEntries[1]->id)->toEqual($this->entries[0]->id)
|
||||
->and($sortedEntries[2]->id)->toEqual($this->entries[1]->id);
|
||||
});
|
||||
|
||||
it('uses the first subscore as a tiebreaker for seating', function () {
|
||||
// entry 0 will be second place
|
||||
$score0 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[0]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 50,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 1 will be third place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[1]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 75,
|
||||
'seating_subscore_totals' => [4, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 2 will be first place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[2]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 25,
|
||||
'seating_subscore_totals' => [6, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
$sortedEntries = ($this->ranker)($this->audition, 'seating');
|
||||
expect($sortedEntries[0]->id)->toEqual($this->entries[2]->id)
|
||||
->and($sortedEntries[1]->id)->toEqual($this->entries[0]->id)
|
||||
->and($sortedEntries[2]->id)->toEqual($this->entries[1]->id);
|
||||
});
|
||||
|
||||
// Test Rank for Advancement
|
||||
it('ranks entries for advancement if there are no ties', function () {
|
||||
// entry 0 will be second place
|
||||
$score0 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[0]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 50,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 1 will be third place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[1]->id,
|
||||
'seating_total' => 75,
|
||||
'advancement_total' => 25,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 2 will be first place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[2]->id,
|
||||
'seating_total' => 25,
|
||||
'advancement_total' => 75,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
$sortedEntries = ($this->ranker)($this->audition, 'advancement');
|
||||
expect($sortedEntries[0]->id)->toEqual($this->entries[2]->id)
|
||||
->and($sortedEntries[1]->id)->toEqual($this->entries[0]->id)
|
||||
->and($sortedEntries[2]->id)->toEqual($this->entries[1]->id);
|
||||
});
|
||||
|
||||
it('assigns a advancementRank property to each entry that is scored', function () {
|
||||
// entry 0 will be second place
|
||||
$score0 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[0]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 50,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 1 will be third place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[1]->id,
|
||||
'seating_total' => 75,
|
||||
'advancement_total' => 25,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 2 will be first place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[2]->id,
|
||||
'seating_total' => 25,
|
||||
'advancement_total' => 75,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
$sortedEntries = ($this->ranker)($this->audition, 'advancement');
|
||||
expect($sortedEntries[0]->advancementRank)->toEqual(1)
|
||||
->and($sortedEntries[1]->advancementRank)->toEqual(2)
|
||||
->and($sortedEntries[2]->advancementRank)->toEqual(3)
|
||||
->and($sortedEntries->count())->toEqual(3);
|
||||
});
|
||||
|
||||
it('uses the second subscore as a second tiebreaker for advancement', function () {
|
||||
// entry 0 will be second place
|
||||
$score0 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[0]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 50,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 1 will be third place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[1]->id,
|
||||
'seating_total' => 75,
|
||||
'advancement_total' => 50,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 4],
|
||||
]);
|
||||
// entry 2 will be first place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[2]->id,
|
||||
'seating_total' => 25,
|
||||
'advancement_total' => 50,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 6],
|
||||
]);
|
||||
$sortedEntries = ($this->ranker)($this->audition, 'advancement');
|
||||
expect($sortedEntries[0]->id)->toEqual($this->entries[2]->id)
|
||||
->and($sortedEntries[1]->id)->toEqual($this->entries[0]->id)
|
||||
->and($sortedEntries[2]->id)->toEqual($this->entries[1]->id);
|
||||
});
|
||||
|
||||
it('uses the first subscore as a tiebreaker for advancement', function () {
|
||||
// entry 0 will be second place
|
||||
$score0 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[0]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 50,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [5, 5],
|
||||
]);
|
||||
// entry 1 will be third place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[1]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 50,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [4, 5],
|
||||
]);
|
||||
// entry 2 will be first place
|
||||
$score1 = EntryTotalScore::create([
|
||||
'entry_id' => $this->entries[2]->id,
|
||||
'seating_total' => 50,
|
||||
'advancement_total' => 50,
|
||||
'seating_subscore_totals' => [5, 5],
|
||||
'advancement_subscore_totals' => [6, 5],
|
||||
]);
|
||||
$sortedEntries = ($this->ranker)($this->audition, 'advancement');
|
||||
expect($sortedEntries[0]->id)->toEqual($this->entries[2]->id)
|
||||
->and($sortedEntries[1]->id)->toEqual($this->entries[0]->id)
|
||||
->and($sortedEntries[2]->id)->toEqual($this->entries[1]->id);
|
||||
});
|
||||
Loading…
Reference in New Issue