From 01759238cf3fee11b62972c203ed0e49e9f047c8 Mon Sep 17 00:00:00 2001 From: Fran Moreno Date: Tue, 25 Jan 2022 08:55:30 +0100 Subject: [PATCH] Add functional test and fix container usage (#472) * Add functional test * Fix action service id * Bump doctrine-bundle * Deprecate instantiating TokenStorageUsernameCallable with Container --- .github/workflows/test.yaml | 4 - composer.json | 9 +- src/Resources/config/actions.php | 2 +- src/Resources/config/auditable.php | 2 +- src/User/TokenStorageUsernameCallable.php | 45 +++++++-- tests/App/AppKernel.php | 92 +++++++++++++++++++ tests/App/DataFixtures/UserFixture.php | 33 +++++++ tests/App/Entity/User.php | 59 ++++++++++++ tests/App/config/config.yml | 28 ++++++ tests/App/config/config_symfony_v4.yml | 8 ++ tests/App/config/config_symfony_v5.yml | 11 +++ tests/App/config/routes.yml | 3 + tests/App/config/services.php | 22 +++++ .../SimpleThingsEntityAuditExtensionTest.php | 2 +- tests/Functional/SmokeTest.php | 51 ++++++++++ tests/custom_bootstrap.php | 43 +++++++++ 16 files changed, 395 insertions(+), 19 deletions(-) create mode 100644 tests/App/AppKernel.php create mode 100644 tests/App/DataFixtures/UserFixture.php create mode 100644 tests/App/Entity/User.php create mode 100644 tests/App/config/config.yml create mode 100644 tests/App/config/config_symfony_v4.yml create mode 100644 tests/App/config/config_symfony_v5.yml create mode 100644 tests/App/config/routes.yml create mode 100644 tests/App/config/services.php create mode 100644 tests/Functional/SmokeTest.php create mode 100644 tests/custom_bootstrap.php diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e39474e1..8409ecf5 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -40,10 +40,6 @@ jobs: dependencies: lowest allowed-to-fail: false variant: normal - - php-version: '8.1' - dependencies: highest - allowed-to-fail: false - variant: symfony/framework-bundle:"4.4.*" - php-version: '8.1' dependencies: highest allowed-to-fail: false diff --git a/composer.json b/composer.json index dd4747dc..7351f52d 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,8 @@ "twig/twig": "^2.6 || ^3.0" }, "require-dev": { - "doctrine/doctrine-bundle": "^1.4 || ^2.2", + "doctrine/doctrine-bundle": "^1.12.8 || ^2.3.2", + "doctrine/doctrine-fixtures-bundle": "^3.4", "gedmo/doctrine-extensions": "^3.0", "matthiasnoback/symfony-dependency-injection-test": "^4.2.1", "phpstan/extension-installer": "^1.1", @@ -34,15 +35,19 @@ "phpunit/phpunit": "^9.5", "psalm/plugin-phpunit": "^0.16.1", "psalm/plugin-symfony": "^3.0", + "symfony/browser-kit": "^4.4 || ^5.3 || ^6.0", "symfony/cache": "^4.4 || ^5.3 || ^6.0", + "symfony/framework-bundle": "^4.4 || ^5.3 || ^6.0", "symfony/http-foundation": "^4.4 || ^5.3 || ^6.0", "symfony/phpunit-bridge": "^6.0", + "symfony/security-bundle": "^4.4 || ^5.3 || ^6.0", + "symfony/twig-bundle": "^4.4 || ^5.3 || ^6.0", "symfony/var-dumper": "^4.4 || ^5.3 || ^6.0", "vimeo/psalm": "^4.8", "weirdan/doctrine-psalm-plugin": "^2.0" }, "conflict": { - "doctrine/doctrine-bundle": "<1.4", + "doctrine/doctrine-bundle": "<1.12.8", "gedmo/doctrine-extensions": "<3.0", "symfony/framework-bundle": "<4.4" }, diff --git a/src/Resources/config/actions.php b/src/Resources/config/actions.php index 3d257650..34928b29 100644 --- a/src/Resources/config/actions.php +++ b/src/Resources/config/actions.php @@ -44,7 +44,7 @@ new ReferenceConfigurator('simplethings_entityaudit.reader'), ]) - ->set(ViewDetailAction::class, ViewEntityAction::class) + ->set(ViewEntityAction::class, ViewEntityAction::class) ->public() ->args([ new ReferenceConfigurator('twig'), diff --git a/src/Resources/config/auditable.php b/src/Resources/config/auditable.php index d301aef7..97ff1fd5 100644 --- a/src/Resources/config/auditable.php +++ b/src/Resources/config/auditable.php @@ -63,7 +63,7 @@ ->args([new ReferenceConfigurator('simplethings_entityaudit.manager')]) ->set('simplethings_entityaudit.username_callable.token_storage', TokenStorageUsernameCallable::class) - ->args([new ReferenceConfigurator('service_container')]) + ->args([new ReferenceConfigurator('security.token_storage')]) ->set('simplethings_entityaudit.config', AuditConfiguration::class) ->public() diff --git a/src/User/TokenStorageUsernameCallable.php b/src/User/TokenStorageUsernameCallable.php index b00bb883..775aae43 100644 --- a/src/User/TokenStorageUsernameCallable.php +++ b/src/User/TokenStorageUsernameCallable.php @@ -14,20 +14,45 @@ namespace SimpleThings\EntityAudit\User; use Symfony\Component\DependencyInjection\Container; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; class TokenStorageUsernameCallable { /** - * NEXT_MAJOR: Inject the required services instead of using the container. - * - * @var Container + * @var TokenStorageInterface */ - private $container; + private $tokenStorage; - public function __construct(Container $container) + /** + * NEXT_MAJOR: remove Container type. + * + * @param Container|TokenStorageInterface $tokenStorageOrContainer + */ + public function __construct(object $tokenStorageOrContainer) { - $this->container = $container; + if ($tokenStorageOrContainer instanceof TokenStorageInterface) { + $this->tokenStorage = $tokenStorageOrContainer; + } elseif ($tokenStorageOrContainer instanceof Container) { + @trigger_error(sprintf( + 'Passing as argument 1 an instance of "%s" to "%s" is deprecated since' + .' sonata-project/entity-audit-bundle 1.x and will throw an "%s" in version 2.0.' + .' You must pass an instance of "%s" instead.', + Container::class, + __METHOD__, + \TypeError::class, + TokenStorageInterface::class + ), \E_USER_DEPRECATED); + + $this->tokenStorage = $tokenStorageOrContainer->get('security.token_storage'); + } else { + throw new \TypeError(sprintf( + 'Argument 1 passed to "%s()" must be an instance of "%s" or %s, instance of "%s" given.', + __METHOD__, + TokenStorageInterface::class, + Container::class, + \get_class($tokenStorageOrContainer) + )); + } } /** @@ -37,10 +62,10 @@ public function __construct(Container $container) */ public function __invoke() { - /** @var TokenInterface $token */ - $token = $this->container->get('security.token_storage')->getToken(); + $token = $this->tokenStorage->getToken(); + if (null !== $token && null !== $token->getUser()) { - // @phpstan-ignore-next-line + // @phpstan-ignore-next-line Use only "getUserIdentifier" when dropping support of Symfony < 5.3 return method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(); } diff --git a/tests/App/AppKernel.php b/tests/App/AppKernel.php new file mode 100644 index 00000000..de8ca3bc --- /dev/null +++ b/tests/App/AppKernel.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SimpleThings\EntityAudit\Tests\App; + +use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; +use Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle; +use SimpleThings\EntityAudit\SimpleThingsEntityAuditBundle; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; +use Symfony\Bundle\SecurityBundle\SecurityBundle; +use Symfony\Bundle\TwigBundle\TwigBundle; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpFoundation\InputBag; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + +final class AppKernel extends Kernel +{ + use MicroKernelTrait; + + public function __construct() + { + parent::__construct('test', false); + } + + public function registerBundles(): iterable + { + return [ + new FrameworkBundle(), + new TwigBundle(), + new DoctrineBundle(), + new SecurityBundle(), + new DoctrineFixturesBundle(), + new SimpleThingsEntityAuditBundle(), + ]; + } + + public function getCacheDir(): string + { + return sprintf('%scache', $this->getBaseDir()); + } + + public function getLogDir(): string + { + return sprintf('%slog', $this->getBaseDir()); + } + + public function getProjectDir(): string + { + return __DIR__; + } + + /** + * TODO: add typehint when support for Symfony < 5.1 is dropped. + * + * @param RoutingConfigurator $routes + */ + protected function configureRoutes($routes): void + { + $routes->import(sprintf('%s/config/routes.yml', $this->getProjectDir())); + } + + protected function configureContainer(ContainerBuilder $containerBuilder, LoaderInterface $loader): void + { + $loader->load(__DIR__.'/config/config.yml'); + + if (class_exists(InputBag::class)) { + $loader->load(__DIR__.'/config/config_symfony_v5.yml'); + } else { + $loader->load(__DIR__.'/config/config_symfony_v4.yml'); + } + + $loader->load(__DIR__.'/config/services.php'); + } + + private function getBaseDir(): string + { + return sprintf('%s/entity-audit-bundle/var/', sys_get_temp_dir()); + } +} diff --git a/tests/App/DataFixtures/UserFixture.php b/tests/App/DataFixtures/UserFixture.php new file mode 100644 index 00000000..d030dd7c --- /dev/null +++ b/tests/App/DataFixtures/UserFixture.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SimpleThings\EntityAudit\Tests\App\DataFixtures; + +use Doctrine\Bundle\FixturesBundle\Fixture; +use Doctrine\Persistence\ObjectManager; +use SimpleThings\EntityAudit\Tests\App\Entity\User; + +final class UserFixture extends Fixture +{ + public function load(ObjectManager $manager): void + { + $user = new User('bob'); + + $manager->persist($user); + $manager->flush(); + + $user->setName('alice'); + + $manager->flush(); + } +} diff --git a/tests/App/Entity/User.php b/tests/App/Entity/User.php new file mode 100644 index 00000000..ad660f84 --- /dev/null +++ b/tests/App/Entity/User.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SimpleThings\EntityAudit\Tests\App\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + * @ORM\Table(name="bundle_user") + */ +class User +{ + /** + * @var int|null + * + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue + */ + protected $id; + + /** + * @var string + * + * @ORM\Column(type="string") + */ + private $name; + + public function __construct(string $name) + { + $this->name = $name; + } + + public function setName(string $name): void + { + $this->name = $name; + } + + public function getId(): ?int + { + return $this->id; + } + + public function getName(): string + { + return $this->name; + } +} diff --git a/tests/App/config/config.yml b/tests/App/config/config.yml new file mode 100644 index 00000000..ade215e4 --- /dev/null +++ b/tests/App/config/config.yml @@ -0,0 +1,28 @@ +framework: + secret: '%env(APP_SECRET)%' + test: true + +twig: + exception_controller: null + strict_variables: false + +parameters: + env(DATABASE_URL): 'sqlite:////%kernel.cache_dir%/test_database.db' + +doctrine: + dbal: + url: "%env(resolve:DATABASE_URL)%" + orm: + auto_generate_proxy_classes: true + auto_mapping: true + mappings: + AuditEntityTest: + type: annotation + dir: "%kernel.project_dir%/Entity" + is_bundle: false + prefix: SimpleThings\EntityAudit\Tests\App\Entity + +simple_things_entity_audit: + revision_table_name: bundle_revisions + audited_entities: + - SimpleThings\EntityAudit\Tests\App\Entity\User diff --git a/tests/App/config/config_symfony_v4.yml b/tests/App/config/config_symfony_v4.yml new file mode 100644 index 00000000..0aa5ab7e --- /dev/null +++ b/tests/App/config/config_symfony_v4.yml @@ -0,0 +1,8 @@ +framework: + session: + storage_id: session.storage.mock_file + +security: + firewalls: + main: + anonymous: true diff --git a/tests/App/config/config_symfony_v5.yml b/tests/App/config/config_symfony_v5.yml new file mode 100644 index 00000000..595440c5 --- /dev/null +++ b/tests/App/config/config_symfony_v5.yml @@ -0,0 +1,11 @@ +framework: + session: + storage_factory_id: session.storage.factory.mock_file + router: + utf8: true + +security: + enable_authenticator_manager: true + firewalls: + main: + lazy: true diff --git a/tests/App/config/routes.yml b/tests/App/config/routes.yml new file mode 100644 index 00000000..c396154d --- /dev/null +++ b/tests/App/config/routes.yml @@ -0,0 +1,3 @@ +simple_things_entity_audit: + resource: "@SimpleThingsEntityAuditBundle/Resources/config/routing/audit.xml" + prefix: /audit diff --git a/tests/App/config/services.php b/tests/App/config/services.php new file mode 100644 index 00000000..7cee5189 --- /dev/null +++ b/tests/App/config/services.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; + +return static function (ContainerConfigurator $containerConfigurator): void { + $containerConfigurator->services() + ->defaults() + ->autowire() + ->autoconfigure() + ->load('SimpleThings\\EntityAudit\\Tests\\App\\DataFixtures\\', dirname(__DIR__).'/DataFixtures'); +}; diff --git a/tests/DependencyInjection/SimpleThingsEntityAuditExtensionTest.php b/tests/DependencyInjection/SimpleThingsEntityAuditExtensionTest.php index 416e583f..eeb01d2a 100644 --- a/tests/DependencyInjection/SimpleThingsEntityAuditExtensionTest.php +++ b/tests/DependencyInjection/SimpleThingsEntityAuditExtensionTest.php @@ -38,7 +38,7 @@ public function testItRegistersDefaultServices(): void $this->assertContainerBuilderHasServiceDefinitionWithTag('simplethings_entityaudit.create_schema_listener', 'doctrine.event_subscriber', ['connection' => 'default']); $this->assertContainerBuilderHasService('simplethings_entityaudit.username_callable.token_storage', 'SimpleThings\EntityAudit\User\TokenStorageUsernameCallable'); - $this->assertContainerBuilderHasServiceDefinitionWithArgument('simplethings_entityaudit.username_callable.token_storage', 0, 'service_container'); + $this->assertContainerBuilderHasServiceDefinitionWithArgument('simplethings_entityaudit.username_callable.token_storage', 0, 'security.token_storage'); $this->assertContainerBuilderHasService('simplethings_entityaudit.config', 'SimpleThings\EntityAudit\AuditConfiguration'); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('simplethings_entityaudit.config', 'setAuditedEntityClasses', ['%simplethings.entityaudit.audited_entities%']); diff --git a/tests/Functional/SmokeTest.php b/tests/Functional/SmokeTest.php new file mode 100644 index 00000000..b65bcaa9 --- /dev/null +++ b/tests/Functional/SmokeTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SimpleThings\EntityAudit\Tests\Functional; + +use SimpleThings\EntityAudit\Tests\App\AppKernel; +use SimpleThings\EntityAudit\Tests\App\Entity\User; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +final class SmokeTest extends WebTestCase +{ + /** + * @dataProvider provideUrls + */ + public function testSuccessfulResponses(string $url): void + { + $client = self::createClient(); + $client->request(Request::METHOD_GET, $url); + + static::assertSame(Response::HTTP_OK, $client->getResponse()->getStatusCode()); + } + + public function provideUrls(): iterable + { + yield 'index' => ['/audit']; + yield 'view revision' => ['/audit/viewrev/1']; + + $encodeUserClass = urlencode(User::class); + + yield 'view detail' => [sprintf('/audit/viewent/%s/1', $encodeUserClass)]; + yield 'view entity' => [sprintf('/audit/viewent/%s/1/1', $encodeUserClass)]; + yield 'compare' => [sprintf('/audit/compare/%s/1?newRev=2&oldRev=1', $encodeUserClass)]; + } + + protected static function getKernelClass(): string + { + return AppKernel::class; + } +} diff --git a/tests/custom_bootstrap.php b/tests/custom_bootstrap.php new file mode 100644 index 00000000..4cefeeab --- /dev/null +++ b/tests/custom_bootstrap.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use SimpleThings\EntityAudit\Tests\App\AppKernel; +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\NullOutput; + +$application = new Application(new AppKernel()); +$application->setAutoExit(false); + +$input = new ArrayInput([ + 'command' => 'doctrine:database:drop', + '--force' => true, +]); +$application->run($input, new NullOutput()); + +$input = new ArrayInput([ + 'command' => 'doctrine:database:create', + '--no-interaction' => true, +]); +$application->run($input, new NullOutput()); + +$input = new ArrayInput([ + 'command' => 'doctrine:schema:create', +]); +$application->run($input, new NullOutput()); + +$input = new ArrayInput([ + 'command' => 'doctrine:fixtures:load', + '--no-interaction' => true, +]); +$application->run($input, new NullOutput());