diff --git a/app/Models/Entry.php b/app/Models/Entry.php index b7c0e82..0fbff74 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -33,10 +33,6 @@ class Entry extends Model { $ranker = app(RankAuditionEntries::class); - if ($type !== 'seating' && $type !== 'advancement') { - throw new AuditionAdminException('Invalid type specified. Must be either seating or advancement.'); - } - // Return false if no score. If we have no score, we can't have a rank if (! $this->totalScore) { return false; @@ -50,11 +46,15 @@ class Entry extends Model return $rankedEntries->where('id', $this->id)->first()->seatingRank; } - // Find position of current entry in the ranked entries (1-based index) - $position = $rankedEntries->search(fn ($entry) => $entry->id === $this->id); + return $rankedEntries->where('id', $this->id)->first()->advancementRank; - // Return false if entry not found, otherwise return 1-based position - return $position === false ? false : $position + 1; + /** The code below is deprecated and should be removed in the future. */ + // TODO: Remove this code if nothing is breaking + // // Find position of current entry in the ranked entries (1-based index) + // $position = $rankedEntries->search(fn ($entry) => $entry->id === $this->id); + // + // // Return false if entry not found, otherwise return 1-based position + // return $position === false ? false : $position + 1; } diff --git a/tests/Feature/app/Models/EntryTest.php b/tests/Feature/app/Models/EntryTest.php new file mode 100644 index 0000000..4e80f88 --- /dev/null +++ b/tests/Feature/app/Models/EntryTest.php @@ -0,0 +1,249 @@ +entry = Entry::factory()->create(); +}); + +it('can return its total score', function () { + DB::table('entry_total_scores')->insert([ + 'entry_id' => $this->entry->id, + 'seating_total' => 10, + 'advancement_total' => 10, + 'seating_subscore_totals' => json_encode([5, 5]), + 'advancement_subscore_totals' => json_encode([5, 5]), + 'bonus_total' => null, + ]); + expect($this->entry->totalScore)->toBeInstanceOf(EntryTotalScore::class); +}); + +it('returns false if we try to get a rank when the audition is unscored', function () { + expect($this->entry->rank('seating'))->toBeFalse() + ->and($this->entry->rank('advancement'))->toBeFalse(); +}); + +it('should return the rank in its audition for seating', function () { + // We need the entry to be scored + DB::table('entry_total_scores')->insert([ + 'entry_id' => $this->entry->id, + 'seating_total' => 10, + 'advancement_total' => 10, + 'seating_subscore_totals' => json_encode([5, 5]), + 'advancement_subscore_totals' => json_encode([5, 5]), + 'bonus_total' => null, + ]); + // Prepare a collection of mocked entries + $mockedRankedEntries = collect([ + (object) ['id' => 101, 'seatingRank' => 1], + (object) ['id' => 102, 'seatingRank' => 2], + (object) ['id' => 103, 'seatingRank' => 'declined'], + (object) ['id' => $this->entry->id, 'seatingRank' => 3], + (object) ['id' => 104, 'seatingRank' => 4], + ]); + + // Mock RankAuditionEntries to return the mocked collection + $mockRanker = Mockery::mock(RankAuditionEntries::class); + $mockRanker->shouldReceive('__invoke') + ->once() + ->andReturn($mockedRankedEntries); + + // Bind the mock to the container + $this->app->instance(RankAuditionEntries::class, $mockRanker); + + // Test the function + expect($this->entry->rank('seating'))->toEqual(3); +}); + +it('should return the rank in its audition for advancement', function () { + // We need the entry to be scored + DB::table('entry_total_scores')->insert([ + 'entry_id' => $this->entry->id, + 'seating_total' => 10, + 'advancement_total' => 10, + 'seating_subscore_totals' => json_encode([5, 5]), + 'advancement_subscore_totals' => json_encode([5, 5]), + 'bonus_total' => null, + ]); + // Prepare a collection of mocked entries + $mockedRankedEntries = collect([ + (object) ['id' => 101, 'advancementRank' => 1], + (object) ['id' => 102, 'advancementRank' => 2], + (object) ['id' => 103, 'advancementRank' => 3], + (object) ['id' => $this->entry->id, 'advancementRank' => 4], + (object) ['id' => 104, 'advancementRank' => 5], + ]); + + // Mock RankAuditionEntries to return the mocked collection + $mockRanker = Mockery::mock(RankAuditionEntries::class); + $mockRanker->shouldReceive('__invoke') + ->once() + ->andReturn($mockedRankedEntries); + + // Bind the mock to the container + $this->app->instance(RankAuditionEntries::class, $mockRanker); + + // Test the function + expect($this->entry->rank('advancement'))->toEqual(4); +}); + +it('can return its student', function () { + expect($this->entry->student->id)->toEqual($this->entry->student_id) + ->and(Entry::first()->student)->toBeInstanceOf(Student::class); +}); + +it('can return its audition', function () { + expect($this->entry->audition->id)->toEqual($this->entry->audition_id) + ->and(Entry::first()->audition)->toBeInstanceOf(Audition::class); +}); + +it('can return its school', function () { + expect($this->entry->school)->toBeInstanceOf(App\Models\School::class) + ->and($this->entry->school->id)->toEqual($this->entry->student->school->id); +}); + +it('can return its scoreSheets', function () { + $judge = User::factory()->create(); + $judge2 = User::factory()->create(); + DB::table('score_sheets')->insert([ + [ + 'user_id' => $judge->id, + 'entry_id' => $this->entry->id, + 'subscores' => json_encode([5, 5, 5, 5, 5]), + 'seating_total' => 5, + 'advancement_total' => 5, + ], + [ + 'user_id' => $judge2->id, + 'entry_id' => $this->entry->id, + 'subscores' => json_encode([5, 5, 5, 5, 5]), + 'seating_total' => 5, + 'advancement_total' => 5, + ], + ]); + expect($this->entry->scoreSheets()->count())->toBe(2) + ->and($this->entry->scoreSheets()->first())->toBeInstanceOf(ScoreSheet::class); +}); + +it('can return bonus scores', function () { + $judge = User::factory()->create(); + DB::table('bonus_scores')->insert([ + 'entry_id' => $this->entry->id, + 'user_id' => $judge->id, + 'originally_scored_entry' => $this->entry->id, + 'score' => 28, + ]); + expect($this->entry->bonusScores()->count())->toBe(1) + ->and($this->entry->bonusScores()->first())->toBeInstanceOf(App\Models\BonusScore::class); +}); + +it('can return its advancement votes', function () { + $judge = User::factory()->create(); + DB::table('judge_advancement_votes')->insert([ + 'user_id' => $judge->id, + 'entry_id' => $this->entry->id, + 'vote' => 'yes', + ]); + expect($this->entry->advancementVotes()->count())->toBe(1) + ->and($this->entry->advancementVotes()->first())->toBeInstanceOf(App\Models\JudgeAdvancementVote::class); +}); + +it('can return all of its flags set', function () { + $this->entry->addFlag('no_show'); + $this->entry->addFlag('failed_prelim'); + expect($this->entry->flags()->count())->toEqual(2) + ->and($this->entry->flags()->first())->toBeInstanceOf(App\Models\EntryFlag::class); +}); + +it('can add a flag', function () { + expect($this->entry->flags()->count())->toEqual(0); + $this->entry->addFlag('no_show'); + expect($this->entry->flags()->count())->toEqual(1); +}); + +it('can check for the presence of a given flag', function () { + expect($this->entry->hasFlag('no_show'))->toBeFalse(); + $this->entry->addFlag('no_show'); + expect($this->entry->hasFlag('no_show'))->toBeTrue(); +}); + +it('can remove a flag', function () { + $this->entry->addFlag('no_show'); + expect($this->entry->hasFlag('no_show'))->toBeTrue(); + $this->entry->removeFlag('no_show'); + expect($this->entry->hasFlag('no_show'))->toBeFalse(); +}); + +it('will not duplicate a flag', function () { + $this->entry->addFlag('no_show'); + $this->entry->addFlag('no_show'); + expect($this->entry->flags()->count())->toEqual(1); +}); + +it('can always return a score sheet count', function () { + $judge = User::factory()->create(); + $judge2 = User::factory()->create(); + DB::table('score_sheets')->insert([ + [ + 'user_id' => $judge->id, + 'entry_id' => $this->entry->id, + 'subscores' => json_encode([5, 5, 5, 5, 5]), + 'seating_total' => 5, + 'advancement_total' => 5, + ], + [ + 'user_id' => $judge2->id, + 'entry_id' => $this->entry->id, + 'subscores' => json_encode([5, 5, 5, 5, 5]), + 'seating_total' => 5, + 'advancement_total' => 5, + ], + ]); + expect($this->entry->score_sheets_count)->toEqual(2); +}); + +it('returns its seat if it has one', function () { + expect($this->entry->seat)->toBeNull(); + $ensemble = Ensemble::factory()->create(); + DB::table('seats')->insert([ + 'ensemble_id' => $ensemble->id, + 'audition_id' => $this->entry->audition_id, + 'seat' => 5, + 'entry_id' => $this->entry->id, + ]); + $this->entry->refresh(); + expect($this->entry->seat)->toBeInstanceOf(App\Models\Seat::class); +}); + +it('has a scope for only entries entered for seating', function () { + $newEntry = Entry::factory()->create(['for_seating' => false]); + expect(Entry::count())->toEqual(2) + ->and(Entry::forSeating()->count())->toEqual(1); +}); + +it('has a scope for only entries entered for advancement', function () { + $newEntry = Entry::factory()->create(['for_advancement' => false]); + expect(Entry::count())->toEqual(2) + ->and(Entry::forAdvancement()->count())->toEqual(1); +}); + +it('has a scope for only available entries', function () { + $declinedEntry = Entry::factory()->create(); + $noShowEntry = Entry::factory()->create(); + $failedEntry = Entry::factory()->create(); + $declinedEntry->addFlag('declined'); + $noShowEntry->addFlag('no_show'); + $failedEntry->addFlag('failed_prelim'); + expect(Entry::count())->toEqual(4) + ->and(Entry::available()->count())->toEqual(1); +});