Files
myep-team/tests/Service/Assignment/AssignmentPlannerTest.php
T

215 lines
7.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Service\Assignment;
use App\Entity\Destination;
use App\Entity\StaffingRule;
use App\Enum\StaffingFee;
use App\Enum\StaffingRole;
use App\Model\PlannedAssignment;
use App\Service\Assignment\AssignmentPlanner;
use PHPUnit\Framework\TestCase;
/**
* Pins the rules listed on AssignmentPlanner, one test per case.
*/
class AssignmentPlannerTest extends TestCase
{
public function testGuidesOnlyIsOneAssignmentWithAPlacePerGuide(): void
{
$planned = $this->plan(self::rule(guides: 2));
$this->assertSame([[StaffingRole::GUIDE, 2, null, []]], self::summarize($planned));
}
public function testWithoutSplitEachLevelIsAKursleitungOfItsOwn(): void
{
$planned = $this->plan(self::rule(guides: 2, beginner: true, advanced: true));
$this->assertSame([
[StaffingRole::GUIDE, 2, null, []],
[StaffingRole::SKI_BEGINNER, 1, null, [StaffingFee::COURSE]],
[StaffingRole::SKI_ADVANCED, 1, null, [StaffingFee::COURSE]],
], self::summarize($planned));
}
public function testSplitWithOneLevelKeepsTheRemainingGuides(): void
{
$planned = $this->plan(self::rule(guides: 2, advanced: true, splitAdvanced: true));
$this->assertSame([
[StaffingRole::GUIDE, 1, null, []],
[StaffingRole::GUIDE_SKI_ADVANCED, 1, null, [StaffingFee::COURSE]],
], self::summarize($planned));
}
public function testSplitWithBothLevelsUsesUpTwoGuides(): void
{
// decision 3: two single-level combined profiles, never the A+F one
$planned = $this->plan(self::rule(guides: 2, beginner: true, advanced: true, splitBeginner: true, splitAdvanced: true));
$this->assertSame([
[StaffingRole::GUIDE_SKI_BEGINNER, 1, null, [StaffingFee::COURSE]],
[StaffingRole::GUIDE_SKI_ADVANCED, 1, null, [StaffingFee::COURSE]],
], self::summarize($planned));
}
public function testOneGuideTakesOverOneLevelAndTheOtherStaysAKursleitung(): void
{
$planned = $this->plan(self::rule(guides: 1, beginner: true, advanced: true, splitAdvanced: true, guiding: true), self::busTravel());
$this->assertSame([
[StaffingRole::GUIDE_SKI_ADVANCED, 1, 5, [StaffingFee::GUIDING, StaffingFee::COURSE]],
[StaffingRole::SKI_BEGINNER, 1, null, [StaffingFee::COURSE]],
], self::summarize($planned));
}
public function testATakeOverOfALevelThatIsOffIsIgnored(): void
{
$planned = $this->plan(self::rule(guides: 1, advanced: true, splitBeginner: true));
$this->assertSame([
[StaffingRole::GUIDE, 1, null, []],
[StaffingRole::SKI_ADVANCED, 1, null, [StaffingFee::COURSE]],
], self::summarize($planned));
}
public function testGuidingIsAFeeOnTheFirstGuideTypeAssignmentOnly(): void
{
$planned = $this->plan(self::rule(guides: 3, beginner: true, advanced: true, splitBeginner: true, splitAdvanced: true, guiding: true));
$this->assertSame([
[StaffingRole::GUIDE, 1, null, [StaffingFee::GUIDING]],
[StaffingRole::GUIDE_SKI_BEGINNER, 1, null, [StaffingFee::COURSE]],
[StaffingRole::GUIDE_SKI_ADVANCED, 1, null, [StaffingFee::COURSE]],
], self::summarize($planned));
}
public function testGuidingSplitsOffOnePlaceWhichAlsoEscortsTheBus(): void
{
$planned = $this->plan(self::rule(guides: 3, guiding: true), self::busTravel());
$this->assertSame([
[StaffingRole::GUIDE, 1, 5, [StaffingFee::GUIDING]],
[StaffingRole::GUIDE, 2, null, []],
], self::summarize($planned));
}
public function testWithoutAPlainReiseleitungTheFirstCombinedOneGuides(): void
{
$planned = $this->plan(self::rule(guides: 2, beginner: true, advanced: true, splitBeginner: true, splitAdvanced: true, guiding: true));
$this->assertSame([
[StaffingRole::GUIDE_SKI_BEGINNER, 1, null, [StaffingFee::GUIDING, StaffingFee::COURSE]],
[StaffingRole::GUIDE_SKI_ADVANCED, 1, null, [StaffingFee::COURSE]],
], self::summarize($planned));
}
public function testGuidingStaysOffThePlainKursleitung(): void
{
$planned = $this->plan(self::rule(guides: 1, beginner: true, guiding: true));
$this->assertSame([
[StaffingRole::GUIDE, 1, null, [StaffingFee::GUIDING]],
[StaffingRole::SKI_BEGINNER, 1, null, [StaffingFee::COURSE]],
], self::summarize($planned));
}
public function testTheBusEscortTakesTheFirstBoardingPointInDepartureOrder(): void
{
// stored order is not departure order: Göttingen leaves before Kassel
$destination = self::destination([
['busProId' => 7, 'city' => 'Kassel', 'street' => null, 'time' => '00:30'],
['busProId' => 5, 'city' => 'Göttingen', 'street' => null, 'time' => '08.03.2024 22:00:00'],
]);
$planned = $this->plan(self::rule(guides: 2), $destination);
$this->assertSame(5, $planned[0]->pickup);
}
public function testTheBusEscortIsSetOnExactlyOneAssignment(): void
{
$planned = $this->plan(self::rule(guides: 3, beginner: true, splitBeginner: true), self::busTravel());
$this->assertSame([
[StaffingRole::GUIDE, 2, 5, []],
[StaffingRole::GUIDE_SKI_BEGINNER, 1, null, [StaffingFee::COURSE]],
], self::summarize($planned));
}
public function testWithoutAPlainReiseleitungTheFirstCombinedOneEscortsTheBus(): void
{
$planned = $this->plan(self::rule(guides: 2, beginner: true, advanced: true, splitBeginner: true, splitAdvanced: true), self::busTravel());
$this->assertSame(5, $planned[0]->pickup);
$this->assertNull($planned[1]->pickup);
}
public function testNoBusEscortWithoutBoardingPoints(): void
{
$planned = $this->plan(self::rule(guides: 1));
$this->assertNull($planned[0]->pickup);
}
public function testWithoutAGuideNobodyEscortsTheBus(): void
{
$planned = $this->plan(self::rule(guides: 0, beginner: true), self::busTravel());
$this->assertSame([[StaffingRole::SKI_BEGINNER, 1, null, [StaffingFee::COURSE]]], self::summarize($planned));
}
/**
* @return list<PlannedAssignment>
*/
private function plan(StaffingRule $rule, ?Destination $destination = null): array
{
return (new AssignmentPlanner())->plan($rule, $destination ?? self::destination([]));
}
private static function busTravel(): Destination
{
return self::destination([
['busProId' => 5, 'city' => 'Göttingen', 'street' => null, 'time' => '08.03.2024 22:00:00'],
]);
}
/**
* @param list<PlannedAssignment> $planned
*/
private static function summarize(array $planned): array
{
return array_map(
static fn (PlannedAssignment $p): array => [$p->role, $p->places, $p->pickup, $p->fees],
$planned,
);
}
private static function rule(
int $guides,
bool $beginner = false,
bool $advanced = false,
bool $splitBeginner = false,
bool $splitAdvanced = false,
bool $guiding = false,
): StaffingRule {
return (new StaffingRule())
->setProductCode('DKIMP')
->setGuides($guides)
->setSkiBeginner($beginner)
->setSkiAdvanced($advanced)
->setSplitBeginner($splitBeginner)
->setSplitAdvanced($splitAdvanced)
->setGuiding($guiding)
;
}
private static function destination(array $pickups): Destination
{
return (new Destination())->setPickups($pickups);
}
}