8000 [Workflow] Added tests for the is_valid() guard expression · symfony/symfony@5969d68 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5969d68

Browse files
committed
[Workflow] Added tests for the is_valid() guard expression
1 parent 8e6900a commit 5969d68

File tree

7 files changed

+99
-74
lines changed

7 files changed

+99
-74
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
/**
1919
* @author Christian Flothmann <christian.flothmann@sensiolabs.de>
20+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
2021
*/
2122
class WorkflowGuardListenerPass implements CompilerPassInterface
2223
{
@@ -31,20 +32,17 @@ public function process(ContainerBuilder $container)
3132

3233
$container->getParameterBag()->remove('workflow.has_guard_listeners');
3334

34-
if (!$container->has('security.token_storage')) {
35-
throw new LogicException('The "security.token_storage" service is needed to be able to use the workflow guard listener.');
36-
}
37-
38-
if (!$container->has('security.authorization_checker')) {
39-
throw new LogicException('The "security.authorization_checker" service is needed to be able to use the workflow guard listener.');
40-
}
41-
42-
if (!$container->has('security.authentication.trust_resolver')) {
43-
throw new LogicException('The "security.authentication.trust_resolver" service is needed to be able to use the workflow guard listener.');
44-
}
45-
46-
if (!$container->has('security.role_hierarchy')) {
47-
throw new LogicException('The "security.role_hierarchy" service is needed to be able to use the workflow guard listener.');
35+
$servicesNeeded = array(
36+
'security.token_storage',
37+
'security.authorization_checker',
38+
'security.authentication.trust_resolver',
39+
'security.role_hierarchy',
40+
);
41+
42+
foreach ($servicesNeeded as $service) {
43+
if (!$container->has($service)) {
44+
throw new LogicException(sprintf('The "%s" service is needed to be able to use the workflow guard listener.', $service));
45+
}
4846
}
4947
}
5048
}

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
use Symfony\Component\Translation\Translator;
8484
use Symfony\Component\Validator\ConstraintValidatorInterface;
8585
use Symfony\Component\Validator\ObjectInitializerInterface;
86+
use Symfony\Component\Validator\Validator\ValidatorInterface;
8687
use Symfony\Component\WebLink\HttpHeaderSerializer;
8788
use Symfony\Component\Workflow;
8889
use Symfony\Component\Yaml\Command\LintCommand as BaseYamlLintCommand;
@@ -589,6 +590,10 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $
589590
throw new LogicException('Cannot guard workflows as the Security component is not installed.');
590591
}
591592

593+
if (!interface_exists(ValidatorInterface::class)) {
594+
throw new LogicException('Cannot guard workflows as the Validator component is not installed.');
595+
}
596+
592597
$eventName = sprintf('workflow.%s.guard.%s', $name, $transitionName);
593598
$guard->addTag('kernel.event_listener', array('event' => $eventName, 'method' => 'onTransition'));
594599
$configuration[$eventName] = $config['guard'];

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.php

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\WorkflowGuardListenerPass;
1616
use Symfony\Component\DependencyInjection\ContainerBuilder;
17-
use Symfony\Component\DependencyInjection\Exception\LogicException;
1817
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
1918
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
2019
use Symfony\Component\Security\Core\Authorization\Authorizati 10000 onCheckerInterface;
2120
use Symfony\Component\Security\Core\Role\RoleHierarchy;
22-
use Symfony\Component\Workflow\EventListener\GuardListener;
21+
use Symfony\Component\Validator\Validator\ValidatorInterface;
2322

