feat: generate draft assignments from a staffing matrix
This commit is contained in:
1 parent
3116d23199
commit
2fc9806c69
41 files changed
+2618
No files matched your search
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Service\Assignment;
|
||||
|
||||
use App\Entity\Assignment;
|
||||
use App\Entity\Destination;
|
||||
use App\Entity\Fee;
|
||||
use App\Entity\JobProfile;
|
||||
use App\Entity\StaffingRule;
|
||||
use App\Enum\StaffingFee;
|
||||
use App\Enum\StaffingRole;
|
||||
use App\Model\PlannedAssignment;
|
||||
use App\Model\StaffingCandidate;
|
||||
use App\Repository\AssignmentRepository;
|
||||
use App\Repository\DestinationRepository;
|
||||
use App\Repository\StaffingRuleRepository;
|
||||
use App\Service\Assignment\AssignmentGenerator;
|
||||
use App\Service\Assignment\AssignmentPlanner;
|
||||
use App\Service\Assignment\StaffingResolver;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
class AssignmentGeneratorTest extends TestCase
|
||||
{
|
||||
public function testThePreviewSeesDisabledRowsAndTheCommandDoesNot(): void
|
||||
{
|
||||
$enabled = self::rule('DKIMP', enabled: true);
|
||||
$disabled = self::rule('DGSMP', enabled: false);
|
||||
|
||||
$asked = [];
|
||||
$destinationRepository = $this->createStub(DestinationRepository::class);
|
||||
$destinationRepository->method('getStaffingCandidates')->willReturnCallback(
|
||||
static function (array $productCodes) use (&$asked): array {
|
||||
$asked[] = $productCodes;
|
||||
|
||||
return [];
|
||||
}
|
||||
);
|
||||
|
||||
$generator = $this->generator([$enabled, $disabled], $destinationRepository);
|
||||
|
||||
$generator->getCandidates(enabledOnly: false);
|
||||
$generator->getCandidates(enabledOnly: true);
|
||||
|
||||
$this->assertSame([['DKIMP', 'DGSMP'], ['DKIMP']], $asked);
|
||||
}
|
||||
|
||||
public function testADateWithAnAssignmentIsMarkedSkipped(): void
|
||||
{
|
||||
$staffed = self::destination(1, 'DKIMP');
|
||||
$open = self::destination(2, 'DKIMP');
|
||||
|
||||
$destinationRepository = $this->createStub(DestinationRepository::class);
|
||||
$destinationRepository->method('getStaffingCandidates')->willReturn([$staffed, $open]);
|
||||
|
||||
$assignmentRepository = $this->createStub(AssignmentRepository::class);
|
||||
$assignmentRepository->method('getDestinationIdsWithAnyAssignment')->willReturn([1]);
|
||||
|
||||
$candidates = $this
|
||||
->generator([self::rule('DKIMP', enabled: true)], $destinationRepository, $assignmentRepository)
|
||||
->getCandidates(enabledOnly: true)
|
||||
;
|
||||
|
||||
$this->assertTrue($candidates[0]->skipped);
|
||||
$this->assertFalse($candidates[1]->skipped);
|
||||
}
|
||||
|
||||
public function testGenerateBuildsDraftsFromThePlan(): void
|
||||
{
|
||||
$destination = self::destination(1, 'DKIMP');
|
||||
$guide = new JobProfile();
|
||||
$course = new JobProfile();
|
||||
$guidingFee = new Fee();
|
||||
$courseFee = new Fee();
|
||||
|
||||
$resolver = $this->createStub(StaffingResolver::class);
|
||||
$resolver->method('jobProfile')->willReturnCallback(
|
||||
static fn (StaffingRole $role): JobProfile => StaffingRole::GUIDE === $role ? $guide : $course
|
||||
);
|
||||
$resolver->method('fee')->willReturnCallback(
|
||||
static fn (StaffingFee $fee): Fee => StaffingFee::GUIDING === $fee ? $guidingFee : $courseFee
|
||||
);
|
||||
|
||||
$persisted = [];
|
||||
$entityManager = $this->createMock(EntityManagerInterface::class);
|
||||
$entityManager->method('persist')->willReturnCallback(static function (object $entity) use (&$persisted): void {
|
||||
$persisted[] = $entity;
|
||||
});
|
||||
$entityManager->expects($this->once())->method('flush');
|
||||
|
||||
$generator = $this->generator([], resolver: $resolver, entityManager: $entityManager);
|
||||
|
||||
$assignments = $generator->generate(new StaffingCandidate($destination, self::rule('DKIMP', enabled: true), [
|
||||
new PlannedAssignment(StaffingRole::GUIDE, 2, 5, [StaffingFee::GUIDING]),
|
||||
new PlannedAssignment(StaffingRole::SKI_BEGINNER, 1, null, [StaffingFee::COURSE]),
|
||||
], false));
|
||||
|
||||
$this->assertSame($persisted, $assignments);
|
||||
$this->assertCount(2, $assignments);
|
||||
|
||||
$this->assertSame($destination, $assignments[0]->getDestination());
|
||||
$this->assertSame($guide, $assignments[0]->getJobProfile());
|
||||
$this->assertSame(2, $assignments[0]->getAvailableDispositions());
|
||||
$this->assertSame(5, $assignments[0]->getPickup());
|
||||
$this->assertSame([$guidingFee], $assignments[0]->getFees()->toArray());
|
||||
$this->assertSame(Assignment::STATUS_DRAFT, $assignments[0]->getStatus());
|
||||
|
||||
$this->assertSame($course, $assignments[1]->getJobProfile());
|
||||
$this->assertSame(0, $assignments[1]->getPickup());
|
||||
$this->assertSame([$courseFee], $assignments[1]->getFees()->toArray());
|
||||
}
|
||||
|
||||
public function testASkippedCandidateGeneratesNothing(): void
|
||||
{
|
||||
$entityManager = $this->createMock(EntityManagerInterface::class);
|
||||
$entityManager->expects($this->never())->method('persist');
|
||||
$entityManager->expects($this->never())->method('flush');
|
||||
|
||||
$assignments = $this->generator([], entityManager: $entityManager)->generate(new StaffingCandidate(
|
||||
self::destination(1, 'DKIMP'),
|
||||
self::rule('DKIMP', enabled: true),
|
||||
[new PlannedAssignment(StaffingRole::GUIDE, 1)],
|
||||
true,
|
||||
));
|
||||
|
||||
$this->assertSame([], $assignments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<StaffingRule> $rules
|
||||
*/
|
||||
private function generator(
|
||||
array $rules,
|
||||
?DestinationRepository $destinationRepository = null,
|
||||
?AssignmentRepository $assignmentRepository = null,
|
||||
?StaffingResolver $resolver = null,
|
||||
?EntityManagerInterface $entityManager = null,
|
||||
): AssignmentGenerator {
|
||||
$staffingRuleRepository = $this->createStub(StaffingRuleRepository::class);
|
||||
$staffingRuleRepository->method('getList')->willReturn($rules);
|
||||
|
||||
if (null === $assignmentRepository) {
|
||||
$assignmentRepository = $this->createStub(AssignmentRepository::class);
|
||||
$assignmentRepository->method('getDestinationIdsWithAnyAssignment')->willReturn([]);
|
||||
}
|
||||
|
||||
return new AssignmentGenerator(
|
||||
$staffingRuleRepository,
|
||||
$destinationRepository ?? $this->createStub(DestinationRepository::class),
|
||||
$assignmentRepository,
|
||||
new AssignmentPlanner(),
|
||||
$resolver ?? $this->createStub(StaffingResolver::class),
|
||||
$entityManager ?? $this->createStub(EntityManagerInterface::class),
|
||||
new NullLogger(),
|
||||
);
|
||||
}
|
||||
|
||||
private static function rule(string $productCode, bool $enabled): StaffingRule
|
||||
{
|
||||
return (new StaffingRule())->setProductCode($productCode)->setGuides(1)->setEnabled($enabled);
|
||||
}
|
||||
|
||||
private static function destination(int $id, string $productCode): Destination
|
||||
{
|
||||
$destination = (new Destination())->setProductCode($productCode)->setCode($productCode.'110127');
|
||||
|
||||
(new \ReflectionProperty(Destination::class, 'id'))->setValue($destination, $id);
|
||||
|
||||
return $destination;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user