Skip to content

Commit 36a5735

Browse files
authored
Add support for dynamic route configuration in TestKernel (#37)
1 parent f830ee5 commit 36a5735

File tree

9 files changed

+197
-4
lines changed

9 files changed

+197
-4
lines changed

composer.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
"symfony/event-dispatcher": "^5.4 || ^6.4",
3131
"symfony/filesystem": "^5.4 || ^6.4",
3232
"symfony/framework-bundle": "^5.4 || ^6.4",
33-
"symfony/http-kernel": "^5.4 || ^6.4"
33+
"symfony/http-kernel": "^5.4 || ^6.4",
34+
"symfony/routing": "^5.4 || ^6.4"
3435
},
3536
"require-dev": {
3637
"dama/doctrine-test-bundle": "^6.0 || ^7.0",
@@ -43,7 +44,8 @@
4344
"phpstan/phpstan": "^1.10.60",
4445
"phpstan/phpstan-phpunit": "^1.3.16",
4546
"phpstan/phpstan-symfony": "^1.3.8",
46-
"shipmonk/composer-dependency-analyser": "^1.7"
47+
"shipmonk/composer-dependency-analyser": "^1.7",
48+
"symfony/http-foundation": "^5.4 || ^6.4"
4749
},
4850
"conflict": {
4951
"pimcore/pimcore": ">=11.2.0 <11.2.2"

src/Kernel/TestKernel.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
88
use Symfony\Component\DependencyInjection\ContainerBuilder;
99
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
10+
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
1011

1112
class TestKernel extends CompatibilityKernel
1213
{
1314
private bool $dynamicCache = false;
1415
/** @var list<class-string<BundleInterface>> */
1516
private array $testBundles = [];
17+
/** @var list<string|callable(RoutingConfigurator):void> */
18+
private array $testRoutes = [];
1619
/** @var list<array{CompilerPassInterface, string, int}> */
1720
private array $testCompilerPasses = [];
1821

@@ -34,6 +37,15 @@ public function addTestConfig(string|callable $config): void
3437
$this->dynamicCache = true;
3538
}
3639

40+
/**
41+
* @param string|callable(RoutingConfigurator):void $config path to a config file or a callable which gets the {@see RoutingConfigurator} as its first argument
42+
*/
43+
public function addTestRoute(string|callable $config): void
44+
{
45+
$this->testRoutes[] = $config;
46+
$this->dynamicCache = true;
47+
}
48+
3749
/**
3850
* @param array<string, mixed> $config
3951
*/
@@ -99,6 +111,19 @@ public function registerBundles(): array
99111
return $bundles;
100112
}
101113