2423
class WorkflowGuardListenerPassTest extends TestCase
2524
{
@@ -29,53 +28,37 @@ class WorkflowGuardListenerPassTest extends TestCase
2928
protected function setUp()
3029
{
3130
$this->container = new ContainerBuilder();
32-
$this->container->register('foo.listener.guard', GuardListener::class);
33-
$this->container->register('bar.listener.guard', GuardListener::class);
3431
$this->compilerPass = new WorkflowGuardListenerPass();
3532
}
3633

37-
public function testListenersAreNotRemovedIfParameterIsNotSet()
34+
public function testNoExeptionIfParameterIsNotSet()
3835
{
3936
$this->compilerPass->process($this->container);
4037

41-
$this->assertTrue($this->container->hasDefinition('foo.listener.guard'));
42-
$this->assertTrue($this->container->hasDefinition('bar.listener.guard'));
43-
}
44-
45-
public function testParameterIsRemovedWhenThePassIsProcessed()
46-
{
47-
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
48-
49-
try {
50-
$this->compilerPass->process($this->container);
51-
} catch (LogicException $e) {
52-
// Here, we are not interested in the exception handling. This is tested further down.
53-
}
54-
5538
$this->assertFalse($this->container->hasParameter('workflow.has_guard_listeners'));
5639
}
5740

58-
public function testListenersAreNotRemovedIfAllDependenciesArePresent()
41+
public function testNoExeptionIfAllDependenciesArePresent()
5942
{
60-
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
43+
$this->container->setParameter('workflow.has_guard_listeners', true);
6144
$this->container->register('security.token_storage', TokenStorageInterface::class);
6245
$this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class);
6346
$this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class);
6447
$this->container->register('security.role_hierarchy', RoleHierarchy::class);
48+
$this->container->register('validator', ValidatorInterface::class);
6549

6650
$this->compilerPass->process($this->container);
6751

68-
$this->assertTrue($this->container->hasDefinition('foo.listener.guard'));
69-
$this->assertTrue($this->container->hasDefinition('bar.listener.guard'));
52+
$this->assertFalse($this->container->hasParameter('workflow.has_guard_listeners'));
7053
}
7154

7255
/**
7356
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
7457
* @expectedExceptionMessage The "security.token_storage" service is needed to be able to use the workflow guard listener.
7558
*/
76-
public function testListenersAreRemovedIfTheTokenStorageServiceIsNotPresent()
59+
public function testExceptionIfTheTokenStorageServiceIsNotPresent()
7760
{
78-
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
61+
$this->container->setParameter('workflow.has_guard_listeners', true);
7962
$this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class);
8063
$this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class);
8164
$this->container->register('security.role_hierarchy', RoleHierarchy::class);
@@ -87,9 +70,9 @@ public function testListenersAreRemovedIfTheTokenStorageServiceIsNotPresent()
8770
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
8871
* @expectedExceptionMessage The "security.authorization_checker" service is needed to be able to use the workflow guard listener.
8972
*/
90-
public function testListenersAreRemovedIfTheAuthorizationCheckerServiceIsNotPresent()
73+
public function testExceptionIfTheAuthorizationCheckerServiceIsNotPresent()
9174
{
92-
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
75+
$this->container->setParameter('workflow.has_guard_listeners', true);
9376
$this->container->register('security.token_storage', TokenStorageInterface::class);
9477
$this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class);
9578
$this->container->register('security.role_hierarchy', RoleHierarchy::class);
@@ -101,9 +84,9 @@ public function testListenersAreRemovedIfTheAuthorizationCheckerServiceIsNotPres
10184
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
10285
* @expectedExceptionMessage The "security.authentication.trust_resolver" service is needed to be able to use the workflow guard listener.
10386
*/
104-
public function testListenersAreRemovedIfTheAuthenticationTrustResolverServiceIsNotPresent()
87+
public function testExceptionIfTheAuthenticationTrustResolverServiceIsNotPresent()
10588
{
106-
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
89+
$this->container->setParameter('workflow.has_guard_listeners', true);
10790
$this->container->register('security.token_storage', TokenStorageInterface::class);
10891
$this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class);
10992
$this->container->register('security.role_hierarchy', RoleHierarchy::class);
@@ -115,9 +98,9 @@ public function testListenersAreRemovedIfTheAuthenticationTrustResolverServiceIs
11598
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
11699
* @expectedExceptionMessage The "security.role_hierarchy" service is needed to be able to use the workflow guard listener.
117100
*/
118-
public function testListenersAreRemovedIfTheRoleHierarchyServiceIsNotPresent()
101+
public function testExceptionIfTheRoleHierarchyServiceIsNotPresent()
119102
{
120-
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
103+
$this->container->setParameter('workflow.has_guard_listeners', true);
121104
$this->container->register('security.token_storage', TokenStorageInterface::class);
122105
$this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class);
123106
$this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class);

