From 0cb5981b7c5cc554194bfed187201050d4bdd405 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Wed, 17 Jul 2024 11:26:29 -0500 Subject: [PATCH] CreateEntry action functions properly #29 Creating an entry should check on the status of the draw and respond appropriately --- app/Actions/CreateEntry.php | 82 ++++++++++++++ app/Exceptions/CreateEntryException.php | 9 ++ tests/Feature/Actions/CreateEntryTest.php | 127 ++++++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 app/Actions/CreateEntry.php create mode 100644 app/Exceptions/CreateEntryException.php create mode 100644 tests/Feature/Actions/CreateEntryTest.php diff --git a/app/Actions/CreateEntry.php b/app/Actions/CreateEntry.php new file mode 100644 index 0000000..a3bf35d --- /dev/null +++ b/app/Actions/CreateEntry.php @@ -0,0 +1,82 @@ +createEntry($student, $audition, $entry_for); + } + + public function createEntry(Student $student, Audition $audition, string|array|null $entry_for = null) + { + if (! $entry_for) { + $entry_for = ['seating', 'advancement']; + } + $entry_for = collect($entry_for); + $this->verifySubmission($student, $audition); + $entry = Entry::make([ + 'student_id' => $student->id, + 'audition_id' => $audition->id, + 'draw_number' => $this->checkDraw($audition), + 'for_seating' => $entry_for->contains('seating'), + 'for_advancement' => $entry_for->contains('advancement'), + ]); + $entry->save(); + + return $entry; + } + + private function checkDraw(Audition $audition) + { + if (! $audition->hasFlag('drawn')) { + return null; + } + // get the maximum value of draw_number from $audition->entries() + $draw_number = $audition->entries()->max('draw_number'); + + return $draw_number + 1; + } + + private function verifySubmission(Student $student, Audition $audition): void + { + // Make sure it's a valid student + if (! $student->exists()) { + throw new CreateEntryException('Invalid student provided'); + } + // Make sure the audition is valid + if (! $audition->exists()) { + throw new CreateEntryException('Invalid audition provided'); + } + // A student can't enter the same audition twice + if (Entry::where('student_id', $student->id)->where('audition_id', $audition->id)->exists()) { + throw new CreateEntryException('That student is already entered in that audition'); + } + // Can't enter a published audition + if ($audition->hasFlag('seats_published')) { + throw new CreateEntryException('Cannot add an entry to an audition where seats are published'); + } + if ($audition->hasFlag('advancement_published')) { + throw new CreateEntryException('Cannot add an entry to an audition where advancement is published'); + } + // Verify the grade of the student is in range for the audition + if ($student->grade > $audition->maximum_grade) { + throw new CreateEntryException('The grade of the student exceeds the maximum for that audition'); + } + if ($student->grade < $audition->minimum_grade) { + throw new CreateEntryException('The grade of the student does not meet the minimum for that audition'); + } + } +} diff --git a/app/Exceptions/CreateEntryException.php b/app/Exceptions/CreateEntryException.php new file mode 100644 index 0000000..8dd356f --- /dev/null +++ b/app/Exceptions/CreateEntryException.php @@ -0,0 +1,9 @@ +createEntry = App::make(CreateEntry::class); +}); + +it('throws an exception if the student does not exist', function () { + $audition = Audition::factory()->create(); + $student = Student::factory()->make(); + $this->createEntry->__invoke($student, $audition); +})->throws(CreateEntryException::class, 'Invalid student provided'); +it('throws an exception if the audition does not exist', function () { + $audition = Audition::factory()->make(); + $student = Student::factory()->create(); + $this->createEntry->__invoke($student, $audition); +})->throws(CreateEntryException::class, 'Invalid audition provided'); +it('throws an exception if the student is already entered in the audition', function () { + // Arrange + $audition = Audition::factory()->create(); + $student = Student::factory()->create(); + $entry = Entry::create([ + 'student_id' => $student->id, + 'audition_id' => $audition->id, + ]); + // Act & Assert + $this->createEntry->createEntry($student, $audition); +})->throws(CreateEntryException::class, 'That student is already entered in that audition'); +it('throws an exception if seats are published for the audition', function () { + // Arrange + $audition = Audition::factory()->create(); + $student = Student::factory()->create(); + $audition->addFlag('seats_published'); + // Act & Assert + $this->createEntry->createEntry($student, $audition); +})->throws(CreateEntryException::class, 'Cannot add an entry to an audition where seats are published'); +it('throws an exception if advancement is published for the audition', function () { + // Arrange + $audition = Audition::factory()->create(); + $student = Student::factory()->create(); + $audition->addFlag('advancement_published'); + // Act & Assert + $this->createEntry->createEntry($student, $audition); +})->throws(CreateEntryException::class, 'Cannot add an entry to an audition where advancement is published'); +it('throws an exception if the grade of the student exceeds the maximum grade for the audition', function () { + // Arrange + $audition = Audition::factory()->create(['minimum_grade' => 8, 'maximum_grade' => 9]); + $student = Student::factory()->create(['grade' => 11]); + // Act & Assert + $this->createEntry->createEntry($student, $audition); +})->throws(CreateEntryException::class, 'The grade of the student exceeds the maximum for that audition'); +it('throws an exception if the grade of the student does not meet the minimum grade for the audition', function () { + // Arrange + $audition = Audition::factory()->create(['minimum_grade' => 8, 'maximum_grade' => 9]); + $student = Student::factory()->create(['grade' => 7]); + // Act & Assert + $this->createEntry->createEntry($student, $audition); +})->throws(CreateEntryException::class, 'The grade of the student does not meet the minimum for that audition'); +it('returns an entry object', function () { + // Arrange + $audition = Audition::factory()->create(['minimum_grade' => 8, 'maximum_grade' => 9]); + $student = Student::factory()->create(['grade' => 8]); + // Act & Assert + $entry = $this->createEntry->createEntry($student, $audition); + expect($entry instanceof Entry)->toBeTrue(); +}); +it('creates an entry with a null draw number if the audition is not drawn', function () { + $audition = Audition::factory()->create(['minimum_grade' => 8, 'maximum_grade' => 9]); + $student = Student::factory()->create(['grade' => 8]); + // Act & Assert + $entry = $this->createEntry->createEntry($student, $audition); + expect($entry->draw_number)->toBeNull(); +}); +it('assigns the next highest draw_number available if the audition is drawn', function () { + $audition = Audition::factory()->create(['minimum_grade' => 8, 'maximum_grade' => 9]); + $student = Student::factory()->create(['grade' => 8]); + $audition->addFlag('drawn'); + foreach (range(1, 5) as $number) { + Entry::factory()->create([ + 'audition_id' => $audition->id, + 'draw_number' => $number, + ]); + } + $entry = $this->createEntry->createEntry($student, $audition); + expect($entry->draw_number)->toBe(6); +}); +it('makes the entry for both seating and advancement if nothing is specified', function () { + $audition = Audition::factory()->create(['minimum_grade' => 8, 'maximum_grade' => 9]); + $student = Student::factory()->create(['grade' => 8]); + // Act & Assert + $entry = $this->createEntry->createEntry($student, $audition); + expect($entry->for_seating)->toBeTrue() + ->and($entry->for_advancement)->toBeTrue(); +}); +it('makes the entry for only seating if only seating is specified', function () { + $audition = Audition::factory()->create(['minimum_grade' => 8, 'maximum_grade' => 9]); + $student = Student::factory()->create(['grade' => 8]); + // Act & Assert + $entry = $this->createEntry->createEntry($student, $audition, 'seating'); + expect($entry->for_seating)->toBeTrue() + ->and($entry->for_advancement)->toBeFalse(); +}); +it('makes the entry for only advancement if only advancement is specified', function () { + $audition = Audition::factory()->create(['minimum_grade' => 8, 'maximum_grade' => 9]); + $student = Student::factory()->create(['grade' => 8]); + // Act & Assert + $entry = $this->createEntry->createEntry($student, $audition, 'advancement'); + expect($entry->for_seating)->toBeFalse() + ->and($entry->for_advancement)->toBeTrue(); +}); +it('makes the entry for both seating and advancement if both are specified', function () { + $audition = Audition::factory()->create(['minimum_grade' => 8, 'maximum_grade' => 9]); + $student = Student::factory()->create(['grade' => 8]); + // Act & Assert + $entry = $this->createEntry->createEntry($student, $audition, ['seating', 'advancement']); + expect($entry->for_seating)->toBeTrue() + ->and($entry->for_advancement)->toBeTrue(); +});