114+
protected function configureRoutes(RoutingConfigurator $routes): void
115+
{
116+
parent::configureRoutes($routes);
117+
118+
foreach ($this->testRoutes as $route) {
119+
if (\is_callable($route)) {
120+
$route($routes);
121+
} else {
122+
$routes->import($route);
123+
}
124+
}
125+
}
126+
102127
protected function buildContainer(): ContainerBuilder
103128
{
104129
$container = parent::buildContainer();
@@ -115,6 +140,7 @@ private function getTestConfigHash(): string
115140
return hash('xxh3', json_encode([
116141
$this->testBundles,
117142
array_map(fn ($config) => \is_callable($config) ? self::closureHash($config(...)) : $config, $this->testConfigs),
143+
array_map(fn ($config) => \is_callable($config) ? self::closureHash($config(...)) : $config, $this->testRoutes),
118144
$this->testExtensionConfigs,
119145
array_map(fn ($pass) => [$pass[0]::class, $pass[1], $pass[2]], $this->testCompilerPasses),
120146
], \JSON_THROW_ON_ERROR));
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Neusta\Pimcore\TestingFramework\Test\Attribute;
5+
6+
use Neusta\Pimcore\TestingFramework\Kernel\TestKernel;
7+
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
8+
9+
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
10+
final class ConfigureRoute implements KernelConfiguration
11+
{
12+
/**
13+
* @param string|\Closure(RoutingConfigurator):void $config path to a config file or a closure which gets the {@see RoutingConfigurator} as its first argument
14+
*/
15+
public function __construct(
16+
private readonly string|\Closure $config,
17+
) {
18+
}
19+
20+
public function configure(TestKernel $kernel): void
21+
{
22+
$kernel->addTestRoute($this->config);
23+
}
24+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Neusta\Pimcore\TestingFramework\Tests\Fixtures\Controller;
5+
6+
use Symfony\Component\HttpFoundation\Request;
7+
use Symfony\Component\HttpFoundation\Response;
8+
9+
final class ExampleController
10+
{
11+
public function __invoke(Request $request): Response
12+
{
13+
return new Response('');
14+
}
15+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php declare(strict_types=1);
2+
3+
use Neusta\Pimcore\TestingFramework\Tests\Fixtures\Controller\ExampleController;
4+
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
5+
6+
return function (RoutingConfigurator $routes): void {
7+
$routes->add('example_route', '/example')
8+
->controller(ExampleController::class)
9+
;
10+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<routes xmlns="http://symfony.com/schema/routing"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://symfony.com/schema/routing
5+
https://symfony.com/schema/routing/routing-1.0.xsd">
6+
7+
<route id="example_route" path="/example"
8+
controller="Neusta\Pimcore\TestingFramework\Tests\Fixtures\Controller\ExampleController"/>
9+
</routes>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
example_route:
2+
path: /example
3+
controller: Neusta\Pimcore\TestingFramework\Tests\Fixtures\Controller\ExampleController

tests/Functional/KernelCacheTest.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
use Neusta\Pimcore\TestingFramework\Kernel\TestKernel;
77
use Neusta\Pimcore\TestingFramework\Test\ConfigurableKernelTestCase;
88
use Neusta\Pimcore\TestingFramework\Tests\Fixtures\ConfigurationBundle\ConfigurationBundle;
9+
use Neusta\Pimcore\TestingFramework\Tests\Fixtures\Controller\ExampleController;
910
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1011
use Symfony\Component\DependencyInjection\ContainerBuilder;
12+
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
1113

1214
class KernelCacheTest extends ConfigurableKernelTestCase
1315
{
@@ -27,6 +29,11 @@ public function it_does_not_change_the_cache_directory_of_the_standard_kernel():
2729
public function it_creates_a_distinct_cache_directory_per_test_config(): void
2830
{
2931
$cacheDirs = [
32+
self::bootKernel(['config' => function (TestKernel $kernel) {
33+
// Trigger the dynamic cache without configuring anything.
34+
// This is to ensure that the hash changes when the kernel is actually configured.
35+
(new \ReflectionProperty($kernel, 'dynamicCache'))->setValue($kernel, true);
36+
}])->getCacheDir(),
3037
self::bootKernel(['config' => function (TestKernel $kernel) {
3138
$kernel->addTestExtensionConfig('framework', ['secret' => 'foo']);
3239
}])->getCacheDir(),
@@ -53,9 +60,14 @@ public function process(ContainerBuilder $container): void
5360
}
5461
});
5562
}])->getCacheDir(),
63+
self::bootKernel(['config' => function (TestKernel $kernel) {
64+
$kernel->addTestRoute(function (RoutingConfigurator $routes): void {
65+
$routes->add('example_route', '/example')->controller(ExampleController::class);
66+
});
67+
}])->getCacheDir(),
5668
];
5769

