From 38fbe40902a8543d7166a20b9ddec4fdc5b9cd1f Mon Sep 17 00:00:00 2001 From: Mathieu Santostefano Date: Tue, 19 Sep 2023 17:45:00 +0200 Subject: [PATCH 1/2] Add LoggerAssertionsTrait which provide shortcuts to assert a log has been written --- .../Bundle/FrameworkBundle/CHANGELOG.md | 1 + .../FrameworkBundle/Test/KernelTestCase.php | 1 + .../Test/LoggerAssertionsTrait.php | 48 +++++++++++++++++++ .../Controller/LoggerController.php | 27 +++++++++++ .../Bundle/TestBundle/Logger/TestLogger.php | 33 +++++++++++++ .../TestBundle/Resources/config/routing.yml | 4 ++ .../Tests/Functional/LoggerTest.php | 25 ++++++++++ .../Tests/Functional/app/Logger/bundles.php | 18 +++++++ .../Tests/Functional/app/Logger/config.yml | 6 +++ .../Tests/Functional/app/Logger/routing.yml | 2 + .../Tests/Functional/app/Logger/services.yml | 13 +++++ 11 files changed, 178 insertions(+) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Test/LoggerAssertionsTrait.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/LoggerController.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Logger/TestLogger.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/LoggerTest.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/bundles.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/config.yml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/routing.yml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/services.yml diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index cf66d7ef1b113..9b14be1b5c725 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 6.4 --- + * Add `LoggerAssertionsTrait` * Add `AbstractController::renderBlock()` and `renderBlockView()` * Add native return type to `Translator` and to `Application::reset()` * Deprecate the integration of Doctrine annotations, either uninstall the `doctrine/annotations` package or disable the integration by setting `framework.annotations` to `false` diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php index 8d27b757f14f1..1240d1ad1152d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php @@ -25,6 +25,7 @@ */ abstract class KernelTestCase extends TestCase { + use LoggerAssertionsTrait; use MailerAssertionsTrait; use NotificationAssertionsTrait; diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/LoggerAssertionsTrait.php b/src/Symfony/Bundle/FrameworkBundle/Test/LoggerAssertionsTrait.php new file mode 100644 index 0000000000000..72b1a3c5ba893 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Test/LoggerAssertionsTrait.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Test; + +use Monolog\Handler\TestHandler; +use Monolog\Logger; +use Monolog\LogRecord; + +trait LoggerAssertionsTrait +{ + public static function assertLogExists(string $expectedLog, string $level = Logger::DEBUG): void + { + /** @var TestHandler $logger */ + $logger = self::getContainer()->get('monolog.handler.test'); + + self::assertTrue($logger->hasRecordThatPasses( + function (array|LogRecord $record) use ($expectedLog) { + return $record['message'] === $expectedLog; + }, + $level, + )); + } + + public static function assertLogMatches(string $expectedRegex, string $level = Logger::DEBUG): void + { + /** @var TestHandler $logger */ + $logger = self::getContainer()->get('monolog.handler.test'); + + self::assertTrue($logger->hasRecordThatMatches($expectedRegex, $level)); + } + + public static function assertLogContains(string $expectedLog, string $level = Logger::DEBUG): void + { + /** @var TestHandler $logger */ + $logger = self::getContainer()->get('monolog.handler.test'); + + self::assertTrue($logger->hasRecordThatContains($expectedLog, $level)); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/LoggerController.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/LoggerController.php new file mode 100644 index 0000000000000..3694ed77b394f --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/LoggerController.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller; + +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpFoundation\Response; + +final class LoggerController +{ + public function index(LoggerInterface $logger) + { + $logger->debug('test1_'.__CLASS__); + $logger->debug('test2_'.__CLASS__); + $logger->debug('test3_'.__CLASS__); + + return new Response(); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Logger/TestLogger.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Logger/TestLogger.php new file mode 100644 index 0000000000000..e0527cc04cb83 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Logger/TestLogger.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 Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Logger; + +use Monolog\Logger; + +class TestLogger extends Logger +{ + public array $logs = []; + + public function __construct($handler) + { + parent::__construct(__CLASS__, [$handler]); + } + + public function log($level, $message, array $context = []): void + { + $this->logs[] = [ + 'level' => $level, + 'message' => (string) $message, + 'context' => $context, + ]; + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml index 5630ed621048f..8907b65102565 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml @@ -68,3 +68,7 @@ uid: send_notification: path: /send_notification defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\NotificationController::indexAction } + +log: + path: /log + defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\LoggerController::index } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/LoggerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/LoggerTest.php new file mode 100644 index 0000000000000..562cff068eeba --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/LoggerTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +final class LoggerTest extends AbstractWebTestCase +{ + public function testLoggerAssertion() + { + $client = $this->createClient(['test_case' => 'Logger', 'root_config' => 'config.yml', 'debug' => true]); + $client->request('GET', '/log'); + + $this->assertLogExists('test1_Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\LoggerController'); + $this->assertLogMatches('/(test2_).*(LoggerController)/'); + $this->assertLogContains('test3'); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/bundles.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/bundles.php new file mode 100644 index 0000000000000..15ff182c6fed5 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return [ + new FrameworkBundle(), + new TestBundle(), +]; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/config.yml new file mode 100644 index 0000000000000..c91beb5873782 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/config.yml @@ -0,0 +1,6 @@ +imports: + - { resource: ../config/default.yml } + - { resource: services.yml } + +framework: + profiler: ~ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/routing.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/routing.yml new file mode 100644 index 0000000000000..8d3bcd42ec8d5 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/routing.yml @@ -0,0 +1,2 @@ +_loggertest_bundle: + resource: '@TestBundle/Resources/config/routing.yml' diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/services.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/services.yml new file mode 100644 index 0000000000000..aea1942fb5572 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Logger/services.yml @@ -0,0 +1,13 @@ +services: + _defaults: + public: true + + Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\LoggerController: + tags: ['controller.service_arguments'] + + monolog.handler.test: + class: Monolog\Handler\TestHandler + + logger: + class: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Logger\TestLogger + arguments: ['@monolog.handler.test'] From 34c2d5a6585127957891831bb93755f31b651c35 Mon Sep 17 00:00:00 2001 From: Mathieu Santostefano Date: Thu, 21 Sep 2023 12:05:49 +0200 Subject: [PATCH 2/2] Ensure monolog.handler.test is registered --- .../Test/LoggerAssertionsTrait.php | 16 +++++++++ .../Logger/TestLoggerWithoutHandler.php | 33 +++++++++++++++++++ .../Tests/Functional/LoggerTest.php | 26 +++++++++++++++ .../app/LoggerWithoutHandler/bundles.php | 18 ++++++++++ .../app/LoggerWithoutHandler/config.yml | 6 ++++ .../app/LoggerWithoutHandler/routing.yml | 2 ++ .../app/LoggerWithoutHandler/services.yml | 9 +++++ 7 files changed, 110 insertions(+) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Logger/TestLoggerWithoutHandler.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/bundles.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/config.yml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/routing.yml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/services.yml diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/LoggerAssertionsTrait.php b/src/Symfony/Bundle/FrameworkBundle/Test/LoggerAssertionsTrait.php index 72b1a3c5ba893..8d6dd579bbeda 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/LoggerAssertionsTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/LoggerAssertionsTrait.php @@ -19,6 +19,8 @@ trait LoggerAssertionsTrait { public static function assertLogExists(string $expectedLog, string $level = Logger::DEBUG): void { + self::ensureMonologHandlerIsAvailable(); + /** @var TestHandler $logger */ $logger = self::getContainer()->get('monolog.handler.test'); @@ -32,6 +34,8 @@ function (array|LogRecord $record) use ($expectedLog) { public static function assertLogMatches(string $expectedRegex, string $level = Logger::DEBUG): void { + self::ensureMonologHandlerIsAvailable(); + /** @var TestHandler $logger */ $logger = self::getContainer()->get('monolog.handler.test'); @@ -40,9 +44,21 @@ public static function assertLogMatches(string $expectedRegex, string $level = L public static function assertLogContains(string $expectedLog, string $level = Logger::DEBUG): void { + self::ensureMonologHandlerIsAvailable(); + /** @var TestHandler $logger */ $logger = self::getContainer()->get('monolog.handler.test'); self::assertTrue($logger->hasRecordThatContains($expectedLog, $level)); } + + /** + * @internal + */ + private static function ensureMonologHandlerIsAvailable(): void + { + if (!self::getContainer()->has('monolog.handler.test')) { + self::fail('The "monolog.handler.test" service is not available. Try registering the service "Monolog\Handler\TestHandler" as "monolog.handler.test" in your test configuration.'); + } + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Logger/TestLoggerWithoutHandler.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Logger/TestLoggerWithoutHandler.php new file mode 100644 index 0000000000000..18d4371c2bedb --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Logger/TestLoggerWithoutHandler.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 Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Logger; + +use Monolog\Logger; + +class TestLoggerWithoutHandler extends Logger +{ + public array $logs = []; + + public function __construct() + { + parent::__construct(__CLASS__); + } + + public function log($level, $message, array $context = []): void + { + $this->logs[] = [ + 'level' => $level, + 'message' => (string) $message, + 'context' => $context, + ]; + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/LoggerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/LoggerTest.php index 562cff068eeba..fb9a52d86d074 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/LoggerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/LoggerTest.php @@ -11,6 +11,8 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; +use PHPUnit\Framework\AssertionFailedError; + final class LoggerTest extends AbstractWebTestCase { public function testLoggerAssertion() @@ -22,4 +24,28 @@ public function testLoggerAssertion() $this->assertLogMatches('/(test2_).*(LoggerController)/'); $this->assertLogContains('test3'); } + + public function testLoggerAssertionWithoutTestHandler() + { + $client = $this->createClient(['test_case' => 'LoggerWithoutHandler', 'root_config' => 'config.yml', 'debug' => true]); + $client->request('GET', '/log'); + + try { + $this->assertLogExists('test1_Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\LoggerController'); + } catch (AssertionFailedError $e) { + $this->assertSame('The "monolog.handler.test" service is not available. Try registering the service "Monolog\Handler\TestHandler" as "monolog.handler.test" in your test configuration.', $e->getMessage()); + } + + try { + $this->assertLogMatches('/(test2_).*(LoggerController)/'); + } catch (AssertionFailedError $e) { + $this->assertSame('The "monolog.handler.test" service is not available. Try registering the service "Monolog\Handler\TestHandler" as "monolog.handler.test" in your test configuration.', $e->getMessage()); + } + + try { + $this->assertLogContains('test3'); + } catch (AssertionFailedError $e) { + $this->assertSame('The "monolog.handler.test" service is not available. Try registering the service "Monolog\Handler\TestHandler" as "monolog.handler.test" in your test configuration.', $e->getMessage()); + } + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/bundles.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/bundles.php new file mode 100644 index 0000000000000..15ff182c6fed5 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return [ + new FrameworkBundle(), + new TestBundle(), +]; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/config.yml new file mode 100644 index 0000000000000..c91beb5873782 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/config.yml @@ -0,0 +1,6 @@ +imports: + - { resource: ../config/default.yml } + - { resource: services.yml } + +framework: + profiler: ~ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/routing.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/routing.yml new file mode 100644 index 0000000000000..8d3bcd42ec8d5 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/routing.yml @@ -0,0 +1,2 @@ +_loggertest_bundle: + resource: '@TestBundle/Resources/config/routing.yml' diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/services.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/services.yml new file mode 100644 index 0000000000000..7484719dbb5fe --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/LoggerWithoutHandler/services.yml @@ -0,0 +1,9 @@ +services: + _defaults: + public: true + + Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\LoggerController: + tags: ['controller.service_arguments'] + + logger: + class: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Logger\TestLoggerWithoutHandler