src/Symfony/Component/Workflow/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ CHANGELOG
99
3.4.0
1010
-----
1111

12-
* Add guard `is_valid()` method support
12+
* Add guard `is_valid()` method support.
1313
* Added support for `Event::getWorkflowName()` for "announce" events.
1414
* Added `workflow.completed` events which are fired after a transition is completed.
1515

src/Symfony/Component/Workflow/EventListener/ExpressionLanguage.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ protected function registerFunctions()
3636
return sprintf('0 === count($validator->validate(%s, null, %s))', $object, $groups);
3737
}, function (array $variables, $object = null, $groups = null) {
3838
if (!$variables['validator'] instanceof ValidatorInterface) {
39-
throw new RuntimeException('Validator not defined, did you install the component?');
39+
throw new RuntimeException('"is_valid" cannot be used as the Validator component is not installed.');
4040
}
4141

4242
$errors = $variables['validator']->validate($object, null, $groups);

src/Symfony/Component/Workflow/Tests/EventListener/GuardListenerTest.php

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
99
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
1010
use Symfony\Component\Security\Core\Role\Role;
11+
use Symfony\Component\Validator\Validator\ValidatorInterface;
1112
use Symfony\Component\Workflow\EventListener\ExpressionLanguage;
1213
use Symfony\Component\Workflow\EventListener\GuardListener;
1314
use Symfony\Component\Workflow\Event\GuardEvent;
@@ -16,59 +17,85 @@
1617

1718
class GuardListenerTest extends TestCase
1819
{
19-
private $tokenStorage;
20+
private $authenticationChecker;
21+
private $validator;
2022
private $listener;
2123

2224
protected function setUp()
2325
{
2426
$configuration = array(
25-
'event_name_a' => 'true',
26-
'event_name_b' => 'false',
27+
'test_is_granted' => 'is_granted("something")',
28+
'test_is_valid' => 'is_valid(subject)',
2729
);
28-
2930
$expressionLanguage = new ExpressionLanguage();
30-
$this->tokenStorage = $this->getMockBuilder(TokenStorageInterface::class)->getMock();
31-
$authenticationChecker = $this->getMockBuilder(AuthorizationCheckerInterface::class)->getMock();
31+
$token = $this->getMockBuilder(TokenInterface::class)->getMock();
32+
$token->expects($this->any())->method('getRoles')->willReturn(array(new Role('ROLE_USER')));
33+
$tokenStorage = $this->getMockBuilder(TokenStorageInterface::class)->getMock();
34+
$tokenStorage->expects($this->any())->method('getToken')->willReturn($token);
35+
$this->authenticationChecker = $this->getMockBuilder(AuthorizationCheckerInterface::class)->getMock();
3236
$trustResolver = $this->getMockBuilder(AuthenticationTrustResolverInterface::class)->getMock();
33-
34-
$this->listener = new GuardListener($configuration, $expressionLanguage, $this->tokenStorage, $authenticationChecker, $trustResolver);
37+
$this->validator = $this->getMockBuilder(ValidatorInterface::class)->getMock();
38+
$this->listener = new GuardListener($configuration, $expressionLanguage, $tokenStorage, $this->authenticationChecker, $trustResolver, null, $this->validator);
3539
}
3640

3741
protected function tearDown()
3842
{
43+
$this->authenticationChecker = null;
44+
$this->validator = null;
3945
$this->listener = null;
4046
}
4147

4248
public function testWithNotSupportedEvent()
4349
{
4450
$event = $this->createEvent();
45-
$this->configureTokenStorage(false);
51+
$this->configureAuthenticationChecker(false);
52+
$this->configureValidator(false);
4653

4754
$this->listener->onTransition($event, 'not supported');
4855

4956
$this->assertFalse($event->isBlocked());
5057
}
5158

52-
public function testWithSupportedEventAndReject()
59+
public function testWithSecuritySupportedEventAndReject()
5360
{
5461
$event = $this->createEvent();
55-
$this->configureTokenStorage(true);
62+
$this->configureAuthenticationChecker(true, false);
5663

57-
$this->listener->onTransition($event, 'event_name_a');
64+
$this->listener->onTransition($event, 'test_is_granted');
65+
66+
$this->assertTrue($event->isBlocked());
67+
}
68+
69+
public function testWithSecuritySupportedEventAndAccept()
70+
{
71+
$event = $this->createEvent();
72+
$this->configureAuthenticationChecker(true, true);
73+
74+
$this->listener->onTransition($event, 'test_is_granted');
5875

5976
$this->assertFalse($event->isBlocked());
6077
}
6178

62-
public function testWithSupportedEventAndAccept()
79+
public function testWithValidatorSupportedEventAndReject()
6380
{
6481
$event = $this->createEvent();
65-
$this->configureTokenStorage(true);
82+
$this->configureValidator(true, false);
6683

67-
$this->listener->onTransition($event, 'event_name_b');
84+
$this->listener->onTransition($event, 'test_is_valid');
6885

6986
$this->assertTrue($event->isBlocked());
7087
}
7188

89+
public function testWithValidatorSupportedEventAndAccept()
90+
{
91+
$event = $this->createEvent();
92+
$this->configureValidator(true, true);
93+
94+
$this->listener->onTransition($event, 'test_is_valid');
95+
96+
$this->assertFalse($event->isBlocked());
97+
}
98+
7299
private function createEvent()
73100
{
74101
$subject = new \stdClass();
@@ -78,28 +105,39 @@ private function createEvent()
78105
return new GuardEvent($subject, $subject->marking, $transition);
79106
}
80107

81-
private function configureTokenStorage($hasUser)
108+
private function configureAuthenticationChecker($isUsed, $granted = true)
82109
{
83-
if (!$hasUser) {
84-
$this->tokenStorage
110+
if (!$isUsed) {
111+
$this->authenticationChecker
85112
->expects($this->never())
86-
->method('getToken')
113+
->method('isGranted')
87114
;
88115

89116
return;
90117
}
91118

92-
$token = $this->getMockBuilder(TokenInterface::class)->getMock();
93-
$token
119+
$this->authenticationChecker
94120
->expects($this->once())
95-
->method('getRoles')
96-
->willReturn(array(new Role('ROLE_ADMIN')))
121+
->method('isGranted')
122+
->willReturn($granted)
97123
;
124+
}
125+
126+
private function configureValidator($isUsed, $valid = true)
127+
{
128+
if (!$isUsed) {
129+
$this->validator
130+
->expects($this->never())
131+
->method('validate')
132+
;
133+
134+
return;
135+
}
98136

99-
$this->tokenStorage
137+
$this->validator
100138
->expects($this->once())
101-
->method('getToken')
102-
->willReturn($token)
139+
->method('validate')
140+
->willReturn($valid ? array() : array('a violation'))
103141
;
104142
}
105143
}

src/Symfony/Component/Workflow/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
"symfony/dependency-injection": "~3.4|~4.0",
2929
"symfony/event-dispatcher": "~3.4|~4.0",
3030
"symfony/expression-language": "~3.4|~4.0",
31-
"symfony/security-core": "~3.4|~4.0"
31+
"symfony/security-core": "~3.4|~4.0",
32+
"symfony/validator": "~3.4|~4.0"
3233
},
3334
"autoload": {
3435
"psr-4": { "Symfony\\Component\\Workflow\\": "" }

0 commit comments

Comments
 (0)
0