58-
self::assertSame($cacheDirs, array_unique($cacheDirs));
59-
self::assertNotContains(self::bootKernel()->getCacheDir(), $cacheDirs);
70+
self::assertNotContains(self::bootKernel()->getCacheDir(), $cacheDirs, 'Every cache dir should be dynamic.');
71+
self::assertSame($cacheDirs, array_unique($cacheDirs), 'Every cache dir should be unique.');
6072
}
6173
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Neusta\Pimcore\TestingFramework\Tests\Functional;
5+
6+
use Neusta\Pimcore\TestingFramework\Kernel\TestKernel;
7+
use Neusta\Pimcore\TestingFramework\Test\Attribute\ConfigureRoute;
8+
use Neusta\Pimcore\TestingFramework\Test\ConfigurableKernelTestCase;
9+
use Neusta\Pimcore\TestingFramework\Tests\Fixtures\Controller\ExampleController;
10+
use Symfony\Component\DependencyInjection\ContainerInterface;
11+
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
12+
use Symfony\Component\Routing\Route;
13+
14+
final class RouteConfigurationTest extends ConfigurableKernelTestCase
15+
{
16+
public function provideDifferentConfigurationFormats(): iterable
17+
{
18+
yield 'YAML' => [__DIR__ . '/../Fixtures/Resources/Routes/routes.yaml'];
19+
yield 'XML' => [__DIR__ . '/../Fixtures/Resources/Routes/routes.xml'];
20+
yield 'PHP' => [__DIR__ . '/../Fixtures/Resources/Routes/routes.php'];
21+
yield 'Callable' => [function (RoutingConfigurator $routes): void {
22+
$routes->add('example_route', '/example')->controller(ExampleController::class);
23+
}];
24+
}
25+
26+
/**
27+
* @test
28+
*
29+
* @dataProvider provideDifferentConfigurationFormats
30+
*/
31+
public function different_configuration_formats(string|callable $config): void
32+
{
33+
self::bootKernel(['config' => fn (TestKernel $kernel) => $kernel->addTestRoute($config)]);
34+
35+
self::assertRouteConfiguration(self::getContainer());
36+
}
37+
38+
public function provideDifferentConfigurationFormatsViaKernelConfigurationObject(): iterable
39+
{
40+
yield 'YAML' => [new ConfigureRoute(__DIR__ . '/../Fixtures/Resources/Routes/routes.yaml')];
41+
yield 'XML' => [new ConfigureRoute(__DIR__ . '/../Fixtures/Resources/Routes/routes.xml')];
42+
yield 'PHP' => [new ConfigureRoute(__DIR__ . '/../Fixtures/Resources/Routes/routes.php')];
43+
yield 'Callable' => [new ConfigureRoute(function (RoutingConfigurator $routes): void {
44+
$routes->add('example_route', '/example')->controller(ExampleController::class);
45+
})];
46+
}
47+
48+
/**
49+
* @test
50+
*
51+
* @dataProvider provideDifferentConfigurationFormatsViaKernelConfigurationObject
52+
*/
53+
public function different_configuration_formats_via_data_provider(): void
54+
{
55+
self::assertRouteConfiguration(self::getContainer());
56+
}
57+
58+
/**
59+
* @test
60+
*/
61+
#[ConfigureRoute(__DIR__ . '/../Fixtures/Resources/Routes/routes.yaml')]
62+
public function configuration_in_yaml_via_attribute(): void
63+
{
64+
self::assertRouteConfiguration(self::getContainer());
65+
}
66+
67+
/**
68+
* @test
69+
*/
70+
#[ConfigureRoute(__DIR__ . '/../Fixtures/Resources/Routes/routes.xml')]
71+
public function configuration_in_xml_via_attribute(): void
72+
{
73+
self::assertRouteConfiguration(self::getContainer());
74+
}
75+
76+
/**
77+
* @test
78+
*/
79+
#[ConfigureRoute(__DIR__ . '/../Fixtures/Resources/Routes/routes.php')]
80+
public function configuration_in_php_via_attribute(): void
81+
{
82+
self::assertRouteConfiguration(self::getContainer());
83+
}
84+
85+
public static function assertRouteConfiguration(ContainerInterface $container): void
86+
{
87+
$route = $container->get('router')->getRouteCollection()->get('example_route');
88+
89+
self::assertInstanceOf(Route::class, $route);
90+
self::assertSame('/example', $route->getPath());
91+
}
92+
}

0 commit comments

Comments
 (0)