Completed
Push — 3.x ( e95e95...638cd1 )
by Oskar
05:54
created

Compiler/ExtensionCompilerPassTest.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Tests\DependencyInjection;
15
16
use Knp\Menu\FactoryInterface;
17
use Knp\Menu\Matcher\MatcherInterface;
18
use Knp\Menu\Provider\MenuProviderInterface;
19
use PHPUnit\Framework\TestCase;
20
use Sonata\AdminBundle\Admin\AbstractAdmin;
21
use Sonata\AdminBundle\Admin\AdminExtensionInterface;
22
use Sonata\AdminBundle\Controller\CRUDController;
23
use Sonata\AdminBundle\DependencyInjection\Compiler\ExtensionCompilerPass;
24
use Sonata\AdminBundle\DependencyInjection\SonataAdminExtension;
25
use Sonata\AdminBundle\Tests\Fixtures\DependencyInjection\TimestampableTrait;
26
use Sonata\BlockBundle\DependencyInjection\SonataBlockExtension;
27
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
28
use Symfony\Bundle\FrameworkBundle\Translation\TranslatorInterface;
29
use Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory;
30
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
31
use Symfony\Component\Config\FileLocatorInterface;
32
use Symfony\Component\DependencyInjection\ContainerBuilder;
33
use Symfony\Component\EventDispatcher\EventDispatcher;
34
use Symfony\Component\Form\FormFactoryInterface;
35
use Symfony\Component\HttpFoundation\RequestStack;
36
use Symfony\Component\HttpFoundation\Session\Session;
37
use Symfony\Component\HttpKernel\KernelInterface;
38
use Symfony\Component\PropertyAccess\PropertyAccessor;
39
use Symfony\Component\Routing\RouterInterface;
40
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
41
use Symfony\Component\Validator\Validator\ValidatorInterface;
42
43
class ExtensionCompilerPassTest extends TestCase
44
{
45
    /** @var SonataAdminExtension $extension */
46
    private $extension;
47
48
    /** @var array $config */
49
    private $config;
50
51
    private $hasTraits;
52
53
    /**
54
     * Root name of the configuration.
55
     *
56
     * @var string
57
     */
58
    private $root;
59
60
    public function setUp(): void
61
    {
62
        $this->extension = new SonataAdminExtension();
63
        $this->config = $this->getConfig();
64
        $this->root = 'sonata.admin';
65
        $this->hasTraits = version_compare(PHP_VERSION, '5.4.0', '>=');
66
    }
67
68
    /**
69
     * @covers \Sonata\AdminBundle\DependencyInjection\SonataAdminExtension::load
70
     */
71
    public function testAdminExtensionLoad(): void
72
    {
73
        $this->extension->load([], $container = $this->getContainer());
74
75
        $this->assertTrue($container->hasParameter($this->root.'.extension.map'));
76
        $this->assertInternalType('array', $extensionMap = $container->getParameter($this->root.'.extension.map'));
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit\Framework\Assert::assertInternalType() has been deprecated with message: https://github.com/sebastianbergmann/phpunit/issues/3369

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
77
78
        $this->assertArrayHasKey('admins', $extensionMap);
79
        $this->assertArrayHasKey('excludes', $extensionMap);
80
        $this->assertArrayHasKey('implements', $extensionMap);
81
        $this->assertArrayHasKey('extends', $extensionMap);
82
        $this->assertArrayHasKey('instanceof', $extensionMap);
83
        $this->assertArrayHasKey('uses', $extensionMap);
84
    }
85
86
    /**
87
     * @covers \Sonata\AdminBundle\DependencyInjection\Compiler\ExtensionCompilerPass::flattenExtensionConfiguration
88
     */
89
    public function testFlattenEmptyExtensionConfiguration(): void
90
    {
91
        $this->extension->load([], $container = $this->getContainer());
92
        $extensionMap = $container->getParameter($this->root.'.extension.map');
93
94
        $method = new \ReflectionMethod(
95
            ExtensionCompilerPass::class, 'flattenExtensionConfiguration'
96
        );
97
98
        $method->setAccessible(true);
99
        $extensionMap = $method->invokeArgs(new ExtensionCompilerPass(), [$extensionMap]);
100
101
        $this->assertArrayHasKey('admins', $extensionMap);
102
        $this->assertArrayHasKey('excludes', $extensionMap);
103
        $this->assertArrayHasKey('implements', $extensionMap);
104
        $this->assertArrayHasKey('extends', $extensionMap);
105
        $this->assertArrayHasKey('instanceof', $extensionMap);
106
        $this->assertArrayHasKey('uses', $extensionMap);
107
108
        $this->assertEmpty($extensionMap['admins']);
109
        $this->assertEmpty($extensionMap['excludes']);
110
        $this->assertEmpty($extensionMap['implements']);
111
        $this->assertEmpty($extensionMap['extends']);
112
        $this->assertEmpty($extensionMap['instanceof']);
113
        $this->assertEmpty($extensionMap['uses']);
114
    }
115
116
    /**
117
     * @covers \Sonata\AdminBundle\DependencyInjection\Compiler\ExtensionCompilerPass::flattenExtensionConfiguration
118
     */
119
    public function testFlattenExtensionConfiguration(): void
120
    {
121
        $config = $this->getConfig();
122
        $this->extension->load([$config], $container = $this->getContainer());
123
        $extensionMap = $container->getParameter($this->root.'.extension.map');
124
125
        $method = new \ReflectionMethod(
126
            ExtensionCompilerPass::class, 'flattenExtensionConfiguration'
127
        );
128
129
        $method->setAccessible(true);
130
        $extensionMap = $method->invokeArgs(new ExtensionCompilerPass(), [$extensionMap]);
131
132
        // Admins
133
        $this->assertArrayHasKey('admins', $extensionMap);
134
        $this->assertCount(1, $extensionMap['admins']);
135
136
        $this->assertArrayHasKey('sonata_extension_publish', $extensionMap['admins']['sonata_post_admin']);
137
        $this->assertCount(1, $extensionMap['admins']['sonata_post_admin']);
138
139
        // Excludes
140
        $this->assertArrayHasKey('excludes', $extensionMap);
141
        $this->assertCount(2, $extensionMap['excludes']);
142
143
        $this->assertArrayHasKey('sonata_article_admin', $extensionMap['excludes']);
144
        $this->assertCount(1, $extensionMap['excludes']['sonata_article_admin']);
145
        $this->assertArrayHasKey('sonata_extension_history', $extensionMap['excludes']['sonata_article_admin']);
146
147
        $this->assertArrayHasKey('sonata_post_admin', $extensionMap['excludes']);
148
        $this->assertCount(1, $extensionMap['excludes']['sonata_post_admin']);
149
        $this->assertArrayHasKey('sonata_extension_order', $extensionMap['excludes']['sonata_post_admin']);
150
151
        // Implements
152
        $this->assertArrayHasKey('implements', $extensionMap);
153
        $this->assertCount(1, $extensionMap['implements']);
154
155
        $this->assertArrayHasKey(Publishable::class, $extensionMap['implements']);
156
        $this->assertCount(2, $extensionMap['implements'][Publishable::class]);
157
        $this->assertArrayHasKey('sonata_extension_publish', $extensionMap['implements'][Publishable::class]);
158
        $this->assertArrayHasKey('sonata_extension_order', $extensionMap['implements'][Publishable::class]);
159
160
        // Extends
161
        $this->assertArrayHasKey('extends', $extensionMap);
162
        $this->assertCount(1, $extensionMap['extends']);
163
164
        $this->assertArrayHasKey(Post::class, $extensionMap['extends']);
165
        $this->assertCount(1, $extensionMap['extends'][Post::class]);
166
        $this->assertArrayHasKey('sonata_extension_order', $extensionMap['extends'][Post::class]);
167
168
        // Instanceof
169
        $this->assertArrayHasKey('instanceof', $extensionMap);
170
        $this->assertCount(1, $extensionMap['instanceof']);
171
172
        $this->assertArrayHasKey(Post::class, $extensionMap['instanceof']);
173
        $this->assertCount(1, $extensionMap['instanceof'][Post::class]);
174
        $this->assertArrayHasKey('sonata_extension_history', $extensionMap['instanceof'][Post::class]);
175
176
        // Uses
177
        $this->assertArrayHasKey('uses', $extensionMap);
178
179
        if ($this->hasTraits) {
180
            $this->assertCount(1, $extensionMap['uses']);
181
            $this->assertArrayHasKey(TimestampableTrait::class, $extensionMap['uses']);
182
            $this->assertCount(1, $extensionMap['uses'][TimestampableTrait::class]);
183
            $this->assertArrayHasKey('sonata_extension_post', $extensionMap['uses'][TimestampableTrait::class]);
184
        } else {
185
            $this->assertCount(0, $extensionMap['uses']);
186
            $this->assertArrayNotHasKey(TimestampableTrait::class, $extensionMap['uses']);
187
        }
188
    }
189
190
    /**
191
     * @covers \Sonata\AdminBundle\DependencyInjection\Compiler\ExtensionCompilerPass::process
192
     */
193
    public function testProcessWithInvalidExtensionId(): void
194
    {
195
        $this->expectException(\InvalidArgumentException::class);
196
197
        $config = [
198
            'extensions' => [
199
                'sonata_extension_unknown' => [
200
                    'excludes' => ['sonata_article_admin'],
201
                    'instanceof' => [Post::class],
202
                ],
203
            ],
204
        ];
205
206
        $container = $this->getContainer();
207
        $this->extension->load([$config], $container);
208
209
        $extensionsPass = new ExtensionCompilerPass();
210
        $extensionsPass->process($container);
211
        $container->compile();
212
    }
213
214
    /**
215
     * @doesNotPerformAssertions
216
     * @covers \Sonata\AdminBundle\DependencyInjection\Compiler\ExtensionCompilerPass::process
217
     */
218
    public function testProcessWithInvalidAdminId(): void
219
    {
220
        $config = [
221
            'extensions' => [
222
                'sonata_extension_publish' => [
223
                    'admins' => ['sonata_unknown_admin'],
224
                    'implements' => [Publishable::class],
225
                ],
226
            ],
227
        ];
228
229
        $container = $this->getContainer();
230
        $this->extension->load([$config], $container);
231
232
        $extensionsPass = new ExtensionCompilerPass();
233
        $extensionsPass->process($container);
234
        $container->compile();
235
236
        // nothing should fail the extension just isn't added to the 'sonata_unknown_admin'
237
    }
238
239
    /**
240
     * @covers \Sonata\AdminBundle\DependencyInjection\Compiler\ExtensionCompilerPass::process
241
     */
242
    public function testProcess(): void
243
    {
244
        $container = $this->getContainer();
245
        $this->extension->load([$this->config], $container);
246
247
        $extensionsPass = new ExtensionCompilerPass();
248
        $extensionsPass->process($container);
249
        $container->compile();
250
251
        $this->assertTrue($container->hasDefinition('sonata_extension_publish'));
252
        $this->assertTrue($container->hasDefinition('sonata_extension_history'));
253
        $this->assertTrue($container->hasDefinition('sonata_extension_order'));
254
        $this->assertTrue($container->hasDefinition('sonata_extension_security'));
255
        $this->assertTrue($container->hasDefinition('sonata_extension_timestamp'));
256
257
        $this->assertTrue($container->hasDefinition('sonata_post_admin'));
258
        $this->assertTrue($container->hasDefinition('sonata_article_admin'));
259
        $this->assertTrue($container->hasDefinition('sonata_news_admin'));
260
261
        $securityExtension = $container->get('sonata_extension_security');
262
        $publishExtension = $container->get('sonata_extension_publish');
263
        $historyExtension = $container->get('sonata_extension_history');
264
        $orderExtension = $container->get('sonata_extension_order');
265
        $filterExtension = $container->get('sonata_extension_filter');
266
267
        $def = $container->get('sonata_post_admin');
268
        $extensions = $def->getExtensions();
269
        $this->assertCount(4, $extensions);
270
271
        $this->assertSame($historyExtension, $extensions[0]);
272
        $this->assertSame($publishExtension, $extensions[2]);
273
        $this->assertSame($securityExtension, $extensions[3]);
274
275
        $def = $container->get('sonata_article_admin');
276
        $extensions = $def->getExtensions();
277
        $this->assertCount(5, $extensions);
278
279
        $this->assertSame($filterExtension, $extensions[0]);
280
        $this->assertSame($securityExtension, $extensions[1]);
281
        $this->assertSame($publishExtension, $extensions[2]);
282
        $this->assertSame($orderExtension, $extensions[4]);
283
284
        $def = $container->get('sonata_news_admin');
285
        $extensions = $def->getExtensions();
286
        $this->assertCount(5, $extensions);
287
        $this->assertSame($historyExtension, $extensions[0]);
288
        $this->assertSame($securityExtension, $extensions[1]);
289
        $this->assertSame($filterExtension, $extensions[2]);
290
        $this->assertSame($orderExtension, $extensions[4]);
291
    }
292
293
    /**
294
     * @doesNotPerformAssertions
295
     */
296
    public function testProcessThrowsExceptionIfTraitsAreNotAvailable(): void
297
    {
298
        if (!$this->hasTraits) {
299
            $this->expectException(InvalidConfigurationException::class, 'PHP >= 5.4.0 is required to use traits.');
300
        }
301
302
        $config = [
303
            'extensions' => [
304
                'sonata_extension_post' => [
305
                    'uses' => [TimestampableTrait::class],
306
                ],
307
            ],
308
        ];
309
310
        $container = $this->getContainer();
311
        $this->extension->load([$config], $container);
312
313
        $extensionsPass = new ExtensionCompilerPass();
314
        $extensionsPass->process($container);
315
        $container->compile();
316
    }
317
318
    /**
319
     * @return array
320
     */
321
    protected function getConfig()
322
    {
323
        $config = [
324
            'extensions' => [
325
                'sonata_extension_publish' => [
326
                    'admins' => ['sonata_post_admin'],
327
                    'implements' => [Publishable::class],
328
                ],
329
                'sonata_extension_history' => [
330
                    'excludes' => ['sonata_article_admin'],
331
                    'instanceof' => [Post::class],
332
                    'priority' => 255,
333
                ],
334
                'sonata_extension_order' => [
335
                    'excludes' => ['sonata_post_admin'],
336
                    'extends' => [Post::class],
337
                    'implements' => [Publishable::class],
338
                    'priority' => -128,
339
                ],
340
            ],
341
        ];
342
343
        if ($this->hasTraits) {
344
            $config['extensions']['sonata_extension_post']['uses'] = [TimestampableTrait::class];
345
        }
346
347
        return $config;
348
    }
349
350
    private function getContainer(): ContainerBuilder
351
    {
352
        $container = new ContainerBuilder();
353
        $container->setParameter('kernel.bundles', [
354
            'SonataCoreBundle' => true,
355
            'KnpMenuBundle' => true,
356
        ]);
357
        $container->setParameter('kernel.cache_dir', '/tmp');
358
        $container->setParameter('kernel.debug', true);
359
360
        // Add dependencies for SonataAdminBundle (these services will never get called so dummy classes will do)
361
        $container
362
            ->register('twig')
363
            ->setClass(EngineInterface::class);
364
        $container
365
            ->register('templating')
366
            ->setClass(EngineInterface::class);
367
        $container
368
            ->register('translator')
369
            ->setClass(TranslatorInterface::class);
370
        $container
371
            ->register('validator.validator_factory')
372
            ->setClass(ConstraintValidatorFactory::class);
373
        $container
374
            ->register('router')
375
            ->setClass(RouterInterface::class);
376
        $container
377
            ->register('property_accessor')
378
            ->setClass(PropertyAccessor::class);
379
        $container
380
            ->register('form.factory')
381
            ->setClass(FormFactoryInterface::class);
382
        $container
383
            ->register('validator')
384
            ->setClass(ValidatorInterface::class);
385
        $container
386
            ->register('knp_menu.factory')
387
            ->setClass(FactoryInterface::class);
388
        $container
389
            ->register('knp_menu.matcher')
390
            ->setClass(MatcherInterface::class);
391
        $container
392
            ->register('knp_menu.menu_provider')
393
            ->setClass(MenuProviderInterface::class);
394
        $container
395
            ->register('request_stack')
396
            ->setClass(RequestStack::class);
397
        $container
398
            ->register('session')
399
            ->setClass(Session::class);
400
        $container
401
            ->register('security.authorization_checker')
402
            ->setClass(AuthorizationCheckerInterface::class);
403
404
        // Add admin definition's
405
        $container
406
            ->register('sonata_post_admin')
407
            ->setPublic(true)
408
            ->setClass(MockAdmin::class)
409
            ->setArguments(['', Post::class, CRUDController::class])
410
            ->addTag('sonata.admin');
411
        $container
412
            ->register('sonata_news_admin')
413
            ->setPublic(true)
414
            ->setClass(MockAdmin::class)
415
            ->setArguments(['', News::class, CRUDController::class])
416
            ->addTag('sonata.admin');
417
        $container
418
            ->register('sonata_article_admin')
419
            ->setPublic(true)
420
            ->setClass(MockAdmin::class)
421
            ->setArguments(['', Article::class, CRUDController::class])
422
            ->addTag('sonata.admin');
423
        $container
424
            ->register('event_dispatcher')
425
            ->setClass(EventDispatcher::class);
426
427
        // Add admin extension definition's
428
        $extensionClass = \get_class($this->createMock(AdminExtensionInterface::class));
429
430
        $container
431
            ->register('sonata_extension_publish')
432
            ->setPublic(true)
433
            ->setClass($extensionClass);
434
        $container
435
            ->register('sonata_extension_history')
436
            ->setPublic(true)
437
            ->setClass($extensionClass);
438
        $container
439
            ->register('sonata_extension_order')
440
            ->setPublic(true)
441
            ->setClass($extensionClass);
442
        $container
443
            ->register('sonata_extension_timestamp')
444
            ->setPublic(true)
445
            ->setClass($extensionClass);
446
        $container
447
            ->register('sonata_extension_security')
448
            ->setPublic(true)
449
            ->setClass($extensionClass)
450
            ->addTag('sonata.admin.extension', ['global' => true]);
451
        $container
452
            ->register('sonata_extension_filter')
453
            ->setPublic(true)
454
            ->setClass($extensionClass)
455
            ->addTag('sonata.admin.extension', ['target' => 'sonata_news_admin'])
456
            ->addTag('sonata.admin.extension', ['target' => 'sonata_article_admin']);
457
458
        // Add definitions for sonata.templating service
459
        $container
460
            ->register('kernel')
461
            ->setClass(KernelInterface::class);
462
        $container
463
            ->register('file_locator')
464
            ->setClass(FileLocatorInterface::class);
465
466
        $blockExtension = new SonataBlockExtension();
467
        $blockExtension->load([], $container);
468
469
        return $container;
470
    }
471
}
472
473
class MockAdmin extends AbstractAdmin
474
{
475
}
476
477
class MockAbstractServiceAdmin extends AbstractAdmin
478
{
479
    private $extraArgument;
480
481
    public function __construct($code, $class, $baseControllerName, $extraArgument)
482
    {
483
        $this->extraArgument = $extraArgument;
484
485
        parent::__construct($code, $class, $baseControllerName);
486
    }
487
}
488
489
class Post
490
{
491
}
492
interface Publishable
493
{
494
}
495
class News extends Post
496
{
497
}
498
class Article implements Publishable
499
{
500
}
501