Finish DoublerDecision tests
This commit is contained in:
parent
db22918ff8
commit
5ff3785f9f
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Actions\Development;
|
||||
|
||||
use App\Actions\Tabulation\EnterScore;
|
||||
use App\Models\Entry;
|
||||
|
||||
class FakeScoresForEntry
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function __invoke(Entry $entry): void
|
||||
{
|
||||
$scoreScribe = app(EnterScore::class);
|
||||
$scoringGuide = $entry->audition->scoringGuide;
|
||||
$subscores = $scoringGuide->subscores;
|
||||
$judges = $entry->audition->judges;
|
||||
foreach ($judges as $judge) {
|
||||
$scoringArray = [];
|
||||
foreach ($subscores as $subscore) {
|
||||
$scoringArray[$subscore->id] = mt_rand(0, $subscore->max_score);
|
||||
}
|
||||
$scoreScribe($judge, $entry, $scoringArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ class CreateEntry
|
|||
/**
|
||||
* @throws ManageEntryException
|
||||
*/
|
||||
public function createEntry(Student|int $student, Audition|int $audition, string|array|null $entry_for = null)
|
||||
public function createEntry(Student|int $student, Audition|int $audition, string|array|null $entry_for = null): Entry
|
||||
{
|
||||
if (is_int($student)) {
|
||||
$student = Student::find($student);
|
||||
|
|
|
|||
|
|
@ -27,11 +27,6 @@ class DoublerDecision
|
|||
'decline' => $this->decline($entry),
|
||||
default => throw new AuditionAdminException('Invalid decision specified')
|
||||
};
|
||||
|
||||
if ($decision != 'accept' && $decision != 'decline') {
|
||||
throw new AuditionAdminException('Invalid decision specified');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -56,9 +51,6 @@ class DoublerDecision
|
|||
if ($entry->audition->hasFlag('seats_published')) {
|
||||
throw new AuditionAdminException('Cannot accept an entry in an audition where seats are published');
|
||||
}
|
||||
if ($entry->audition->hasFlag('advancement_published')) {
|
||||
throw new AuditionAdminException('Cannot accept an entry in an audition where advancement is published');
|
||||
}
|
||||
Cache::forget('rank_seating_'.$entry->audition_id);
|
||||
|
||||
// Process student entries
|
||||
|
|
@ -87,6 +79,13 @@ class DoublerDecision
|
|||
if ($entry->hasFlag('declined')) {
|
||||
throw new AuditionAdminException('Entry '.$entry->id.' is already declined');
|
||||
}
|
||||
if (! $entry->totalScore) {
|
||||
throw new AuditionAdminException('Cannot decline an unscored entry');
|
||||
}
|
||||
if ($entry->audition->hasFlag('seats_published')) {
|
||||
throw new AuditionAdminException('Cannot decline an entry in an audition where seats are published');
|
||||
}
|
||||
|
||||
// Flag this entry
|
||||
$entry->addFlag('declined');
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\SubscoreDefinition;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
|
|
@ -20,4 +21,18 @@ class ScoringGuideFactory extends Factory
|
|||
'name' => $this->faker->sentence(3),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the model factory.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function configure()
|
||||
{
|
||||
return $this->afterCreating(function ($scoringGuide) {
|
||||
SubscoreDefinition::factory()
|
||||
->count(5)
|
||||
->create(['scoring_guide_id' => $scoringGuide->id]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,13 @@ class SubscoreDefinitionFactory extends Factory
|
|||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$sg = ScoringGuide::factory()->create();
|
||||
|
||||
return [
|
||||
'scoring_guide_id' => $sg->id,
|
||||
'scoring_guide_id' => function (array $attributes) {
|
||||
return array_key_exists('scoring_guide_id', $attributes)
|
||||
? $attributes['scoring_guide_id']
|
||||
: ScoringGuide::factory()->create()->id;
|
||||
},
|
||||
'name' => $this->faker->word,
|
||||
'max_score' => 100,
|
||||
'weight' => $this->faker->numberBetween(1, 4),
|
||||
|
|
@ -30,6 +33,7 @@ class SubscoreDefinitionFactory extends Factory
|
|||
'for_advance' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
public function seatingOnly(): self
|
||||
{
|
||||
return $this->state(
|
||||
|
|
@ -57,5 +61,4 @@ class SubscoreDefinitionFactory extends Factory
|
|||
fn (array $attributes) => ['tiebreak_order' => 0]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,191 @@
|
|||
<?php
|
||||
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
|
||||
use App\Actions\Development\FakeScoresForEntry;
|
||||
use App\Actions\Entries\CreateEntry;
|
||||
use App\Actions\Entries\DoublerDecision;
|
||||
use App\Exceptions\AuditionAdminException;
|
||||
use App\Models\Audition;
|
||||
use App\Models\Doubler;
|
||||
use App\Models\Room;
|
||||
use App\Models\ScoringGuide;
|
||||
use App\Models\Student;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
test('', function () {
|
||||
$response = $this->get('/');
|
||||
beforeEach(function () {
|
||||
$this->decider = app(DoublerDecision::class);
|
||||
$this->entryScribe = app(CreateEntry::class);
|
||||
$this->scoreFaker = app(FakeScoresForEntry::class);
|
||||
|
||||
$response->assertStatus(200);
|
||||
// Setup doubler
|
||||
$this->scoringGuide = ScoringGuide::factory()->create();
|
||||
$this->judge1 = User::factory()->create();
|
||||
$this->judge2 = User::factory()->create();
|
||||
$this->room = Room::factory()->create();
|
||||
$this->room->addJudge($this->judge1);
|
||||
$this->room->addJudge($this->judge2);
|
||||
$this->audition1 = Audition::factory()->create(
|
||||
[
|
||||
'minimum_grade' => 9, 'maximum_grade' => 12,
|
||||
'scoring_guide_id' => $this->scoringGuide->id,
|
||||
'room_id' => $this->room->id,
|
||||
'order_in_room' => 1,
|
||||
'name' => 'Flute',
|
||||
]);
|
||||
$this->audition2 = Audition::factory()->create(
|
||||
[
|
||||
'minimum_grade' => 9, 'maximum_grade' => 12,
|
||||
'scoring_guide_id' => $this->scoringGuide->id,
|
||||
'room_id' => $this->room->id,
|
||||
'order_in_room' => 2,
|
||||
'event_id' => $this->audition1->event_id,
|
||||
'name' => 'Trumpet',
|
||||
]);
|
||||
$this->audition3 = Audition::factory()->create(
|
||||
[
|
||||
'minimum_grade' => 9, 'maximum_grade' => 12,
|
||||
'scoring_guide_id' => $this->scoringGuide->id,
|
||||
'room_id' => $this->room->id,
|
||||
'order_in_room' => 3,
|
||||
'event_id' => $this->audition1->event_id,
|
||||
'name' => 'Trombone',
|
||||
]);
|
||||
$this->otherEventAudition = Audition::factory()->create([
|
||||
'minimum_grade' => 9, 'maximum_grade' => 12,
|
||||
'scoring_guide_id' => $this->scoringGuide->id,
|
||||
'room_id' => $this->room->id,
|
||||
'order_in_room' => 4,
|
||||
'name' => 'Jazz Trumpet',
|
||||
]);
|
||||
$this->student = Student::factory()->create([
|
||||
'grade' => 9, 'first_name' => 'Percy',
|
||||
'last_name' => 'Grainger',
|
||||
]);
|
||||
$this->entry1 = $this->entryScribe->createEntry($this->student, $this->audition1);
|
||||
$this->entry2 = $this->entryScribe->createEntry($this->student, $this->audition2);
|
||||
$this->entry3 = $this->entryScribe->createEntry($this->student, $this->audition3);
|
||||
$this->otherEventEntry = $this->entryScribe->createEntry($this->student, $this->otherEventAudition);
|
||||
});
|
||||
|
||||
it('is invokable', function () {
|
||||
|
||||
($this->scoreFaker)($this->entry1);
|
||||
($this->scoreFaker)($this->entry2);
|
||||
($this->scoreFaker)($this->entry3);
|
||||
($this->decider)($this->entry2, 'decline');
|
||||
expect($this->entry2->hasFlag('declined'))->toBeTruthy();
|
||||
});
|
||||
|
||||
it('cannot be called with an invalid decision', function () {
|
||||
|
||||
($this->scoreFaker)($this->entry1);
|
||||
($this->scoreFaker)($this->entry2);
|
||||
($this->scoreFaker)($this->entry3);
|
||||
($this->decider)($this->entry2, 'idunno');
|
||||
})->throws(AuditionAdminException::class, 'Invalid decision specified');
|
||||
|
||||
it('can decline an entry', function () {
|
||||
|
||||
($this->scoreFaker)($this->entry1);
|
||||
($this->scoreFaker)($this->entry2);
|
||||
($this->scoreFaker)($this->entry3);
|
||||
$this->decider->decline($this->entry2);
|
||||
expect($this->entry2->hasFlag('declined'))->toBeTruthy();
|
||||
});
|
||||
|
||||
it('will not decline an entry with no scores', function () {
|
||||
$this->decider->decline($this->entry2);
|
||||
})->throws(AuditionAdminException::class, 'Cannot decline an unscored entry');
|
||||
|
||||
it('will not decline an entry that is already declined', function () {
|
||||
($this->scoreFaker)($this->entry2);
|
||||
$this->entry2->addFlag('declined');
|
||||
$this->decider->decline($this->entry2);
|
||||
})->throws(AuditionAdminException::class, 'Entry 2 is already declined');
|
||||
|
||||
it('will not decline an entry in a published event', function () {
|
||||
($this->scoreFaker)($this->entry2);
|
||||
$this->audition2->addFlag('seats_published');
|
||||
$this->entry2->refresh();
|
||||
$this->decider->decline($this->entry2);
|
||||
})->throws(AuditionAdminException::class, 'Cannot decline an entry in an audition where seats are published');
|
||||
|
||||
it('accepts an entry and declines others in the same event', function () {
|
||||
($this->scoreFaker)($this->entry1);
|
||||
($this->scoreFaker)($this->entry2);
|
||||
($this->scoreFaker)($this->entry3);
|
||||
$this->decider->accept($this->entry2);
|
||||
$this->entry1->refresh();
|
||||
$this->entry2->refresh();
|
||||
$this->entry3->refresh();
|
||||
expect($this->entry1->hasFlag('declined'))->toBeTruthy()
|
||||
->and($this->entry3->hasFlag('declined'))->toBeTruthy();
|
||||
$doubler = Doubler::findDoubler($this->entry2->student_id, $this->audition2->event_id);
|
||||
expect($doubler->accepted_entry)->toBe($this->entry2->id);
|
||||
});
|
||||
|
||||
it('will not accept an entry into an event with seats published', function () {
|
||||
($this->scoreFaker)($this->entry1);
|
||||
($this->scoreFaker)($this->entry2);
|
||||
($this->scoreFaker)($this->entry3);
|
||||
$this->audition2->addFlag('seats_published');
|
||||
$this->audition2->refresh();
|
||||
$this->entry2->refresh();
|
||||
$this->decider->accept($this->entry2);
|
||||
$this->entry1->refresh();
|
||||
$this->entry2->refresh();
|
||||
$this->entry3->refresh();
|
||||
expect($this->entry1->hasFlag('declined'))->toBeTruthy()
|
||||
->and($this->entry3->hasFlag('declined'))->toBeTruthy();
|
||||
$doubler = Doubler::findDoubler($this->entry2->student_id, $this->audition2->event_id);
|
||||
expect($doubler->accepted_entry)->toBe($this->entry2->id);
|
||||
})->throws(AuditionAdminException::class, 'Cannot accept an entry in an audition where seats are published');
|
||||
|
||||
it('will not accept an entry that has already been declined', function () {
|
||||
($this->scoreFaker)($this->entry1);
|
||||
($this->scoreFaker)($this->entry2);
|
||||
($this->scoreFaker)($this->entry3);
|
||||
$this->entry2->addFlag('declined');
|
||||
$this->decider->accept($this->entry2);
|
||||
$this->entry1->refresh();
|
||||
$this->entry2->refresh();
|
||||
$this->entry3->refresh();
|
||||
expect($this->entry1->hasFlag('declined'))->toBeTruthy()
|
||||
->and($this->entry3->hasFlag('declined'))->toBeTruthy();
|
||||
$doubler = Doubler::findDoubler($this->entry2->student_id, $this->audition2->event_id);
|
||||
expect($doubler->accepted_entry)->toBe($this->entry2->id);
|
||||
})->throws(AuditionAdminException::class, 'Entry 2 is already declined');
|
||||
|
||||
it('when accepting a seat, does not decline no_show or failed_prelim entries', function () {
|
||||
($this->scoreFaker)($this->entry2);
|
||||
$this->entry1->addFlag('no_show');
|
||||
$this->entry3->addFlag('failed_prelim');
|
||||
$this->decider->accept($this->entry2);
|
||||
$this->entry1->refresh();
|
||||
$this->entry2->refresh();
|
||||
$this->entry3->refresh();
|
||||
expect($this->entry1->hasFlag('declined'))->toBeFalsy()
|
||||
->and($this->entry3->hasFlag('declined'))->toBeFalsy();
|
||||
$doubler = Doubler::findDoubler($this->entry2->student_id, $this->audition2->event_id);
|
||||
expect($doubler->accepted_entry)->toBe($this->entry2->id);
|
||||
});
|
||||
|
||||
it('when accepting an entry, does not decline seats in other events', function () {
|
||||
($this->scoreFaker)($this->entry1);
|
||||
($this->scoreFaker)($this->entry2);
|
||||
($this->scoreFaker)($this->entry3);
|
||||
$this->decider->accept($this->entry2);
|
||||
$this->entry1->refresh();
|
||||
$this->entry2->refresh();
|
||||
$this->entry3->refresh();
|
||||
expect($this->otherEventEntry->hasFlag('declined'))->toBeFalsy();
|
||||
});
|
||||
|
||||
it('will not accept an entry if the student has unscored entries', function () {
|
||||
$this->decider->accept($this->entry2);
|
||||
})->throws(AuditionAdminException::class,
|
||||
'Cannot accept seating for Percy Grainger because student has unscored entries');
|
||||
|
|
|
|||
Loading…
Reference in New Issue