mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-05 14:25:00 +00:00
Refactor workflow guard logic and add internal methods
Removed guard logic from EntityWorkflowTransitionEventSubscriber and created a new EntityWorkflowGuardTransition class for separation of concerns. Marked several setter methods in EntityWorkflowStepSignature as internal to guide proper usage. Added comprehensive tests to ensure the new guard logic functions correctly.
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
<?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\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, 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);
|
||||
|
||||
$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, 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);
|
||||
|
||||
$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(), 'f3eeb57c-7532-11ec-9495-e7942a2ac7bc'];
|
||||
yield [self::buildEntityWorkflow([]), 'transition1', null, 'd9e39a18-704c-11ef-b235-8fe0619caee7'];
|
||||
yield [self::buildEntityWorkflow([new User()]), 'transition1', null, 'd9e39a18-704c-11ef-b235-8fe0619caee7'];
|
||||
yield [self::buildEntityWorkflow([$user = new User()]), 'transition3', $user, '5b6b95e0-704d-11ef-a5a9-4b6fc11a8eeb'];
|
||||
}
|
||||
|
||||
public static function provideValidTransition(): iterable
|
||||
{
|
||||
yield [self::buildEntityWorkflow([$u = new User()]), 'transition1', $u, 'step1'];
|
||||
yield [self::buildEntityWorkflow([$u = new User()]), 'transition2', $u, 'step2'];
|
||||
yield [self::buildEntityWorkflow([new User()]), 'transition2', null, 'step2'];
|
||||
yield [self::buildEntityWorkflow([]), 'transition2', null, 'step2'];
|
||||
yield [self::buildEntityWorkflow([new User()]), 'transition3', null, 'step3'];
|
||||
yield [self::buildEntityWorkflow([]), 'transition3', null, 'step3'];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user