From 30862271f259c94b3df7116dee4958330db65c1c Mon Sep 17 00:00:00 2001 From: Matt Young Date: Tue, 1 Jul 2025 02:30:06 -0500 Subject: [PATCH] UpdateEntry tests --- app/Actions/Entries/UpdateEntry.php | 31 +-- .../Controllers/Admin/EntryController.php | 3 +- app/Observers/EntryObserver.php | 3 +- .../app/Actions/Entries/UpdateEntryTest.php | 196 ++++++++++++++++++ 4 files changed, 215 insertions(+), 18 deletions(-) create mode 100644 tests/Feature/app/Actions/Entries/UpdateEntryTest.php diff --git a/app/Actions/Entries/UpdateEntry.php b/app/Actions/Entries/UpdateEntry.php index 4926dc8..9d94992 100644 --- a/app/Actions/Entries/UpdateEntry.php +++ b/app/Actions/Entries/UpdateEntry.php @@ -2,6 +2,7 @@ namespace App\Actions\Entries; +use App\Exceptions\AuditionAdminException; use App\Exceptions\ManageEntryException; use App\Models\Audition; use App\Models\AuditLogEntry; @@ -27,7 +28,7 @@ class UpdateEntry /** * @throws ManageEntryException */ - public function __invoke(Entry $entry, array $updateData): void + public function __invoke(Entry|int $entry, array $updateData): void { $this->updateEntry($entry, $updateData); } @@ -41,7 +42,7 @@ class UpdateEntry $entry = Entry::find($entry); } if (! $entry || ! $entry->exists) { - throw new ManageEntryException('Invalid entry provided'); + throw new AuditionAdminException('Invalid entry provided'); } $this->entry = $entry; if (array_key_exists('for_seating', $updateData)) { @@ -81,34 +82,34 @@ class UpdateEntry $audition = Audition::find($audition); } if (! $audition || ! $audition->exists) { - throw new ManageEntryException('Invalid audition provided'); + throw new AuditionAdminException('Invalid audition provided'); } if ($this->entry->audition->hasFlag('seats_published')) { - throw new ManageEntryException('Cannot change the audition for an entry where seating for that entry\'s current audition is published'); + throw new AuditionAdminException('Cannot change the audition for an entry where seating for that entry\'s current audition is published'); } if ($this->entry->audition->hasFlag('advancement_published')) { - throw new ManageEntryException('Cannot change the audition for an entry where advancement for that entry\'s current audition is published'); + throw new AuditionAdminException('Cannot change the audition for an entry where advancement for that entry\'s current audition is published'); } if ($audition->hasFlag('seats_published')) { - throw new ManageEntryException('Cannot change the entry to an audition with published seating'); + throw new AuditionAdminException('Cannot change the entry to an audition with published seating'); } if ($audition->hasFlag('advancement_published')) { - throw new ManageEntryException('Cannot change the entry to an audition with published advancement'); + throw new AuditionAdminException('Cannot change the entry to an audition with published advancement'); } if ($this->entry->student->grade > $audition->maximum_grade) { - throw new ManageEntryException('The grade of the student exceeds the maximum for that audition'); + throw new AuditionAdminException('The student is too old to enter that audition'); } if ($this->entry->student->grade < $audition->minimum_grade) { - throw new ManageEntryException('The grade of the student does not meet the minimum for that audition'); + throw new AuditionAdminException('The student is too young to enter that audition'); } if ($this->entry->scoreSheets()->count() > 0) { - throw new ManageEntryException('Cannot change the audition for an entry with scores'); + throw new AuditionAdminException('Cannot change the audition for an entry with scores'); } if ($audition->id !== $this->entry->audition_id && Entry::where('student_id', $this->entry->student_id) ->where('audition_id', $audition->id)->exists()) { - throw new ManageEntryException('That student is already entered in that audition'); + throw new AuditionAdminException('That student is already entered in that audition'); } // Escape if we're not actually making a change if ($this->entry->audition_id == $audition->id) { @@ -139,13 +140,13 @@ class UpdateEntry } if ($forSeating) { if ($this->entry->audition->hasFlag('seats_published')) { - throw new ManageEntryException('Cannot add seating to an entry in an audition where seats are published'); + throw new AuditionAdminException('Cannot add seating to an entry in an audition where seats are published'); } $this->entry->for_seating = 1; $this->log_message .= 'Entry '.$this->entry->id.' is entered for seating '.'
'; } else { if ($this->entry->audition->hasFlag('seats_published')) { - throw new ManageEntryException('Cannot remove seating from an entry in an audition where seats are published'); + throw new AuditionAdminException('Cannot remove seating from an entry in an audition where seats are published'); } $this->entry->for_seating = 0; $this->log_message .= 'Entry '.$this->entry->id.' is NOT entered for seating '.'
'; @@ -162,13 +163,13 @@ class UpdateEntry } if ($forAdvancement) { if ($this->entry->audition->hasFlag('advancement_published')) { - throw new ManageEntryException('Cannot add advancement to an entry in an audition where advancement is published'); + throw new AuditionAdminException('Cannot add advancement to an entry in an audition where advancement is published'); } $this->entry->for_advancement = 1; $this->log_message .= 'Entry '.$this->entry->id.' is entered for '.auditionSetting('advanceTo').'
'; } else { if ($this->entry->audition->hasFlag('advancement_published')) { - throw new ManageEntryException('Cannot remove advancement from an entry in an audition where advancement is published'); + throw new AuditionAdminException('Cannot remove advancement from an entry in an audition where advancement is published'); } $this->entry->for_advancement = 0; $this->log_message .= 'Entry '.$this->entry->id.' is NOT entered for '.auditionSetting('advanceTo').'
'; diff --git a/app/Http/Controllers/Admin/EntryController.php b/app/Http/Controllers/Admin/EntryController.php index 79ac829..b25e840 100644 --- a/app/Http/Controllers/Admin/EntryController.php +++ b/app/Http/Controllers/Admin/EntryController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Admin; use App\Actions\Entries\CreateEntry; use App\Actions\Entries\UpdateEntry; +use App\Exceptions\AuditionAdminException; use App\Exceptions\ManageEntryException; use App\Http\Controllers\Controller; use App\Models\Audition; @@ -192,7 +193,7 @@ class EntryController extends Controller } try { $updater($entry, $validData); - } catch (ManageEntryException $e) { + } catch (AuditionAdminException $e) { return redirect()->route('admin.entries.index')->with('error', $e->getMessage()); } if ($validData['late_fee_waived']) { diff --git a/app/Observers/EntryObserver.php b/app/Observers/EntryObserver.php index 72268bc..e89e4ed 100644 --- a/app/Observers/EntryObserver.php +++ b/app/Observers/EntryObserver.php @@ -34,8 +34,7 @@ class EntryObserver // Update doubler table when an entry is updated Doubler::syncForEvent($entry->audition->event_id); if ($entry->wasChanged('audition_id')) { - $originalData = $entry->getOriginal(); - Doubler::syncForEvent($originalData->audition->event_id); + Doubler::syncDoublers(); } } diff --git a/tests/Feature/app/Actions/Entries/UpdateEntryTest.php b/tests/Feature/app/Actions/Entries/UpdateEntryTest.php new file mode 100644 index 0000000..6cc237f --- /dev/null +++ b/tests/Feature/app/Actions/Entries/UpdateEntryTest.php @@ -0,0 +1,196 @@ +entryScribe = app(UpdateEntry::class); + $this->audition = Audition::factory()->create( + [ + 'minimum_grade' => 9, 'maximum_grade' => 10, + 'name' => 'Flute', + ]); + $this->student = Student::factory()->create(['grade' => 9]); + $this->entry = Entry::factory()->create([ + 'student_id' => $this->student->id, + 'audition_id' => $this->audition->id, + ]); +}); + +it('throws an exception when an invalid entry is provided', function () { + ($this->entryScribe)(35, []); +})->throws(AuditionAdminException::class, 'Invalid entry provided'); + +it('throws an exception when a non-existent entry is provided', function () { + $newEntry = Entry::factory()->make(); + ($this->entryScribe)($newEntry, []); +})->throws(AuditionAdminException::class, 'Invalid entry provided'); + +it('will not modify an entry in an audition with published seats', function () { + $this->entry->audition->addFlag('seats_published'); + $newAudition = Audition::factory()->create(['event_id' => $this->entry->audition->event_id]); + ($this->entryScribe)($this->entry, ['audition_id' => $newAudition->id]); +})->throws(AuditionAdminException::class, + 'Cannot change the audition for an entry where seating for that entry\'s current audition is published'); + +it('will not modify an entry in an audition with published advancement', function () { + $this->entry->audition->addFlag('advancement_published'); + $newAudition = Audition::factory()->create(['event_id' => $this->entry->audition->event_id]); + ($this->entryScribe)($this->entry, ['audition_id' => $newAudition->id]); +})->throws(AuditionAdminException::class, + 'Cannot change the audition for an entry where advancement for that entry\'s current audition is published'); + +it('will not move an entry to an audition with published seating', function () { + $newAudition = Audition::factory()->create(['event_id' => $this->entry->audition->event_id]); + $newAudition->addFlag('seats_published'); + ($this->entryScribe)($this->entry, ['audition_id' => $newAudition->id]); +})->throws(AuditionAdminException::class, 'Cannot change the entry to an audition with published seating'); + +it('will not move an entry to an audition with published advancement', function () { + $newAudition = Audition::factory()->create(['event_id' => $this->entry->audition->event_id]); + $newAudition->addFlag('advancement_published'); + ($this->entryScribe)($this->entry, ['audition_id' => $newAudition->id]); +})->throws(AuditionAdminException::class, 'Cannot change the entry to an audition with published advancement'); + +it('will not move to an audition for which the student is too young', function () { + $newAudition = Audition::factory()->create([ + 'event_id' => $this->entry->audition->event_id, + 'minimum_grade' => 10, + ]); + $this->entry->student->update(['grade' => 9]); + ($this->entryScribe)($this->entry, ['audition_id' => $newAudition->id]); +})->throws(AuditionAdminException::class, 'The student is too young to enter that audition')->skip('test needs work'); + +it('will not move to an audition for which the student is too old', function () { + $newAudition = Audition::factory()->create([ + 'event_id' => $this->entry->audition->event_id, + 'maximum_grade' => 8, + ]); + $this->entry->student->update(['grade' => 9]); + ($this->entryScribe)($this->entry, ['audition_id' => $newAudition->id]); +})->throws(AuditionAdminException::class, 'The student is too old to enter that audition')->skip('test needs work'); + +it('will not change auditions for an entry with scores', function () { + $scoreFaker = app(FakeScoresForEntry::class); + $newAudition = Audition::factory()->create([ + 'event_id' => $this->entry->audition->event_id, + 'minimum_grade' => 9, + 'maximum_grade' => 10, + ]); + $scoringGuide = ScoringGuide::factory()->create(); + $judge1 = User::factory()->create(); + $judge2 = User::factory()->create(); + $room = Room::factory()->create(); + $room->addJudge($judge1); + $room->addJudge($judge2); + $this->entry->audition->update([ + 'scoring_guide_id' => $scoringGuide->id, + 'room_id' => $room->id, + 'order_in_room' => 1, + ]); + $scoreFaker($this->entry); + ($this->entryScribe)($this->entry, ['audition_id' => $newAudition->id]); +})->throws(AuditionAdminException::class, 'Cannot change the audition for an entry with scores'); + +it(('will not change the audition to one in which the student is already entered'), function () { + $newAudition = Audition::factory()->create(['minimum_grade' => 9, 'maximum_grade' => 10]); + Entry::create([ + 'student_id' => $this->student->id, + 'audition_id' => $newAudition->id, + ]); + ($this->entryScribe)($this->entry, ['audition_id' => $newAudition->id]); +})->throws(AuditionAdminException::class, 'That student is already entered in that audition'); + +it('will not set a draw number if the new audition is not drawn', function () { + $newAudition = Audition::factory()->create(['minimum_grade' => 9, 'maximum_grade' => 10]); + ($this->entryScribe)($this->entry, ['audition_id' => $newAudition->id]); + $this->entry->refresh(); + expect($this->entry->draw_number)->toBeNull(); +}); + +it('will assign the next draw number if the new audition is drawn', function () { + $newAudition = Audition::factory() + ->has(Entry::factory()->count(5)) + ->create(['minimum_grade' => 9, 'maximum_grade' => 10]); + AuditionFlag::create(['audition_id' => $newAudition->id, 'flag_name' => 'drawn']); + $n = 1; + foreach ($newAudition->entries as $entry) { + $entry->update(['draw_number' => $n]); + $n++; + } + ($this->entryScribe)($this->entry, ['audition_id' => $newAudition->id]); + expect($this->entry->draw_number)->toBe($n); +}); + +it('can change and entries audition', function () { + $newAudition = Audition::factory()->create(['minimum_grade' => 9, 'maximum_grade' => 10]); + ($this->entryScribe)($this->entry, ['audition' => $newAudition]); + $this->entry->refresh(); + $freshEntry = Entry::find($this->entry->id); + expect($freshEntry->audition_id)->toBe($newAudition->id); +}); + +it('can remove forAdvancement from the entry', function () { + ($this->entryScribe)($this->entry, ['for_advancement' => false]); + expect($this->entry->for_advancement)->toBeFalsy(); +}); + +it('can add forAdvancement to the entry', function () { + $this->entry->update(['for_advancement' => false]); + expect($this->entry->for_advancement)->toBeFalsy(); + ($this->entryScribe)($this->entry, ['for_advancement' => true]); + expect($this->entry->for_advancement)->toBeTruthy(); +}); + +it('cannot remove forAdvancement if advancement is published', function () { + $this->entry->audition->addFlag('advancement_published'); + ($this->entryScribe)($this->entry, ['for_advancement' => false]); +})->throws(AuditionAdminException::class, + 'Cannot remove advancement from an entry in an audition where advancement is published'); + +it('cannot add forAdvancement if advancement is published', function () { + $this->entry->update(['for_advancement' => false]); + expect($this->entry->for_advancement)->toBeFalsy(); + $this->entry->audition->addFlag('advancement_published'); + ($this->entryScribe)($this->entry, ['for_advancement' => true]); +})->throws(AuditionAdminException::class, + 'Cannot add advancement to an entry in an audition where advancement is published'); + +it('can remove forSeating from the entry', function () { + ($this->entryScribe)($this->entry, ['for_seating' => false]); + expect($this->entry->for_seating)->toBeFalsy(); +}); + +it('can add forSeating to the entry', function () { + $this->entry->update(['for_seating' => false]); + expect($this->entry->for_seating)->toBeFalsy(); + ($this->entryScribe)($this->entry, ['for_seating' => true]); + expect($this->entry->for_seating)->toBeTruthy(); +}); + +it('cannot remove forSeating if seating is published', function () { + $this->entry->audition->addFlag('seats_published'); + ($this->entryScribe)($this->entry, ['for_seating' => false]); +})->throws(AuditionAdminException::class, + 'Cannot remove seating from an entry in an audition where seats are published'); + +it('cannot add forSeating if seating is published', function () { + $this->entry->update(['for_seating' => false]); + expect($this->entry->for_seating)->toBeFalsy(); + $this->entry->audition->addFlag('seats_published'); + ($this->entryScribe)($this->entry, ['for_seating' => true]); +})->throws(AuditionAdminException::class, + 'Cannot add seating to an entry in an audition where seats are published');