Create tests for app/actions/tabulation/EnterScore

This commit is contained in:
Matt Young 2025-07-02 03:30:55 -05:00
parent a924b3bf51
commit 53ccc5a7a3
2 changed files with 141 additions and 13 deletions

View File

@ -6,6 +6,7 @@
namespace App\Actions\Tabulation;
use App\Exceptions\AuditionAdminException;
use App\Exceptions\ScoreEntryException;
use App\Models\AuditLogEntry;
use App\Models\Entry;
@ -33,17 +34,17 @@ class EnterScore
$scores = collect($scores);
// Basic Validity Checks
if (! $user->exists()) {
throw new ScoreEntryException('User does not exist');
if (! User::where('id', $user->id)->exists()) {
throw new AuditionAdminException('User does not exist');
}
if (! $entry->exists()) {
throw new ScoreEntryException('Entry does not exist');
if (! Entry::where('id', $entry->id)->exists()) {
throw new AuditionAdminException('Entry does not exist');
}
if ($entry->audition->hasFlag('seats_published')) {
throw new ScoreEntryException('Cannot score an entry in an audition with published seats');
throw new AuditionAdminException('Cannot score an entry in an audition where seats are published');
}
if ($entry->audition->hasFlag('advancement_published')) {
throw new ScoreEntryException('Cannot score an entry in an audition with published advancement');
throw new AuditionAdminException('Cannot score an entry in an audition where advancement is published');
}
// Check that the specified user is assigned to judge this entry
@ -51,20 +52,20 @@ class EnterScore
->where('room_id', $entry->audition->room_id)
->where('user_id', $user->id)->exists();
if (! $check) {
throw new ScoreEntryException('This judge is not assigned to judge this entry');
throw new AuditionAdminException('This judge is not assigned to judge this entry');
}
// Check if a score already exists
if (! $scoreSheet) {
if (ScoreSheet::where('user_id', $user->id)->where('entry_id', $entry->id)->exists()) {
throw new ScoreEntryException('That judge has already entered scores for that entry');
throw new AuditionAdminException('That judge has already entered scores for that entry');
}
} else {
if ($scoreSheet->user_id !== $user->id) {
throw new ScoreEntryException('Existing score sheet is from a different judge');
throw new AuditionAdminException('Existing score sheet is from a different judge');
}
if ($scoreSheet->entry_id !== $entry->id) {
throw new ScoreEntryException('Existing score sheet is for a different entry');
throw new AuditionAdminException('Existing score sheet is for a different entry');
}
}
@ -76,16 +77,16 @@ class EnterScore
$advancementTotal = 0;
$advancementMaxPossible = 0;
if ($scores->count() !== $subscoresRequired->count()) {
throw new ScoreEntryException('Invalid number of scores');
throw new AuditionAdminException('Invalid number of scores');
}
foreach ($subscoresRequired as $subscore) {
// check that there is an element in the $scores collection with the key = $subscore->id
if (! $scores->keys()->contains($subscore->id)) {
throw new ScoreEntryException('Invalid Score Submission');
throw new AuditionAdminException('Invalid Score Submission');
}
if ($scores[$subscore->id] > $subscore->max_score) {
throw new ScoreEntryException('Supplied subscore exceeds maximum allowed');
throw new AuditionAdminException('Supplied subscore exceeds maximum allowed');
}
// Add subscore to the storage array

View File

@ -0,0 +1,127 @@
<?php
/** @noinspection PhpUnhandledExceptionInspection */
use App\Actions\Tabulation\EnterScore;
use App\Exceptions\AuditionAdminException;
use App\Models\Audition;
use App\Models\Entry;
use App\Models\ScoreSheet;
use App\Models\SubscoreDefinition;
use App\Models\User;
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->audition->judges()->attach([$this->judge1->id, $this->judge2->id]);
$this->entry1 = Entry::factory()->create(['audition_id' => $this->audition->id]);
$this->entry2 = Entry::factory()->create(['audition_id' => $this->audition->id]);
$this->scribe = app(EnterScore::class);
$this->possibleScoreArray = [
1001 => 10,
1002 => 11,
1003 => 12,
1004 => 13,
1005 => 14,
];
$this->anotherPossibleScoreArray = [
1001 => 20,
1002 => 21,
1003 => 22,
1004 => 23,
1005 => 24,
];
});
it('can enter a score', function () {
($this->scribe)($this->judge1, $this->entry1, $this->possibleScoreArray);
expect($this->entry1->scoreSheets()->count())->toBe(1)
->and($this->entry1->scoreSheets()->first()->seating_total)->toBe(11.875)
->and($this->entry1->scoreSheets()->first()->advancement_total)->toBe(12.375);
});
it('will not enter a score for a judge that does not exist', function () {
$fakeJudge = User::factory()->make();
($this->scribe)($fakeJudge, $this->entry1, $this->possibleScoreArray);
})->throws(AuditionAdminException::class, 'User does not exist');
it('will not enter a score for an entry that does not exist', function () {
$fakeEntry = Entry::factory()->make();
($this->scribe)($this->judge1, $fakeEntry, $this->possibleScoreArray);
})->throws(AuditionAdminException::class, 'Entry does not exist');
it('will not score an entry if the audition seats are published', function () {
$this->audition->addFlag('seats_published');
($this->scribe)($this->judge1, $this->entry1, $this->possibleScoreArray);
})->throws(AuditionAdminException::class, 'Cannot score an entry in an audition where seats are published');
it('will not score an entry if the audition advancement is published', function () {
$this->audition->addFlag('advancement_published');
($this->scribe)($this->judge1, $this->entry1, $this->possibleScoreArray);
})->throws(AuditionAdminException::class, 'Cannot score an entry in an audition where advancement is published');
it('will not score an entry if the judge is not assigned to judge the entry', function () {
$fakeJudge = User::factory()->create();
($this->scribe)($fakeJudge, $this->entry1, $this->possibleScoreArray);
})->throws(AuditionAdminException::class, 'This judge is not assigned to judge this entry');
it('can modify an existing score sheet', function () {
($this->scribe)($this->judge1, $this->entry1, $this->possibleScoreArray);
$scoreSheet = ScoreSheet::first();
($this->scribe)($this->judge1, $this->entry1, $this->anotherPossibleScoreArray, $scoreSheet);
expect($this->entry1->scoreSheets()->count())->toBe(1)
->and($this->entry1->scoreSheets()->first()->seating_total)->toBe(21.875)
->and($this->entry1->scoreSheets()->first()->advancement_total)->toBe(22.375);
});
it('will not change the judge on a score sheet', function () {
($this->scribe)($this->judge1, $this->entry1, $this->possibleScoreArray);
$scoreSheet = ScoreSheet::first();
($this->scribe)($this->judge2, $this->entry1, $this->anotherPossibleScoreArray, $scoreSheet);
})->throws(AuditionAdminException::class, 'Existing score sheet is from a different judge');
it('will not accept a second score sheet for a judge ane entry', function () {
($this->scribe)($this->judge1, $this->entry1, $this->possibleScoreArray);
($this->scribe)($this->judge1, $this->entry1, $this->anotherPossibleScoreArray);
})->throws(AuditionAdminException::class, 'That judge has already entered scores for that entry');
it('will not change the entry on a score sheet', function () {
($this->scribe)($this->judge1, $this->entry1, $this->possibleScoreArray);
$scoreSheet = ScoreSheet::first();
($this->scribe)($this->judge1, $this->entry2, $this->anotherPossibleScoreArray, $scoreSheet);
})->throws(AuditionAdminException::class, 'Existing score sheet is for a different entry');
it('will not accept an incorrect number of subscores', function () {
array_pop($this->possibleScoreArray);
($this->scribe)($this->judge1, $this->entry1, $this->possibleScoreArray);
})->throws(AuditionAdminException::class, 'Invalid number of scores');
it('will not accept an invalid subscores', function () {
array_pop($this->possibleScoreArray);
$this->possibleScoreArray[3001] = 100;
($this->scribe)($this->judge1, $this->entry1, $this->possibleScoreArray);
})->throws(AuditionAdminException::class, 'Invalid Score Submission');
it('will. not accept a subscore in excess of its maximum', function () {
$this->possibleScoreArray[1001] = 1500;
($this->scribe)($this->judge1, $this->entry1, $this->possibleScoreArray);
})->throws(AuditionAdminException::class, 'Supplied subscore exceeds maximum allowed');
it('removes a no-show flag from an entry', function () {
$this->entry1->addFlag('no_show');
($this->scribe)($this->judge1, $this->entry1, $this->possibleScoreArray);
expect($this->entry1->hasFlag('no_show'))->toBeFalse();
});
it('logs score entry', function () {
($this->scribe)($this->judge1, $this->entry1, $this->possibleScoreArray);
$logEntry = \App\Models\AuditLogEntry::latest()->first();
expect($logEntry->message)->toStartWith('Entered Score for entry id ');
});