From 6ddbd70b5d7628627782038e8c584b894dbe52fa Mon Sep 17 00:00:00 2001 From: Frank Dekker Date: Sat, 20 Jan 2024 17:46:17 +0100 Subject: [PATCH] Add functional tests (#6) --- .github/workflows/test.yml | 7 +- .gitignore | 7 +- composer.json | 6 +- dev/config/config.yml | 1 + docs/configuration.md | 6 ++ phpunit.xml.dist | 6 ++ src/Controller/IndexController.php | 14 ++- src/DependencyInjection/Configuration.php | 5 ++ src/DependencyInjection/Extension.php | 52 +++++------ src/Entity/TempFile.php | 6 +- src/Iterator/MaxRuntimeIterator.php | 13 ++- src/Resources/config/services.php | 8 +- src/Resources/views/index.html.twig | 6 +- src/Service/File/LogParser.php | 5 +- src/Service/JsonManifestAssetLoader.php | 41 +++++++++ src/Service/JsonManifestVersionStrategy.php | 42 --------- src/Util/Clock.php | 15 ++++ .../Functional/AbstractFunctionalTestCase.php | 59 +++++++++++++ tests/Functional/DeleteFileTest.php | 31 +++++++ tests/Functional/DeleteFolderTest.php | 31 +++++++ tests/Functional/DownloadFileTest.php | 18 ++++ tests/Functional/DownloadFolderTest.php | 22 +++++ tests/Functional/FoldersTest.php | 20 +++++ tests/Functional/IndexTest.php | 29 ++++++ tests/Functional/LogRecordsTest.php | 22 +++++ .../Service/File/LogParserTest.php | 6 +- .../Controller/DeleteFileControllerTest.php | 2 +- .../Controller/DeleteFolderControllerTest.php | 2 +- .../Controller/DownloadFileControllerTest.php | 2 +- .../DownloadFolderControllerTest.php | 2 +- tests/Unit/Controller/IndexControllerTest.php | 20 ++++- .../Controller/LogRecordsControllerTest.php | 2 +- tests/Unit/Entity/LogFolderCollectionTest.php | 2 +- .../Unit/Entity/Output/LogFileOutputTest.php | 1 - tests/Unit/Entity/TempFileTest.php | 2 +- .../Unit/Iterator/MaxRuntimeIteratorTest.php | 42 ++++----- .../Unit/Service/File/LogFileServiceTest.php | 2 +- .../File/LogRecordsOutputProviderTest.php | 4 +- .../File/Monolog/MonologFileParserTest.php | 2 +- .../Service/Folder/LogFolderFactoryTest.php | 1 - .../Folder/LogFolderOutputFactoryTest.php | 2 +- .../Service/Folder/ZipArchiveFactoryTest.php | 2 +- .../Service/JsonManifestAssetLoaderTest.php | 68 ++++++++++++++ .../JsonManifestVersionStrategyTest.php | 43 --------- tests/Unit/Util/ClockTest.php | 18 ++++ tests/{ => Utility}/TestEntityTrait.php | 2 +- tests/Utility/TestKernel.php | 88 +++++++++++++++++++ tests/resources/Functional/log/test.log | 3 + tests/resources/Functional/routes.php | 9 ++ 49 files changed, 614 insertions(+), 185 deletions(-) create mode 100644 src/Service/JsonManifestAssetLoader.php delete mode 100644 src/Service/JsonManifestVersionStrategy.php create mode 100644 src/Util/Clock.php create mode 100644 tests/Functional/AbstractFunctionalTestCase.php create mode 100644 tests/Functional/DeleteFileTest.php create mode 100644 tests/Functional/DeleteFolderTest.php create mode 100644 tests/Functional/DownloadFileTest.php create mode 100644 tests/Functional/DownloadFolderTest.php create mode 100644 tests/Functional/FoldersTest.php create mode 100644 tests/Functional/IndexTest.php create mode 100644 tests/Functional/LogRecordsTest.php create mode 100644 tests/Unit/Service/JsonManifestAssetLoaderTest.php delete mode 100644 tests/Unit/Service/JsonManifestVersionStrategyTest.php create mode 100644 tests/Unit/Util/ClockTest.php rename tests/{ => Utility}/TestEntityTrait.php (98%) create mode 100644 tests/Utility/TestKernel.php create mode 100644 tests/resources/Functional/log/test.log create mode 100644 tests/resources/Functional/routes.php diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 76f6afff..db3ae6a9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,9 +17,6 @@ jobs: matrix: php-versions: [ '8.1', '8.2', '8.3' ] composer-flags: [ '', '--prefer-lowest' ] - exclude: - - php-versions: 8.3 - composer-flags: '' steps: - uses: actions/checkout@v4 @@ -33,7 +30,7 @@ jobs: run: composer validate - name: Install dependencies - run: composer update --prefer-dist --no-progress --no-suggest --prefer-stable ${{ matrix.composer-flags }} + run: composer update --prefer-dist --no-progress --prefer-stable ${{ matrix.composer-flags }} - name: Run test suite run: composer test @@ -51,7 +48,7 @@ jobs: coverage: pcov - name: Install dependencies - run: composer update --prefer-dist --no-progress --no-suggest --prefer-stable + run: composer update --prefer-dist --no-progress --prefer-stable - name: Run test suite run: php -dpcov.enabled=1 -dpcov.exclude="~vendor~" vendor/bin/phpunit --testsuite unit,integration --coverage-clover ./.coverage/coverage.xml diff --git a/.gitignore b/.gitignore index ffd96567..bcdb0386 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ /.idea -/vendor -/composer.lock -/phpunit.xml /.phpunit.result.cache +/composer.lock /dev/public/bundles /dev/var +/phpunit.xml +/tests/.kernel +/vendor diff --git a/composer.json b/composer.json index 489f97b4..e6db1587 100644 --- a/composer.json +++ b/composer.json @@ -28,12 +28,12 @@ "require": { "php": ">=8.1", "ext-json": "*", - "symfony/asset": "^6.0||^7.0", + "psr/clock": "^1.0", "symfony/config": "^6.0||^7.0", "symfony/dependency-injection": "^6.0||^7.0", + "symfony/filesystem": "^6.0||^7.0", "symfony/finder": "^6.0||^7.0", "symfony/framework-bundle": "^6.0||^7.0", - "symfony/filesystem": "^6.0||^7.0", "symfony/http-foundation": "^6.0||^7.0", "symfony/http-kernel": "^6.0||^7.0", "symfony/routing": "^6.0||^7.0", @@ -58,7 +58,7 @@ "symfony/css-selector": "^6.0||^7.0", "symfony/monolog-bridge": "^6.0||^7.0", "symfony/monolog-bundle": "^3.10", - "symfony/phpunit-bridge": "^6.0||^7.0", + "symfony/phpunit-bridge": "^6.4||^7.0", "symfony/runtime": "^6.0||^7.0", "symfony/templating": "^6.0||^7.0", "symfony/twig-bundle": "^6.0||^7.0", diff --git a/dev/config/config.yml b/dev/config/config.yml index a5810808..09229e58 100644 --- a/dev/config/config.yml +++ b/dev/config/config.yml @@ -24,6 +24,7 @@ web_profiler: intercept_redirects: false twig: + debug: false strict_variables: true monolog: diff --git a/docs/configuration.md b/docs/configuration.md index f2bb36ce..7b7f9f2c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -21,6 +21,7 @@ fd_log_viewer: start_of_line_pattern: '/^\[\d{4}-\d{2}-\d{2}[^]]*]\s+\S+\.\S+:/' log_message_pattern: '/^\[(?P[^\]]+)\]\s+(?P[^\.]+)\.(?P[^:]+):\s+(?P.*)\s+(?P[[{].*?[\]}])\s+(?P[[{].*?[\]}])\s+$/s' date_format: "Y-m-d H:i:s" + enable_default_monolog: true ``` ## Configuration reference @@ -112,3 +113,8 @@ If you use a custom monolog format, adjust this pattern to your needs. ### log_files.date_format `string` This is the date format that will be used to format the date in frontend. Default: `Y-m-d H:i:s` + + +### enable_default_monolog `boolean` + +Should the default monolog configuration be enabled? Default: `true` diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 6c4f90a8..a3907f2b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -11,7 +11,13 @@ beStrictAboutTodoAnnotatedTests="true" executionOrder="defects" > + + + + + tests/Functional + tests/Integration diff --git a/src/Controller/IndexController.php b/src/Controller/IndexController.php index e47debe3..9e84b5ef 100644 --- a/src/Controller/IndexController.php +++ b/src/Controller/IndexController.php @@ -6,6 +6,7 @@ use FD\LogViewer\Entity\Output\DirectionEnum; use FD\LogViewer\Routing\RouteService; use FD\LogViewer\Service\Folder\LogFolderOutputProvider; +use FD\LogViewer\Service\JsonManifestAssetLoader; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Throwable; @@ -13,6 +14,7 @@ class IndexController extends AbstractController { public function __construct( + private readonly JsonManifestAssetLoader $assetLoader, private readonly RouteService $routeService, private readonly LogFolderOutputProvider $folderOutputProvider ) { @@ -29,6 +31,16 @@ public function __invoke(): Response // retrieve all log files and folders $folders = $this->folderOutputProvider->provide(DirectionEnum::Desc); - return $this->render('@FdLogViewer/index.html.twig', ['base_uri' => $baseUri, 'folders' => $folders]); + return $this->render( + '@FdLogViewer/index.html.twig', + [ + 'base_uri' => $baseUri, + 'folders' => $folders, + 'assets' => [ + 'style' => $this->assetLoader->getUrl('style.css'), + 'js' => $this->assetLoader->getUrl('src/main.ts') + ], + ] + ); } } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 4e9e8b76..39d4086c 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -21,6 +21,10 @@ public function getConfigTreeBuilder(): TreeBuilder $rootNode = $tree->getRootNode(); $rootNode->children() + ->scalarNode('enable_default_monolog') + ->info('Enable default monolog configuration') + ->defaultTrue() + ->end() ->arrayNode('log_files') ->info('List of log files to show') ->useAttributeAsKey('log_name') @@ -62,6 +66,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->end() ->end(); + return $tree; } } diff --git a/src/DependencyInjection/Extension.php b/src/DependencyInjection/Extension.php index 56eebfbf..31f70201 100644 --- a/src/DependencyInjection/Extension.php +++ b/src/DependencyInjection/Extension.php @@ -5,11 +5,9 @@ use FD\LogViewer\Entity\Config\FinderConfig; use FD\LogViewer\Entity\Config\LogFilesConfig; -use FD\LogViewer\Service\JsonManifestVersionStrategy; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension as BaseExtension; -use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; use Symfony\Component\DependencyInjection\Reference; use Throwable; @@ -18,7 +16,7 @@ * @codeCoverageIgnore * @internal */ -final class Extension extends BaseExtension implements PrependExtensionInterface +final class Extension extends BaseExtension { /** * @inheritDoc @@ -32,19 +30,8 @@ public function load(array $configs, ContainerBuilder $container): void $mergedConfigs = $this->processConfiguration(new Configuration(), $configs); // add defaults - if (count($mergedConfigs['log_files']) === 0) { - $mergedConfigs['log_files']['monolog'] = [ - 'type' => 'monolog', - 'name' => 'Monolog', - 'finder' => [ - 'in' => '%kernel.logs_dir%', - 'name' => '*.log', - 'ignoreUnreadableDirs' => true, - 'followLinks' => false - ], - 'downloadable' => false, - 'deletable' => false, - ]; + if ($mergedConfigs['enable_default_monolog']) { + $mergedConfigs = self::addMonologDefault($mergedConfigs); } foreach ($mergedConfigs['log_files'] as $key => $config) { @@ -76,22 +63,25 @@ public function getAlias(): string } /** - * @inheritdoc + * @template T of array + * @phpstan-param T $configs + * + * @phpstan-return T */ - public function prepend(ContainerBuilder $container): void + private static function addMonologDefault(array $configs): array { - $container->prependExtensionConfig( - 'framework', - [ - 'assets' => [ - 'enabled' => true, - 'packages' => [ - 'fd_symfony_log_viewer' => [ - 'version_strategy' => JsonManifestVersionStrategy::class - ], - ], - ], - ] - ); + // monolog + $configs['log_files']['monolog']['type'] ??= 'monolog'; + $configs['log_files']['monolog']['name'] ??= 'Monolog'; + $configs['log_files']['monolog']['downloadable'] ??= false; + $configs['log_files']['monolog']['deletable'] ??= false; + + // finder + $configs['log_files']['monolog']['finder']['in'] ??= '%kernel.logs_dir%'; + $configs['log_files']['monolog']['finder']['name'] ??= '*.log'; + $configs['log_files']['monolog']['finder']['ignoreUnreadableDirs'] ??= true; + $configs['log_files']['monolog']['finder']['followLinks'] ??= false; + + return $configs; } } diff --git a/src/Entity/TempFile.php b/src/Entity/TempFile.php index d0f7461d..f913b43a 100644 --- a/src/Entity/TempFile.php +++ b/src/Entity/TempFile.php @@ -6,6 +6,7 @@ use RuntimeException; use SplFileInfo; +use function register_shutdown_function; use function sys_get_temp_dir; use function tempnam; use function unlink; @@ -21,9 +22,12 @@ public function __construct() // @codeCoverageIgnoreEnd } parent::__construct($path); + + // cleanup temp file when script ends (and not when object lifecycle ends) + register_shutdown_function(fn() => $this->destruct()); } - public function __destruct() + public function destruct(): void { if ($this->isFile()) { @unlink($this->getPathname()); diff --git a/src/Iterator/MaxRuntimeIterator.php b/src/Iterator/MaxRuntimeIterator.php index 5f28b922..be3cce48 100644 --- a/src/Iterator/MaxRuntimeIterator.php +++ b/src/Iterator/MaxRuntimeIterator.php @@ -4,6 +4,7 @@ namespace FD\LogViewer\Iterator; use IteratorAggregate; +use Psr\Clock\ClockInterface; use Traversable; /** @@ -16,8 +17,12 @@ class MaxRuntimeIterator implements IteratorAggregate /** * @param Traversable $iterator */ - public function __construct(private readonly Traversable $iterator, private readonly int $maxRuntimeInSeconds, private bool $throw = true) - { + public function __construct( + private readonly ClockInterface $clock, + private readonly Traversable $iterator, + private readonly int $maxRuntimeInSeconds, + private bool $throw = true + ) { } /** @@ -26,11 +31,11 @@ public function __construct(private readonly Traversable $iterator, private read */ public function getIterator(): Traversable { - $startTime = microtime(true); + $startTime = $this->clock->now()->getTimestamp(); foreach ($this->iterator as $key => $value) { yield $key => $value; - if (microtime(true) - $startTime > $this->maxRuntimeInSeconds) { + if ($this->clock->now()->getTimestamp() - $startTime > $this->maxRuntimeInSeconds) { if ($this->throw) { throw new MaxRuntimeException(); } diff --git a/src/Resources/config/services.php b/src/Resources/config/services.php index d96f300a..d69fa8dc 100644 --- a/src/Resources/config/services.php +++ b/src/Resources/config/services.php @@ -23,12 +23,14 @@ use FD\LogViewer\Service\Folder\LogFolderOutputProvider; use FD\LogViewer\Service\Folder\LogFolderOutputSorter; use FD\LogViewer\Service\Folder\ZipArchiveFactory; -use FD\LogViewer\Service\JsonManifestVersionStrategy; +use FD\LogViewer\Service\JsonManifestAssetLoader; use FD\LogViewer\Service\PerformanceService; use FD\LogViewer\Service\VersionService; use FD\LogViewer\StreamReader\StreamReaderFactory; +use FD\LogViewer\Util\Clock; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use function Symfony\Component\DependencyInjection\Loader\Configurator\inline_service; use function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_iterator; return static function (ContainerConfigurator $container): void { @@ -50,7 +52,7 @@ $services->set(RouteLoader::class) ->tag('routing.loader'); - $services->set(JsonManifestVersionStrategy::class) + $services->set(JsonManifestAssetLoader::class) ->arg('$manifestPath', '%kernel.project_dir%/public/bundles/fdlogviewer/.vite/manifest.json'); $services->set(FinderFactory::class); @@ -60,7 +62,7 @@ $services->set(LogFolderOutputProvider::class); $services->set(LogFolderOutputSorter::class); $services->set(LogRecordsOutputProvider::class); - $services->set(LogParser::class); + $services->set(LogParser::class)->arg('$clock', inline_service(Clock::class)); $services->set(LogFileParserProvider::class) ->arg('$logParsers', tagged_iterator('fd.symfony.log.viewer.monolog_file_parser', 'name')); $services->set(LogQueryDtoFactory::class); diff --git a/src/Resources/views/index.html.twig b/src/Resources/views/index.html.twig index 7c6135ae..3d90ca4b 100644 --- a/src/Resources/views/index.html.twig +++ b/src/Resources/views/index.html.twig @@ -4,17 +4,17 @@ + href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%23fff' viewBox='0 0 16 16'%3E%3Cpath d='M15 3.604H1v1.891h14v-1.89ZM1 7.208V16l7-3.926L15 16V7.208zM15 0H1v1.89h14z'/%3E%3C/svg%3E"/> Log Viewer for Symfony - + {# Toggle dark mode based on system settings #}
- + diff --git a/src/Service/File/LogParser.php b/src/Service/File/LogParser.php index 67b84818..2eebf868 100644 --- a/src/Service/File/LogParser.php +++ b/src/Service/File/LogParser.php @@ -12,13 +12,14 @@ use FD\LogViewer\Iterator\LogRecordIterator; use FD\LogViewer\Iterator\MaxRuntimeIterator; use FD\LogViewer\StreamReader\StreamReaderFactory; +use Psr\Clock\ClockInterface; use SplFileInfo; class LogParser { private const MAX_RUNTIME_IN_SECONDS = 10; - public function __construct(private readonly StreamReaderFactory $streamReaderFactory) + public function __construct(private readonly ClockInterface $clock, private readonly StreamReaderFactory $streamReaderFactory) { } @@ -27,7 +28,7 @@ public function parse(SplFileInfo $file, LogLineParserInterface $lineParser, Log // create iterators $streamReader = $this->streamReaderFactory->createForFile($file, $logQuery->direction, $logQuery->offset); $lineIterator = new LogLineParserIterator($streamReader, $lineParser, $logQuery->direction); - $iterator = new MaxRuntimeIterator($lineIterator, self::MAX_RUNTIME_IN_SECONDS, false); + $iterator = new MaxRuntimeIterator($this->clock, $lineIterator, self::MAX_RUNTIME_IN_SECONDS, false); $iterator = new LogRecordIterator($iterator, $lineParser, $logQuery->query); if ($logQuery->levels !== null || $logQuery->channels !== null) { $iterator = new LogRecordFilterIterator($iterator, $logQuery->levels, $logQuery->channels); diff --git a/src/Service/JsonManifestAssetLoader.php b/src/Service/JsonManifestAssetLoader.php new file mode 100644 index 00000000..6a01c647 --- /dev/null +++ b/src/Service/JsonManifestAssetLoader.php @@ -0,0 +1,41 @@ +|null */ + private ?array $manifestData = null; + + public function __construct(private readonly string $manifestPath) + { + } + + /** + * @throws JsonException + */ + public function getUrl(string $asset): string + { + if ($this->manifestData === null) { + if (file_exists($this->manifestPath) === false) { + throw new RuntimeException('Asset manifest file does not exist: ' . $this->manifestPath . '. Run "php bin/console assets:install".'); + } + + if (is_readable($this->manifestPath) === false) { + throw new RuntimeException('Asset manifest file is not readable: ' . $this->manifestPath); + } + + $data = json_decode((string)file_get_contents($this->manifestPath), true, 512, JSON_THROW_ON_ERROR); + assert(is_array($data)); + $this->manifestData = $data; + } + + $asset = $this->manifestData[$asset]['file'] ?? throw new RuntimeException('Asset file does not exist: ' . $asset); + + return '/bundles/fdlogviewer/' . $asset; + } +} diff --git a/src/Service/JsonManifestVersionStrategy.php b/src/Service/JsonManifestVersionStrategy.php deleted file mode 100644 index f8db7731..00000000 --- a/src/Service/JsonManifestVersionStrategy.php +++ /dev/null @@ -1,42 +0,0 @@ -|null */ - private ?array $manifestData = null; - - public function __construct(private readonly string $manifestPath) - { - } - - /** - * @throws JsonException - */ - public function getVersion(string $path): string - { - return $this->applyVersion($path); - } - - /** - * @throws JsonException - */ - public function applyVersion(string $path): string - { - if ($this->manifestData === null) { - $data = json_decode((string)file_get_contents($this->manifestPath), true, 512, JSON_THROW_ON_ERROR); - assert(is_array($data)); - $this->manifestData = $data; - } - - $path = $this->manifestData[$path]['file'] ?? throw new RuntimeException('Asset manifest file does not exist: ' . $path); - - return 'bundles/fdlogviewer/' . $path; - } -} diff --git a/src/Util/Clock.php b/src/Util/Clock.php new file mode 100644 index 00000000..6b98879d --- /dev/null +++ b/src/Util/Clock.php @@ -0,0 +1,15 @@ +client = static::createClient(['environment' => 'test', 'debug' => false]); + $this->client->catchExceptions(false); + } + + /** + * @template T of object + * @param class-string $serviceId + * + * @return T + * @throws Exception + */ + protected static function getService(string $serviceId, ?string $alias = null): object + { + /** @var T $service */ + $service = self::getContainer()->get($alias ?? $serviceId); + + return $service; + } + + /** + * @return array + */ + protected static function responseToArray(Response $response): array + { + $json = $response->getContent(); + static::assertIsString($json); + + $data = json_decode($json, true); + static::assertIsArray($data); + + return $data; + } + + protected static function getShortMd5(string $relativePath): string + { + return Utils::shortMd5((string)realpath(dirname(__DIR__) . '/' . $relativePath)); + } +} diff --git a/tests/Functional/DeleteFileTest.php b/tests/Functional/DeleteFileTest.php new file mode 100644 index 00000000..b8adcaa9 --- /dev/null +++ b/tests/Functional/DeleteFileTest.php @@ -0,0 +1,31 @@ +filesystem = $this->createMock(Filesystem::class); + static::getContainer()->set(Filesystem::class, $this->filesystem); + } + + public function testInvoke(): void + { + $this->filesystem->expects(self::once()) + ->method('remove') + ->with(static::callback(static fn(string $path) => str_contains($path, 'log' . DIRECTORY_SEPARATOR . 'test.log'))); + + $this->client->request('DELETE', '/log-viewer/api/file/' . self::getShortMd5('resources/Functional/log/test.log')); + static::assertResponseIsSuccessful(); + } +} diff --git a/tests/Functional/DeleteFolderTest.php b/tests/Functional/DeleteFolderTest.php new file mode 100644 index 00000000..23835534 --- /dev/null +++ b/tests/Functional/DeleteFolderTest.php @@ -0,0 +1,31 @@ +filesystem = $this->createMock(Filesystem::class); + static::getContainer()->set(Filesystem::class, $this->filesystem); + } + + public function testInvoke(): void + { + $this->filesystem->expects(self::once()) + ->method('remove') + ->with(static::callback(static fn(string $path) => str_contains($path, 'log' . DIRECTORY_SEPARATOR . 'test.log'))); + + $this->client->request('DELETE', '/log-viewer/api/folder/' . self::getShortMd5('resources/Functional/log')); + static::assertResponseIsSuccessful(); + } +} diff --git a/tests/Functional/DownloadFileTest.php b/tests/Functional/DownloadFileTest.php new file mode 100644 index 00000000..df8e83b0 --- /dev/null +++ b/tests/Functional/DownloadFileTest.php @@ -0,0 +1,18 @@ +client->request('GET', '/log-viewer/api/file/' . self::getShortMd5('resources/Functional/log/test.log')); + static::assertResponseIsSuccessful(); + + static::assertSame('attachment; filename=test.log', $this->client->getResponse()->headers->get('Content-Disposition')); + } +} diff --git a/tests/Functional/DownloadFolderTest.php b/tests/Functional/DownloadFolderTest.php new file mode 100644 index 00000000..61b3d8db --- /dev/null +++ b/tests/Functional/DownloadFolderTest.php @@ -0,0 +1,22 @@ +client->request('GET', '/log-viewer/api/folder/' . self::getShortMd5('resources/Functional/log')); + static::assertResponseIsSuccessful(); + + static::assertSame('attachment; filename=log.zip', $this->client->getResponse()->headers->get('Content-Disposition')); + } +} diff --git a/tests/Functional/FoldersTest.php b/tests/Functional/FoldersTest.php new file mode 100644 index 00000000..4731d87a --- /dev/null +++ b/tests/Functional/FoldersTest.php @@ -0,0 +1,20 @@ +client->request('GET', '/log-viewer/api/folders'); + static::assertResponseIsSuccessful(); + + /** @var array}> $data */ + $data = static::responseToArray($this->client->getResponse()); + static::assertSame('test.log', $data[0]['files'][0]['name']); + } +} diff --git a/tests/Functional/IndexTest.php b/tests/Functional/IndexTest.php new file mode 100644 index 00000000..a6b9e912 --- /dev/null +++ b/tests/Functional/IndexTest.php @@ -0,0 +1,29 @@ +assetLoader = $this->createMock(JsonManifestAssetLoader::class); + static::getContainer()->set(JsonManifestAssetLoader::class, $this->assetLoader); + } + + public function testInvoke(): void + { + $this->assetLoader->expects(self::exactly(2))->method('getUrl')->willReturn('url1', 'url2'); + + $this->client->request('GET', '/log-viewer/'); + static::assertResponseIsSuccessful(); + } +} diff --git a/tests/Functional/LogRecordsTest.php b/tests/Functional/LogRecordsTest.php new file mode 100644 index 00000000..51d73770 --- /dev/null +++ b/tests/Functional/LogRecordsTest.php @@ -0,0 +1,22 @@ +client->request('GET', '/log-viewer/api/logs?file=' . self::getShortMd5('resources/Functional/log/test.log')); + static::assertResponseIsSuccessful(); + + /** @var array{channels: mixed[], levels: mixed[], logs: mixed[]} $data */ + $data = static::responseToArray($this->client->getResponse()); + static::assertCount(2, $data['channels']); + static::assertCount(2, $data['levels']); + static::assertCount(3, $data['logs']); + } +} diff --git a/tests/Integration/Service/File/LogParserTest.php b/tests/Integration/Service/File/LogParserTest.php index 566c22cb..9ea18309 100644 --- a/tests/Integration/Service/File/LogParserTest.php +++ b/tests/Integration/Service/File/LogParserTest.php @@ -5,13 +5,12 @@ use FD\LogViewer\Entity\Output\DirectionEnum; use FD\LogViewer\Entity\Request\LogQueryDto; -use FD\LogViewer\Iterator\MaxRuntimeIterator; use FD\LogViewer\Service\File\LogParser; use FD\LogViewer\Service\File\Monolog\MonologLineParser; use FD\LogViewer\StreamReader\StreamReaderFactory; use FD\LogViewer\Tests\Integration\AbstractIntegrationTestCase; use PHPUnit\Framework\Attributes\CoversClass; -use Symfony\Bridge\PhpUnit\ClockMock; +use Psr\Clock\ClockInterface; use Symfony\Component\Finder\SplFileInfo; #[CoversClass(LogParser::class)] @@ -23,9 +22,8 @@ class LogParserTest extends AbstractIntegrationTestCase protected function setUp(): void { parent::setUp(); - ClockMock::register(MaxRuntimeIterator::class); $this->lineParser = new MonologLineParser(MonologLineParser::START_OF_MESSAGE_PATTERN, MonologLineParser::LOG_LINE_PATTERN); - $this->parser = new LogParser(new StreamReaderFactory()); + $this->parser = new LogParser($this->createMock(ClockInterface::class), new StreamReaderFactory()); } public function testParseWithPaginator(): void diff --git a/tests/Unit/Controller/DeleteFileControllerTest.php b/tests/Unit/Controller/DeleteFileControllerTest.php index 3b3a742c..36eb19d1 100644 --- a/tests/Unit/Controller/DeleteFileControllerTest.php +++ b/tests/Unit/Controller/DeleteFileControllerTest.php @@ -7,7 +7,7 @@ use FD\LogViewer\Controller\DeleteFileController; use FD\LogViewer\Entity\LogFolderCollection; use FD\LogViewer\Service\File\LogFileService; -use FD\LogViewer\Tests\TestEntityTrait; +use FD\LogViewer\Tests\Utility\TestEntityTrait; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; diff --git a/tests/Unit/Controller/DeleteFolderControllerTest.php b/tests/Unit/Controller/DeleteFolderControllerTest.php index 57b27d42..a486b29c 100644 --- a/tests/Unit/Controller/DeleteFolderControllerTest.php +++ b/tests/Unit/Controller/DeleteFolderControllerTest.php @@ -7,7 +7,7 @@ use FD\LogViewer\Controller\DeleteFolderController; use FD\LogViewer\Entity\LogFolderCollection; use FD\LogViewer\Service\File\LogFileService; -use FD\LogViewer\Tests\TestEntityTrait; +use FD\LogViewer\Tests\Utility\TestEntityTrait; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; diff --git a/tests/Unit/Controller/DownloadFileControllerTest.php b/tests/Unit/Controller/DownloadFileControllerTest.php index 7e64ed91..bd81a413 100644 --- a/tests/Unit/Controller/DownloadFileControllerTest.php +++ b/tests/Unit/Controller/DownloadFileControllerTest.php @@ -7,7 +7,7 @@ use FD\LogViewer\Controller\DownloadFileController; use FD\LogViewer\Entity\LogFolderCollection; use FD\LogViewer\Service\File\LogFileService; -use FD\LogViewer\Tests\TestEntityTrait; +use FD\LogViewer\Tests\Utility\TestEntityTrait; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; diff --git a/tests/Unit/Controller/DownloadFolderControllerTest.php b/tests/Unit/Controller/DownloadFolderControllerTest.php index b7f5a8c2..a62cbc99 100644 --- a/tests/Unit/Controller/DownloadFolderControllerTest.php +++ b/tests/Unit/Controller/DownloadFolderControllerTest.php @@ -8,7 +8,7 @@ use FD\LogViewer\Entity\LogFolderCollection; use FD\LogViewer\Service\File\LogFileService; use FD\LogViewer\Service\Folder\ZipArchiveFactory; -use FD\LogViewer\Tests\TestEntityTrait; +use FD\LogViewer\Tests\Utility\TestEntityTrait; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use SplFileInfo; diff --git a/tests/Unit/Controller/IndexControllerTest.php b/tests/Unit/Controller/IndexControllerTest.php index 22872a9b..5349bb91 100644 --- a/tests/Unit/Controller/IndexControllerTest.php +++ b/tests/Unit/Controller/IndexControllerTest.php @@ -9,22 +9,27 @@ use FD\LogViewer\Entity\Output\LogFolderOutput; use FD\LogViewer\Routing\RouteService; use FD\LogViewer\Service\Folder\LogFolderOutputProvider; +use FD\LogViewer\Service\JsonManifestAssetLoader; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Throwable; +use function DR\PHPUnitExtensions\Mock\consecutive; + /** * @extends AbstractControllerTestCase */ #[CoversClass(IndexController::class)] class IndexControllerTest extends AbstractControllerTestCase { + private JsonManifestAssetLoader&MockObject $assetLoader; private RouteService&MockObject $routeService; private LogFolderOutputProvider&MockObject $folderOutputProvider; protected function setUp(): void { + $this->assetLoader = $this->createMock(JsonManifestAssetLoader::class); $this->routeService = $this->createMock(RouteService::class); $this->folderOutputProvider = $this->createMock(LogFolderOutputProvider::class); parent::setUp(); @@ -39,14 +44,25 @@ public function testInvoke(): void $this->routeService->expects(self::once())->method('getBaseUri')->willReturn('baseUri'); $this->folderOutputProvider->expects(self::once())->method('provide')->with(DirectionEnum::Desc)->willReturn([$folder]); + $this->assetLoader->expects(self::exactly(2)) + ->method('getUrl') + ->with(...consecutive(['style.css'], ['src/main.ts'])) + ->willReturn('url1', 'url2'); - $this->expectRender('@FdLogViewer/index.html.twig', ['base_uri' => 'baseUri', 'folders' => [$folder]]); + $this->expectRender( + '@FdLogViewer/index.html.twig', + [ + 'base_uri' => 'baseUri', + 'folders' => [$folder], + 'assets' => ['style' => 'url1', 'js' => 'url2'] + ] + ); ($this->controller)(); } public function getController(): AbstractController { - return new IndexController($this->routeService, $this->folderOutputProvider); + return new IndexController($this->assetLoader, $this->routeService, $this->folderOutputProvider); } } diff --git a/tests/Unit/Controller/LogRecordsControllerTest.php b/tests/Unit/Controller/LogRecordsControllerTest.php index 9cefbda8..7ba4240a 100644 --- a/tests/Unit/Controller/LogRecordsControllerTest.php +++ b/tests/Unit/Controller/LogRecordsControllerTest.php @@ -11,7 +11,7 @@ use FD\LogViewer\Service\File\LogFileService; use FD\LogViewer\Service\File\LogQueryDtoFactory; use FD\LogViewer\Service\File\LogRecordsOutputProvider; -use FD\LogViewer\Tests\TestEntityTrait; +use FD\LogViewer\Tests\Utility\TestEntityTrait; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; diff --git a/tests/Unit/Entity/LogFolderCollectionTest.php b/tests/Unit/Entity/LogFolderCollectionTest.php index d3d1088a..a90fcb62 100644 --- a/tests/Unit/Entity/LogFolderCollectionTest.php +++ b/tests/Unit/Entity/LogFolderCollectionTest.php @@ -5,7 +5,7 @@ use FD\LogViewer\Entity\Config\LogFilesConfig; use FD\LogViewer\Entity\LogFolderCollection; -use FD\LogViewer\Tests\TestEntityTrait; +use FD\LogViewer\Tests\Utility\TestEntityTrait; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; diff --git a/tests/Unit/Entity/Output/LogFileOutputTest.php b/tests/Unit/Entity/Output/LogFileOutputTest.php index 65bbb18b..355dac27 100644 --- a/tests/Unit/Entity/Output/LogFileOutputTest.php +++ b/tests/Unit/Entity/Output/LogFileOutputTest.php @@ -4,7 +4,6 @@ namespace FD\LogViewer\Tests\Unit\Entity\Output; use FD\LogViewer\Entity\Output\LogFileOutput; -use FD\LogViewer\Tests\TestEntityTrait; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; diff --git a/tests/Unit/Entity/TempFileTest.php b/tests/Unit/Entity/TempFileTest.php index 0b3fbeb4..03c7f6b3 100644 --- a/tests/Unit/Entity/TempFileTest.php +++ b/tests/Unit/Entity/TempFileTest.php @@ -17,7 +17,7 @@ public function testConstruct(): void static::assertFileExists($path); // should be deleted on destruct - unset($file); + $file->destruct(); static::assertFileDoesNotExist($path); } } diff --git a/tests/Unit/Iterator/MaxRuntimeIteratorTest.php b/tests/Unit/Iterator/MaxRuntimeIteratorTest.php index 8618faf0..af1eed1e 100644 --- a/tests/Unit/Iterator/MaxRuntimeIteratorTest.php +++ b/tests/Unit/Iterator/MaxRuntimeIteratorTest.php @@ -4,52 +4,52 @@ namespace FD\LogViewer\Tests\Unit\Iterator; use ArrayIterator; +use DateTimeImmutable; use FD\LogViewer\Iterator\MaxRuntimeException; use FD\LogViewer\Iterator\MaxRuntimeIterator; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ClockMock; +use Psr\Clock\ClockInterface; #[CoversClass(MaxRuntimeIterator::class)] class MaxRuntimeIteratorTest extends TestCase { + private ClockInterface&MockObject $clock; + protected function setUp(): void { parent::setUp(); - ClockMock::register(MaxRuntimeIterator::class); - ClockMock::register(static::class); - ClockMock::withClockMock(true); + $this->clock = $this->createMock(ClockInterface::class); + $this->clock + ->method('now') + ->willReturn( + new DateTimeImmutable('2020-01-01 00:00:00'), + new DateTimeImmutable('2020-01-01 00:00:15'), + new DateTimeImmutable('2020-01-01 00:00:30'), + new DateTimeImmutable('2020-01-01 00:00:45'), + ); } public function testGetIteratorShouldNotThrow(): void { - $iterator = new MaxRuntimeIterator(new ArrayIterator([1, 2, 3]), 1, false); + $iterator = new MaxRuntimeIterator($this->clock, new ArrayIterator([1, 2, 3]), 1000, false); static::assertSame([1, 2, 3], iterator_to_array($iterator)); } public function testGetIteratorShouldStopSilently(): void { - $iterator = new MaxRuntimeIterator(new ArrayIterator([1, 2, 3]), 20, false); - $result = []; - foreach ($iterator as $value) { - // increment time by 15 seconds - sleep(15); - $result[] = $value; - } - static::assertSame([1, 2], $result); + // stop running after 20 seconds + $iterator = new MaxRuntimeIterator($this->clock, new ArrayIterator([1, 2, 3]), 20, false); + static::assertSame([1, 2], iterator_to_array($iterator)); } - /** - * @SuppressWarnings(PHPMD.UnusedLocalVariable) - */ public function testGetIteratorShouldThrow(): void { - $iterator = new MaxRuntimeIterator(new ArrayIterator([1, 2, 3]), 20, true); + // throw exception after 20 seconds + $iterator = new MaxRuntimeIterator($this->clock, new ArrayIterator([1, 2, 3]), 20, true); $this->expectException(MaxRuntimeException::class); - foreach ($iterator as $value) { - // increment time by 15 seconds - sleep(15); - } + iterator_to_array($iterator); } } diff --git a/tests/Unit/Service/File/LogFileServiceTest.php b/tests/Unit/Service/File/LogFileServiceTest.php index a2f4a1ad..53165353 100644 --- a/tests/Unit/Service/File/LogFileServiceTest.php +++ b/tests/Unit/Service/File/LogFileServiceTest.php @@ -10,7 +10,7 @@ use FD\LogViewer\Service\File\LogFileService; use FD\LogViewer\Service\FinderFactory; use FD\LogViewer\Service\Folder\LogFolderFactory; -use FD\LogViewer\Tests\TestEntityTrait; +use FD\LogViewer\Tests\Utility\TestEntityTrait; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; diff --git a/tests/Unit/Service/File/LogRecordsOutputProviderTest.php b/tests/Unit/Service/File/LogRecordsOutputProviderTest.php index c54b2f4f..2300a7dd 100644 --- a/tests/Unit/Service/File/LogRecordsOutputProviderTest.php +++ b/tests/Unit/Service/File/LogRecordsOutputProviderTest.php @@ -3,8 +3,6 @@ namespace FD\LogViewer\Tests\Unit\Service\File; -use FD\LogViewer\Entity\Config\FinderConfig; -use FD\LogViewer\Entity\Config\LogFilesConfig; use FD\LogViewer\Entity\Index\LogIndex; use FD\LogViewer\Entity\Index\PerformanceStats; use FD\LogViewer\Entity\LogFile; @@ -14,7 +12,7 @@ use FD\LogViewer\Service\File\LogFileParserProvider; use FD\LogViewer\Service\File\LogRecordsOutputProvider; use FD\LogViewer\Service\PerformanceService; -use FD\LogViewer\Tests\TestEntityTrait; +use FD\LogViewer\Tests\Utility\TestEntityTrait; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; diff --git a/tests/Unit/Service/File/Monolog/MonologFileParserTest.php b/tests/Unit/Service/File/Monolog/MonologFileParserTest.php index bde30dcc..cc83ba74 100644 --- a/tests/Unit/Service/File/Monolog/MonologFileParserTest.php +++ b/tests/Unit/Service/File/Monolog/MonologFileParserTest.php @@ -8,7 +8,7 @@ use FD\LogViewer\Service\File\LogParser; use FD\LogViewer\Service\File\Monolog\MonologFileParser; use FD\LogViewer\Service\File\Monolog\MonologLineParser; -use FD\LogViewer\Tests\TestEntityTrait; +use FD\LogViewer\Tests\Utility\TestEntityTrait; use Monolog\Logger; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; diff --git a/tests/Unit/Service/Folder/LogFolderFactoryTest.php b/tests/Unit/Service/Folder/LogFolderFactoryTest.php index 625d0feb..7d70af90 100644 --- a/tests/Unit/Service/Folder/LogFolderFactoryTest.php +++ b/tests/Unit/Service/Folder/LogFolderFactoryTest.php @@ -8,7 +8,6 @@ use FD\LogViewer\Entity\LogFolder; use FD\LogViewer\Entity\LogFolderCollection; use FD\LogViewer\Service\Folder\LogFolderFactory; -use FD\LogViewer\Tests\TestEntityTrait; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; use Symfony\Component\Finder\SplFileInfo; diff --git a/tests/Unit/Service/Folder/LogFolderOutputFactoryTest.php b/tests/Unit/Service/Folder/LogFolderOutputFactoryTest.php index b6533ff6..725f7ed7 100644 --- a/tests/Unit/Service/Folder/LogFolderOutputFactoryTest.php +++ b/tests/Unit/Service/Folder/LogFolderOutputFactoryTest.php @@ -9,7 +9,7 @@ use FD\LogViewer\Entity\Output\LogFileOutput; use FD\LogViewer\Entity\Output\LogFolderOutput; use FD\LogViewer\Service\Folder\LogFolderOutputFactory; -use FD\LogViewer\Tests\TestEntityTrait; +use FD\LogViewer\Tests\Utility\TestEntityTrait; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; diff --git a/tests/Unit/Service/Folder/ZipArchiveFactoryTest.php b/tests/Unit/Service/Folder/ZipArchiveFactoryTest.php index b47378aa..e7205f2f 100644 --- a/tests/Unit/Service/Folder/ZipArchiveFactoryTest.php +++ b/tests/Unit/Service/Folder/ZipArchiveFactoryTest.php @@ -4,7 +4,7 @@ namespace FD\LogViewer\Tests\Unit\Service\Folder; use FD\LogViewer\Service\Folder\ZipArchiveFactory; -use FD\LogViewer\Tests\TestEntityTrait; +use FD\LogViewer\Tests\Utility\TestEntityTrait; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; diff --git a/tests/Unit/Service/JsonManifestAssetLoaderTest.php b/tests/Unit/Service/JsonManifestAssetLoaderTest.php new file mode 100644 index 00000000..54095993 --- /dev/null +++ b/tests/Unit/Service/JsonManifestAssetLoaderTest.php @@ -0,0 +1,68 @@ + ['file' => 'file']]); + $this->path = vfsStream::setup('root', 0777, ['manifest.json' => $manifest])->url(); + $this->strategy = new JsonManifestAssetLoader($this->path . '/manifest.json'); + } + + /** + * @throws JsonException + */ + public function testGetUrlExistingFile(): void + { + static::assertSame('/bundles/fdlogviewer/file', $this->strategy->getUrl('/path/to/file')); + } + + /** + * @throws JsonException + */ + public function testGetUrlMissingManifestFile(): void + { + $strategy = new JsonManifestAssetLoader('foobar'); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Asset manifest file does not exist:'); + $strategy->getUrl('foobar'); + } + + /** + * @throws JsonException + */ + public function testGetUrlNonReadableManifest(): void + { + chmod($this->path . '/manifest.json', 0000); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Asset manifest file is not readable:'); + $this->strategy->getUrl('foobar'); + } + + /** + * @throws JsonException + */ + public function testGetUrlFailed(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Asset file does not exist:'); + $this->strategy->getUrl('foobar'); + } +} diff --git a/tests/Unit/Service/JsonManifestVersionStrategyTest.php b/tests/Unit/Service/JsonManifestVersionStrategyTest.php deleted file mode 100644 index a3681ab2..00000000 --- a/tests/Unit/Service/JsonManifestVersionStrategyTest.php +++ /dev/null @@ -1,43 +0,0 @@ - ['file' => 'file']]); - $path = vfsStream::setup('root', 0777, ['manifest.json' => $manifest])->url(); - $this->strategy = new JsonManifestVersionStrategy($path . '/manifest.json'); - } - - /** - * @throws JsonException - */ - public function testGetVersionExistingFile(): void - { - static::assertSame('bundles/fdlogviewer/file', $this->strategy->getVersion('/path/to/file')); - } - - /** - * @throws JsonException - */ - public function testGetVersionFailed(): void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Asset manifest file does not exist:'); - $this->strategy->getVersion('foobar'); - } -} diff --git a/tests/Unit/Util/ClockTest.php b/tests/Unit/Util/ClockTest.php new file mode 100644 index 00000000..8a8947c9 --- /dev/null +++ b/tests/Unit/Util/ClockTest.php @@ -0,0 +1,18 @@ +now()->getTimestamp(), 5); + } +} diff --git a/tests/TestEntityTrait.php b/tests/Utility/TestEntityTrait.php similarity index 98% rename from tests/TestEntityTrait.php rename to tests/Utility/TestEntityTrait.php index 5c7da3df..3bcfc655 100644 --- a/tests/TestEntityTrait.php +++ b/tests/Utility/TestEntityTrait.php @@ -1,7 +1,7 @@ + */ + public function registerBundles(): iterable + { + return [new FrameworkBundle(), new MonologBundle(), new TwigBundle(), new FdLogViewerBundle()]; + } + + public function getCacheDir(): string + { + return dirname(__DIR__) . '/.kernel/cache'; + } + + public function getLogDir(): string + { + return dirname(__DIR__) . '/resources/Functional/log'; + } + + /** + * @inheritdoc + */ + public function process(ContainerBuilder $container): void + { + $container->getDefinition(JsonManifestAssetLoader::class)->setPublic(true); + } + + /** + * @throws Exception + */ + public function registerContainerConfiguration(LoaderInterface $loader): void + { + $loader->load(function (ContainerBuilder $container) { + $container->loadFromExtension( + 'framework', + [ + 'secret' => 'test', + 'test' => true, + 'http_method_override' => false, + 'php_errors' => ['log' => true], + 'profiler' => ['enabled' => false], + 'validation' => ['email_validation_mode' => 'html5'], + 'router' => [ + 'resource' => dirname(__DIR__) . '/resources/Functional/routes.php', + 'type' => 'php', + 'strict_requirements' => true, + 'utf8' => true, + ], + ] + ); + + $container->loadFromExtension( + 'fd_log_viewer', + [ + 'log_files' => [ + 'monolog' => [ + 'downloadable' => true, + 'deletable' => true, + ], + ], + ] + ); + $container->loadFromExtension('twig', ['strict_variables' => true, 'debug' => false]); + + $container->register(Filesystem::class)->setPublic(true); + }); + } +} diff --git a/tests/resources/Functional/log/test.log b/tests/resources/Functional/log/test.log new file mode 100644 index 00000000..d874bc4b --- /dev/null +++ b/tests/resources/Functional/log/test.log @@ -0,0 +1,3 @@ +[2000-01-02T12:00:00+00:00] app.DEBUG: Message for line 1 [] [] +[2000-01-02T12:00:13+00:00] request.INFO: Message for line 2 [] [] +[2000-01-02T12:00:20+00:00] security.NOTICE: Message for line 3 [] [] diff --git a/tests/resources/Functional/routes.php b/tests/resources/Functional/routes.php new file mode 100644 index 00000000..e06293d3 --- /dev/null +++ b/tests/resources/Functional/routes.php @@ -0,0 +1,9 @@ +import('.', 'fd_symfony_log_viewer')->prefix('/log-viewer'); +};