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, split: 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, split: true)); $this->assertSame([ [StaffingRole::GUIDE_SKI_BEGINNER, 1, null, [StaffingFee::COURSE]], [StaffingRole::GUIDE_SKI_ADVANCED, 1, null, [StaffingFee::COURSE]], ], self::summarize($planned)); } public function testGuidingIsAFeeOnEveryGuideTypeAssignment(): void { $planned = $this->plan(self::rule(guides: 3, beginner: true, advanced: true, split: true, guiding: true)); $this->assertSame([ [StaffingRole::GUIDE, 1, null, [StaffingFee::GUIDING]], [StaffingRole::GUIDE_SKI_BEGINNER, 1, null, [StaffingFee::GUIDING, StaffingFee::COURSE]], [StaffingRole::GUIDE_SKI_ADVANCED, 1, null, [StaffingFee::GUIDING, 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, busEscort: true), $destination); $this->assertSame(5, $planned[0]->pickup); } public function testTheBusEscortIsSetOnExactlyOneAssignment(): void { $planned = $this->plan(self::rule(guides: 3, beginner: true, split: true, busEscort: true)); $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, split: true, busEscort: true)); $this->assertSame(5, $planned[0]->pickup); $this->assertNull($planned[1]->pickup); } public function testNoBusEscortWithoutBoardingPoints(): void { $planned = $this->plan(self::rule(guides: 1, busEscort: true), self::destination([])); $this->assertNull($planned[0]->pickup); } public function testNoBusEscortWhenTheMatrixSaysNo(): void { $planned = $this->plan(self::rule(guides: 1)); $this->assertNull($planned[0]->pickup); } /** * @return list */ private function plan(StaffingRule $rule, ?Destination $destination = null): array { return (new AssignmentPlanner())->plan($rule, $destination ?? self::destination([ ['busProId' => 5, 'city' => 'Göttingen', 'street' => null, 'time' => '08.03.2024 22:00:00'], ])); } /** * @param list $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 $split = false, bool $guiding = false, bool $busEscort = false, ): StaffingRule { return (new StaffingRule()) ->setProductCode('DKIMP') ->setGuides($guides) ->setSkiBeginner($beginner) ->setSkiAdvanced($advanced) ->setSplit($split) ->setGuiding($guiding) ->setBusEscort($busEscort) ; } private static function destination(array $pickups): Destination { return (new Destination())->setPickups($pickups); } }