mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
This allow to effectively check that a user is allowed to apply all transitions on a workflow and, if yes, enable the given transition.
179 lines
8.1 KiB
PHP
179 lines
8.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/*
|
|
* Chill is a software for social workers
|
|
*
|
|
* For the full copyright and license information, please view
|
|
* the LICENSE file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Chill\MainBundle\Tests\Workflow\EventSubscriber;
|
|
|
|
use Chill\MainBundle\Entity\User;
|
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
|
use Chill\MainBundle\Security\Authorization\EntityWorkflowTransitionVoter;
|
|
use Chill\MainBundle\Templating\Entity\UserRender;
|
|
use Chill\MainBundle\Workflow\EntityWorkflowMarkingStore;
|
|
use Chill\MainBundle\Workflow\EventSubscriber\EntityWorkflowGuardTransition;
|
|
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Prophecy\Argument;
|
|
use Prophecy\PhpUnit\ProphecyTrait;
|
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
use Symfony\Component\Security\Core\Security;
|
|
use Symfony\Component\Workflow\DefinitionBuilder;
|
|
use Symfony\Component\Workflow\Exception\NotEnabledTransitionException;
|
|
use Symfony\Component\Workflow\Metadata\InMemoryMetadataStore;
|
|
use Symfony\Component\Workflow\Registry;
|
|
use Symfony\Component\Workflow\SupportStrategy\WorkflowSupportStrategyInterface;
|
|
use Symfony\Component\Workflow\Transition;
|
|
use Symfony\Component\Workflow\Workflow;
|
|
use Symfony\Component\Workflow\WorkflowInterface;
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @coversNothing
|
|
*/
|
|
class EntityWorkflowGuardTransitionTest extends TestCase
|
|
{
|
|
use ProphecyTrait;
|
|
|
|
public static function buildRegistry(?EventSubscriberInterface $eventSubscriber): Registry
|
|
{
|
|
$builder = new DefinitionBuilder();
|
|
$builder
|
|
->setInitialPlaces(['initial'])
|
|
->addPlaces(['initial', 'intermediate', 'step1', 'step2', 'step3'])
|
|
->addTransition(new Transition('intermediate', 'initial', 'intermediate'))
|
|
->addTransition($transition1 = new Transition('transition1', 'intermediate', 'step1'))
|
|
->addTransition($transition2 = new Transition('transition2', 'intermediate', 'step2'))
|
|
->addTransition($transition3 = new Transition('transition3', 'intermediate', 'step3'))
|
|
;
|
|
|
|
$transitionMetadata = new \SplObjectStorage();
|
|
$transitionMetadata->attach($transition1, ['transitionGuard' => 'only-dest']);
|
|
$transitionMetadata->attach($transition2, ['transitionGuard' => 'only-dest+system']);
|
|
$transitionMetadata->attach($transition3, ['transitionGuard' => 'system']);
|
|
|
|
$builder->setMetadataStore(new InMemoryMetadataStore(transitionsMetadata: $transitionMetadata));
|
|
|
|
if (null !== $eventSubscriber) {
|
|
$eventDispatcher = new EventDispatcher();
|
|
$eventDispatcher->addSubscriber($eventSubscriber);
|
|
}
|
|
|
|
$workflow = new Workflow($builder->build(), new EntityWorkflowMarkingStore(), $eventDispatcher ?? null, 'dummy');
|
|
|
|
$registry = new Registry();
|
|
$registry->addWorkflow(
|
|
$workflow,
|
|
new class () implements WorkflowSupportStrategyInterface {
|
|
public function supports(WorkflowInterface $workflow, object $subject): bool
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
);
|
|
|
|
return $registry;
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideBlockingTransition
|
|
*/
|
|
public function testTransitionGuardBlocked(EntityWorkflow $entityWorkflow, string $transition, ?User $user, bool $isGrantedAllTransition, string $uuid): void
|
|
{
|
|
$userRender = $this->prophesize(UserRender::class);
|
|
$userRender->renderString(Argument::type(User::class), [])->willReturn('user-as-string');
|
|
$security = $this->prophesize(Security::class);
|
|
$security->getUser()->willReturn($user);
|
|
$security->isGranted(EntityWorkflowTransitionVoter::APPLY_ALL_TRANSITIONS, $entityWorkflow->getCurrentStep())
|
|
->willReturn($isGrantedAllTransition);
|
|
|
|
$transitionGuard = new EntityWorkflowGuardTransition($userRender->reveal(), $security->reveal());
|
|
$registry = self::buildRegistry($transitionGuard);
|
|
|
|
$workflow = $registry->get($entityWorkflow, 'dummy');
|
|
|
|
$context = new WorkflowTransitionContextDTO($entityWorkflow);
|
|
|
|
self::expectException(NotEnabledTransitionException::class);
|
|
try {
|
|
$workflow->apply($entityWorkflow, $transition, ['context' => $context, 'byUser' => $user, 'transition' => $transition, 'transitionAt' => new \DateTimeImmutable('now')]);
|
|
} catch (NotEnabledTransitionException $e) {
|
|
$list = $e->getTransitionBlockerList();
|
|
|
|
self::assertEquals(1, $list->count());
|
|
$list = iterator_to_array($list->getIterator());
|
|
self::assertEquals($uuid, $list[0]->getCode());
|
|
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideValidTransition
|
|
*/
|
|
public function testTransitionGuardValid(EntityWorkflow $entityWorkflow, string $transition, ?User $user, bool $isGrantedAllTransition, string $newStep): void
|
|
{
|
|
$userRender = $this->prophesize(UserRender::class);
|
|
$userRender->renderString(Argument::type(User::class), [])->willReturn('user-as-string');
|
|
$security = $this->prophesize(Security::class);
|
|
$security->getUser()->willReturn($user);
|
|
$security->isGranted(EntityWorkflowTransitionVoter::APPLY_ALL_TRANSITIONS, $entityWorkflow->getCurrentStep())
|
|
->willReturn($isGrantedAllTransition);
|
|
|
|
$transitionGuard = new EntityWorkflowGuardTransition($userRender->reveal(), $security->reveal());
|
|
$registry = self::buildRegistry($transitionGuard);
|
|
|
|
$workflow = $registry->get($entityWorkflow, 'dummy');
|
|
$context = new WorkflowTransitionContextDTO($entityWorkflow);
|
|
|
|
$workflow->apply($entityWorkflow, $transition, ['context' => $context, 'byUser' => $user, 'transition' => $transition, 'transitionAt' => new \DateTimeImmutable('now')]);
|
|
|
|
self::assertEquals($newStep, $entityWorkflow->getStep());
|
|
}
|
|
|
|
public static function provideBlockingTransition(): iterable
|
|
{
|
|
yield [self::buildEntityWorkflow([new User()]), 'transition1', new User(), false, 'f3eeb57c-7532-11ec-9495-e7942a2ac7bc'];
|
|
yield [self::buildEntityWorkflow([]), 'transition1', null, false, 'd9e39a18-704c-11ef-b235-8fe0619caee7'];
|
|
yield [self::buildEntityWorkflow([new User()]), 'transition1', null, false, 'd9e39a18-704c-11ef-b235-8fe0619caee7'];
|
|
yield [self::buildEntityWorkflow([$user = new User()]), 'transition3', $user, false, '5b6b95e0-704d-11ef-a5a9-4b6fc11a8eeb'];
|
|
yield [self::buildEntityWorkflow([$user = new User()]), 'transition3', $user, true, '5b6b95e0-704d-11ef-a5a9-4b6fc11a8eeb'];
|
|
}
|
|
|
|
public static function provideValidTransition(): iterable
|
|
{
|
|
yield [self::buildEntityWorkflow([$u = new User()]), 'transition1', $u, false, 'step1'];
|
|
yield [self::buildEntityWorkflow([$u = new User()]), 'transition2', $u, false, 'step2'];
|
|
yield [self::buildEntityWorkflow([new User()]), 'transition2', null, false, 'step2'];
|
|
yield [self::buildEntityWorkflow([]), 'transition2', null, false, 'step2'];
|
|
yield [self::buildEntityWorkflow([new User()]), 'transition3', null, false, 'step3'];
|
|
yield [self::buildEntityWorkflow([]), 'transition3', null, false, 'step3'];
|
|
|
|
// transition allowed thanks to permission "apply all transitions"
|
|
yield [self::buildEntityWorkflow([new User()]), 'transition1', new User(), true, 'step1'];
|
|
yield [self::buildEntityWorkflow([new User()]), 'transition2', new User(), true, 'step2'];
|
|
}
|
|
|
|
public static function buildEntityWorkflow(array $futureDestUsers): EntityWorkflow
|
|
{
|
|
$registry = self::buildRegistry(null);
|
|
$baseContext = ['transition' => 'intermediate', 'transitionAt' => new \DateTimeImmutable()];
|
|
|
|
// test a user not is destination is blocked
|
|
$entityWorkflow = new EntityWorkflow();
|
|
$workflow = $registry->get($entityWorkflow, 'dummy');
|
|
$dto = new WorkflowTransitionContextDTO($entityWorkflow);
|
|
$dto->futureDestUsers = $futureDestUsers;
|
|
$workflow->apply($entityWorkflow, 'intermediate', ['context' => $dto, ...$baseContext]);
|
|
|
|
return $entityWorkflow;
|
|
}
|
|
}
|