diff --git a/.github/workflows/docker/docker-compose.yml b/.github/workflows/docker/docker-compose.yml index 32b97d8..a4600cb 100644 --- a/.github/workflows/docker/docker-compose.yml +++ b/.github/workflows/docker/docker-compose.yml @@ -26,7 +26,8 @@ services: php81: container_name: wonolog-php-81 build: - context: ./php81 + context: . + dockerfile: ./php81/Dockerfile depends_on: - db environment: @@ -36,7 +37,8 @@ services: php82: container_name: wonolog-php-82 build: - context: ./php82 + context: . + dockerfile: ./php82/Dockerfile depends_on: - db environment: diff --git a/.github/workflows/docker/php81/Dockerfile b/.github/workflows/docker/php81/Dockerfile index ea57213..ff9c9d6 100644 --- a/.github/workflows/docker/php81/Dockerfile +++ b/.github/workflows/docker/php81/Dockerfile @@ -10,4 +10,6 @@ RUN chmod +x /usr/bin/install-composer.sh && /usr/bin/install-composer.sh && mv COPY wait-for.sh /usr/bin/wait-for.sh RUN chmod +x /usr/bin/wait-for.sh +RUN git config --global --add safe.directory /var/www/html + WORKDIR /var/www/html diff --git a/.github/workflows/docker/php82/Dockerfile b/.github/workflows/docker/php82/Dockerfile index 829eb2c..1773960 100644 --- a/.github/workflows/docker/php82/Dockerfile +++ b/.github/workflows/docker/php82/Dockerfile @@ -10,4 +10,6 @@ RUN chmod +x /usr/bin/install-composer.sh && /usr/bin/install-composer.sh && mv COPY wait-for.sh /usr/bin/wait-for.sh RUN chmod +x /usr/bin/wait-for.sh +RUN git config --global --add safe.directory /var/www/html + WORKDIR /var/www/html diff --git a/.github/workflows/php-integration-tests.yml b/.github/workflows/php-integration-tests.yml index 900bece..d8e4710 100644 --- a/.github/workflows/php-integration-tests.yml +++ b/.github/workflows/php-integration-tests.yml @@ -23,6 +23,7 @@ jobs: fail-fast: false matrix: php-service: [ '81', '82' ] + dependency-versions: [ 'lowest', 'highest' ] steps: - name: Checkout uses: actions/checkout@v3 @@ -34,4 +35,6 @@ jobs: - name: Run integration tests working-directory: .github/workflows/docker - run: docker compose run --rm php${{ matrix.php-service }} sh -c "wait-for.sh wonolog-db:3306 -t 15 -- composer update && composer tests:integration" + run: | + COMPOSER_DEPENDENCY_VERSIONS=${{ matrix.dependency-versions == 'lowest' && '--prefer-lowest' || '' }} + docker compose run --rm php${{ matrix.php-service }} sh -c "wait-for.sh wonolog-db:3306 -t 15 -- composer update $COMPOSER_DEPENDENCY_VERSIONS && composer tests:integration" diff --git a/.github/workflows/php-static-analysis.yml b/.github/workflows/php-static-analysis.yml index e3d7431..5736bb5 100644 --- a/.github/workflows/php-static-analysis.yml +++ b/.github/workflows/php-static-analysis.yml @@ -33,6 +33,7 @@ jobs: strategy: matrix: php-version: ["8.1", "8.2", "8.3", "8.4"] + dependency-versions: ['highest', 'lowest'] with: PHP_VERSION: ${{ matrix.php-version }} @@ -44,9 +45,12 @@ jobs: static-code-analysis-php: if: ${{ (github.event_name != 'workflow_dispatch') || ((github.event.inputs.jobs == 'Run all') || (github.event.inputs.jobs == 'Run static analysis only')) }} - uses: inpsyde/reusable-workflows/.github/workflows/static-analysis-php.yml@main + uses: inpsyde/reusable-workflows/.github/workflows/static-analysis-php.yml@support_dependency_versions_in_static_analysis strategy: matrix: php-version: ["8.1", "8.2", "8.3", "8.4"] + dependency-versions: ["lowest", "highest"] with: PHP_VERSION: ${{ matrix.php-version }} + PHPSTAN_ARGS: "--no-progress --memory-limit=1G" + DEPENDENCY_VERSIONS: ${{ matrix.dependency-versions }} diff --git a/composer.json b/composer.json index 9715577..03de05a 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ "php": ">=8.1 < 8.5", "psr/log": "^2.0||^3.0", "wecodemore/wordpress-early-hook": "^1.3.0", - "monolog/monolog": "^2.3.5" + "monolog/monolog": "^2.0||^3.0" }, "require-dev": { "brain/monkey": "^2.6.1", diff --git a/phpstan.neon.dist b/phpstan.neon.dist index d9dc2cb..18c97c0 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -13,6 +13,9 @@ parameters: paths: - src treatPhpDocTypesAsCertain: false + reportUnmatchedIgnoredErrors: false ignoreErrors: - '#Class PHPMailer not found.#' - '#Access to property \$\w+ on an unknown class PHPMailer#' + - '#has invalid return type Monolog\\LogRecord#' + - '#Instantiated class Monolog\\LogRecord not found#' diff --git a/src/Configurator.php b/src/Configurator.php index 25800ab..d565542 100644 --- a/src/Configurator.php +++ b/src/Configurator.php @@ -813,7 +813,9 @@ protected function shouldSetup(): bool // We use WONOLOG_DISABLE instead of WONOLOG_ENABLE so that enabled is the default. $disabled = getenv('WONOLOG_DISABLE'); - defined('WONOLOG_DISABLE') and $disabled = WONOLOG_DISABLE; + if (defined('WONOLOG_DISABLE')) { + $disabled = WONOLOG_DISABLE; + } /** * Filters whether to completely disable Wonolog. diff --git a/src/DefaultHandler/FileHandler.php b/src/DefaultHandler/FileHandler.php index ba85215..75beeea 100644 --- a/src/DefaultHandler/FileHandler.php +++ b/src/DefaultHandler/FileHandler.php @@ -13,6 +13,7 @@ namespace Inpsyde\Wonolog\DefaultHandler; +use Inpsyde\Wonolog\Levels; use Inpsyde\Wonolog\LogLevel; use Inpsyde\Wonolog\Processor; use Monolog\Formatter\FormatterInterface; @@ -22,7 +23,7 @@ use Monolog\Handler\NullHandler; use Monolog\Handler\ProcessableHandlerInterface; use Monolog\Handler\StreamHandler; -use Monolog\Logger; +use Monolog\LogRecord; use Monolog\ResettableInterface; class FileHandler implements @@ -164,36 +165,37 @@ public function disableBuffering(): FileHandler } /** - * @param array $record + * @param array|LogRecord $record * @return bool * * @psalm-suppress MixedArgumentTypeCoercion + * @phpstan-ignore-next-line */ - public function handle(array $record): bool + public function handle(array|LogRecord $record): bool { $this->ensureHandler(); - return $this->handler->handle($record); } /** - * @param array $record + * @param array|LogRecord $record * @return bool * * @psalm-suppress MixedArgumentTypeCoercion + * @phpstan-ignore-next-line */ - public function isHandling(array $record): bool + public function isHandling(array|LogRecord $record): bool { $this->ensureHandler(); - return $this->handler->isHandling($record); } /** - * @param array $records + * @param array|array $records * @return void * * @psalm-suppress MixedArgumentTypeCoercion + * @phpstan-ignore-next-line */ public function handleBatch(array $records): void { @@ -230,7 +232,7 @@ public function pushProcessor(callable $callback): HandlerInterface } /** - * @return callable(array):array + * @return callable(array):array | callable(LogRecord):LogRecord * * @psalm-suppress MixedReturnTypeCoercion * @psalm-suppress LessSpecificImplementedReturnType @@ -278,7 +280,8 @@ public function getFormatter(): FormatterInterface /** @var FormatterInterface|null $noopFormatter */ static $noopFormatter; - return $noopFormatter ?? $noopFormatter = new PassthroughFormatter(); + return $noopFormatter + ?? $noopFormatter = new PassthroughFormatter(); } /** @@ -336,12 +339,12 @@ private function ensureHandler(): void if ($this->handler) { return; } - try { $this->logFilePath = $this->logFilePath(); $level = $this->minLevel ?? LogLevel::defaultMinLevel(); if (!$level) { - $level = Logger::DEBUG; + /** @phpstan-ignore-next-line classConstant.deprecated */ + $level = Levels::DEBUG; } $streamBuffer = $this->buffering || $this->bubble; $handler = new StreamHandler($this->logFilePath, $level, $streamBuffer, null, true); diff --git a/src/DefaultHandler/PassthroughFormatter.php b/src/DefaultHandler/PassthroughFormatter.php index 131c262..b60f25e 100644 --- a/src/DefaultHandler/PassthroughFormatter.php +++ b/src/DefaultHandler/PassthroughFormatter.php @@ -5,14 +5,16 @@ namespace Inpsyde\Wonolog\DefaultHandler; use Monolog\Formatter\FormatterInterface; +use Monolog\LogRecord; class PassthroughFormatter implements FormatterInterface { /** - * @param array $record + * @param array|LogRecord $record * @return array + * @phpstan-ignore-next-line */ - public function format(array $record): array + public function format(array|LogRecord $record): array|LogRecord { return $record; } diff --git a/src/Levels.php b/src/Levels.php new file mode 100644 index 0000000..5e33644 --- /dev/null +++ b/src/Levels.php @@ -0,0 +1,87 @@ + $levels Logging levels with the levels as key + * + * @phpstan-var array $levels Logging levels with the levels as key + */ + protected static array $levels = [ + self::DEBUG => 'DEBUG', + self::INFO => 'INFO', + self::NOTICE => 'NOTICE', + self::WARNING => 'WARNING', + self::ERROR => 'ERROR', + self::CRITICAL => 'CRITICAL', + self::ALERT => 'ALERT', + self::EMERGENCY => 'EMERGENCY', + ]; + + /** + * @return array + */ + public static function allLevels(): array + { + return array_flip(static::$levels); + } +} diff --git a/src/LogActionUpdater.php b/src/LogActionUpdater.php index 6b270db..30e4b04 100644 --- a/src/LogActionUpdater.php +++ b/src/LogActionUpdater.php @@ -48,7 +48,6 @@ public function update(LogData $log): void ) { return; } - try { $context = $this->parseContext($log); $this->channels diff --git a/src/LogLevel.php b/src/LogLevel.php index 7886b1c..5f8e43a 100644 --- a/src/LogLevel.php +++ b/src/LogLevel.php @@ -4,22 +4,28 @@ namespace Inpsyde\Wonolog; -use Monolog\Logger; - /** * Utility object used to build default min logging level based WordPress and environment settings. * It also has a method to check the validity of a value as level identifier. */ abstract class LogLevel { - public const DEBUG = Logger::DEBUG; - public const INFO = Logger::INFO; - public const NOTICE = Logger::NOTICE; - public const WARNING = Logger::WARNING; - public const ERROR = Logger::ERROR; - public const CRITICAL = Logger::CRITICAL; - public const ALERT = Logger::ALERT; - public const EMERGENCY = Logger::EMERGENCY; + /** @phpstan-ignore-next-line classConstant.deprecated */ + public const DEBUG = Levels::DEBUG; + /** @phpstan-ignore-next-line classConstant.deprecated */ + public const INFO = Levels::INFO; + /** @phpstan-ignore-next-line classConstant.deprecated */ + public const NOTICE = Levels::NOTICE; + /** @phpstan-ignore-next-line classConstant.deprecated */ + public const WARNING = Levels::WARNING; + /** @phpstan-ignore-next-line classConstant.deprecated */ + public const ERROR = Levels::ERROR; + /** @phpstan-ignore-next-line classConstant.deprecated */ + public const CRITICAL = Levels::CRITICAL; + /** @phpstan-ignore-next-line classConstant.deprecated */ + public const ALERT = Levels::ALERT; + /** @phpstan-ignore-next-line classConstant.deprecated */ + public const EMERGENCY = Levels::EMERGENCY; private static ?int $minLevel = null; @@ -33,7 +39,7 @@ abstract class LogLevel */ final public static function allLevels(): array { - return Logger::getLevels(); + return Levels::allLevels(); } /** @@ -57,7 +63,8 @@ final public static function defaultMinLevel(): int // If no valid level is defined via env var, then let's resort to WP constants. if (!$minLevel) { $const = defined('WP_DEBUG_LOG') ? 'WP_DEBUG_LOG' : 'WP_DEBUG'; - $minLevel = (defined($const) && constant($const)) ? Logger::DEBUG : Logger::WARNING; + /** @phpstan-ignore-next-line classConstant.deprecated */ + $minLevel = (defined($const) && constant($const)) ? Levels::DEBUG : Levels::WARNING; } self::$minLevel = $minLevel; diff --git a/src/MonologUtils.php b/src/MonologUtils.php new file mode 100644 index 0000000..111d240 --- /dev/null +++ b/src/MonologUtils.php @@ -0,0 +1,15 @@ + defined('DOING_CRON') && DOING_CRON, // @phpstan-ignore-line @@ -43,13 +46,33 @@ public function __invoke(array $record): array $data['site_id'] = get_current_blog_id(); $data['network_id'] = get_current_network_id(); } + $logRecordClass = 'Monolog\LogRecord'; + if (class_exists($logRecordClass) && $record instanceof $logRecordClass) { + return $this->handleExtraInfoFromLogRecord($record, $data); + } + return $this->handleExtraInfoFromArrayRecord($record, $data); + } + private function handleExtraInfoFromArrayRecord(array $record, array $data): array + { if (!isset($record['extra']) || !is_array($record['extra'])) { $record['extra'] = []; } $record['extra']['wp'] = $data; + return $record; + } + /** @phpstan-ignore-next-line */ + private function handleExtraInfoFromLogRecord(LogRecord $record, array $data): LogRecord + { + /** @phpstan-ignore-next-line */ + if (!isset($record->extra) || !is_array($record->extra)) { + /** @phpstan-ignore-next-line */ + $record->extra = []; + } + /** @phpstan-ignore-next-line */ + $record->extra['wp'] = $data; return $record; } diff --git a/src/PsrBridge.php b/src/PsrBridge.php index e189400..dfcc9bb 100644 --- a/src/PsrBridge.php +++ b/src/PsrBridge.php @@ -6,11 +6,13 @@ use Inpsyde\Wonolog\Data\Log; use Inpsyde\Wonolog\Data\LogData; +use Monolog\LogRecord; use Monolog\Processor\PsrLogMessageProcessor; use Psr\Log\AbstractLogger; /** * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-ignore-next-line */ class PsrBridge extends AbstractLogger { @@ -21,6 +23,7 @@ class PsrBridge extends AbstractLogger private ?string $defaultChannel = null; private PsrLogMessageProcessor $processor; + private RecordFactory $recordFactory; /** * @param LogActionUpdater $updater @@ -41,6 +44,7 @@ private function __construct(LogActionUpdater $updater, Channels $channels) $this->updater = $updater; $this->channels = $channels; $this->processor = new PsrLogMessageProcessor(null, true); + $this->recordFactory = new RecordFactory(); } /** @@ -91,23 +95,43 @@ public function log(mixed $level, mixed $message, array $context = []): void } unset($context[LogData::CHANNEL]); - /** @var Record $record */ - $record = compact('message', 'context', 'level'); + $record = $this->recordFactory->createRecord($message, $level, $channel, $context); $record = ($this->processor)($record); + + $this->updater->update($this->createLog($record, $level, $channel, $throwable)); + } + + /** + * @phpstan-import-type Record from \Monolog\Logger + */ + protected function createLog( + /** @phpstan-ignore-next-line */ + array|LogRecord $record, + mixed $level, + string $channel, + ?\Throwable $throwable + ): Log { + + $class = 'Monolog\\LogRecord'; + $recordData = (class_exists($class) && $record instanceof $class) + ? $record->toArray() + : $record; + // we receive the $record after the processor, we have to check if key exists // @phpstan-ignore function.alreadyNarrowedType - if (array_key_exists('message', $record)) { - $message = (string) $record['message']; - } + $message = (string) (array_key_exists('message', $recordData) + ? $recordData['message'] + : '' + ); + $context = []; // @phpstan-ignore function.alreadyNarrowedType - if (array_key_exists('context', $record)) { - $context = (array) $record['context']; + if (array_key_exists('context', $recordData)) { + $context = (array) $recordData['context']; } unset($context['exception']); if ($throwable) { $context['exception'] = $throwable; } - - $this->updater->update(new Log($message, $level, $channel, $context)); + return new Log($message, $level, $channel, $context); } } diff --git a/src/PsrBridgeAdapterAbstract.php b/src/PsrBridgeAdapterAbstract.php new file mode 100644 index 0000000..0b7dc43 --- /dev/null +++ b/src/PsrBridgeAdapterAbstract.php @@ -0,0 +1,42 @@ +updater = $updater; + $this->channels = $channels; + $this->processor = new PsrLogMessageProcessor(null, true); + } + + /** + * @param string $defaultChannel + * @return static + */ + public function withDefaultChannel(string $defaultChannel): PsrBridgeAdapterAbstract + { + $this->channels->addChannel($defaultChannel); + $this->defaultChannel = $defaultChannel; + + return $this; + } +} diff --git a/src/RecordFactory.php b/src/RecordFactory.php new file mode 100644 index 0000000..b15d312 --- /dev/null +++ b/src/RecordFactory.php @@ -0,0 +1,46 @@ +createRecordV2($message, $level, $context) + : $this->createRecordV3($message, $level, $channel, $context); + } + + /** + * @phpstan-import-type Record from \Monolog\Logger + */ + public function createRecordV2(string $message, int $level, array $context = []): array + { + /** @var Record $record */ + /** @phpstan-ignore-next-line */ + $record = compact('message', 'context', 'level'); + /** @phpstan-ignore-next-line */ + return $record; + } + + public static function createRecordV3(string $message, int $level, string $channel, array $context = []): LogRecord + { + return new LogRecord( + new \DateTimeImmutable(), + $channel, + /** @phpstan-ignore-next-line class.notFound */ + Level::fromValue($level), + $message, + $context + ); + } +} diff --git a/src/Registry/ProcessorsRegistry.php b/src/Registry/ProcessorsRegistry.php index 909dd0a..330d715 100644 --- a/src/Registry/ProcessorsRegistry.php +++ b/src/Registry/ProcessorsRegistry.php @@ -4,6 +4,8 @@ namespace Inpsyde\Wonolog\Registry; +use Monolog\LogRecord; + class ProcessorsRegistry implements \Countable { /** @@ -212,7 +214,7 @@ public function hasProcessorForAnyChannel(string $identifier): bool /** * @param string $channel - * @return list + * @return list|list */ public function findForChannel(string $channel): array { diff --git a/tests/integration/AdvancedConfigTest.php b/tests/integration/AdvancedConfigTest.php index fd59803..a6e868f 100644 --- a/tests/integration/AdvancedConfigTest.php +++ b/tests/integration/AdvancedConfigTest.php @@ -13,10 +13,11 @@ use Inpsyde\Wonolog\LogActionUpdater; use Inpsyde\Wonolog\Tests\IntegrationTestCase; use Monolog\Handler\TestHandler; -use Monolog\Logger; +use Monolog\LogRecord; use org\bovigo\vfs\vfsStream; use PHPUnit\Framework\AssertionFailedError; use Psr\Log\LogLevel; +use Inpsyde\Wonolog\Levels; use function Inpsyde\Wonolog\makeLogger; @@ -28,12 +29,12 @@ class AdvancedConfigTest extends IntegrationTestCase /** * @var string */ - private $logFile; + private ?string $logFile = null; /** * @var TestHandler */ - private $testHandler; + private ?TestHandler $testHandler = null; /** * @param Configurator $configurator @@ -58,7 +59,7 @@ public function update(string $hook, array $args, LogActionUpdater $updater): vo ->disableBuffering() ->withFolder($dir->url() . '/logs') ->withFilename('wonolog.log') - ->withMinimumLevel(Logger::NOTICE); + ->withMinimumLevel(Levels::NOTICE); $this->logFile = $dir->url() . '/logs/wonolog.log'; $this->testHandler = new TestHandler(); @@ -69,19 +70,34 @@ public function update(string $hook, array $args, LogActionUpdater $updater): vo ->removeHandlerFromChannels('default-handler', Channels::SECURITY) ->pushHandlerForChannels($this->testHandler, 'test-handler', Channels::DEBUG, 'TESTS') ->disableAllDefaultHookListeners() - ->addActionListener(new QueryErrorsListener(Logger::NOTICE)) + ->addActionListener(new QueryErrorsListener(Levels::NOTICE)) ->addActionListener($listener, 'test-listener') ->registerLogHook('my-plugin.log', 'MY_PLUGIN') ->registerLogHook('something.else.happened') ->withIgnorePattern('cron job performed in [0-9\.]+ seconds') ->disableWpContextProcessor() - ->pushProcessor('test-processor', static function (array $record): array { - empty($record['extra']) and $record['extra'] = []; - $record['extra']['testClass'] = __CLASS__; - return $record; + ->pushProcessor('test-processor', function (array|LogRecord $record): array|LogRecord { + return is_array($record) + ? $this->addExtraDataToProcessorWhenRecordIsArray($record) + : $this->addExtraDataToProcessorWhenRecordIsLogRecord($record) + ; }); } + protected function addExtraDataToProcessorWhenRecordIsArray(array $record): array + { + empty($record['extra']) and $record['extra'] = []; + $record['extra']['testClass'] = __CLASS__; + return $record; + } + + protected function addExtraDataToProcessorWhenRecordIsLogRecord(LogRecord $record): LogRecord + { + empty($record->extra) and $record->extra = []; + $record->extra['testClass'] = __CLASS__; + return $record; + } + /** * @test */ @@ -129,7 +145,7 @@ public function testLogFromArrayInDefaultHandlerOnlyDueToChannel(): void [ 'message' => 'Something happened.', 'channel' => Channels::HTTP, - 'level' => LogLevel::NOTICE + 'level' => LogLevel::NOTICE, ] ); @@ -148,7 +164,7 @@ public function testLogFromArrayNoWhereOnlyDueToChannel(): void [ 'message' => 'Something happened.', 'channel' => Channels::SECURITY, - 'level' => LogLevel::NOTICE + 'level' => LogLevel::NOTICE, ] ); @@ -167,7 +183,7 @@ public function testLogFromArrayNowhereDueToIgnorePattern(): void [ 'message' => 'cron job performed in 5.0256 seconds', 'channel' => Channels::DEBUG, - 'level' => LogLevel::NOTICE + 'level' => LogLevel::NOTICE, ] ); @@ -301,7 +317,7 @@ private function assertLogFileHasLine( $context and $messageLog .= sprintf(' (%s)', json_encode($context)); $lines = @file($this->logFile) ?: []; - foreach ((array)$lines as $line) { + foreach ((array) $lines as $line) { preg_match( '~^\[[^\]]+\] (?[A-Z_-]+)\.(?[A-Z]+): (?[^\[\{]+) (?.+?)$~', trim($line), diff --git a/tests/src/IntegrationTestCase.php b/tests/src/IntegrationTestCase.php index f1e8a36..8cf9b5b 100644 --- a/tests/src/IntegrationTestCase.php +++ b/tests/src/IntegrationTestCase.php @@ -14,6 +14,7 @@ namespace Inpsyde\Wonolog\Tests; use Inpsyde\Wonolog\Configurator; +use Inpsyde\Wonolog\LogActionUpdater; use Inpsyde\Wonolog\Registry\HandlersRegistry; abstract class IntegrationTestCase extends \PHPUnit\Framework\TestCase @@ -46,6 +47,13 @@ function (Configurator $configurator): void { } ); + add_action(LogActionUpdater::ACTION_LOGGER_ERROR, static function ($log, $throwable) { + //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_print_backtrace + debug_print_backtrace(); + //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_dump + var_dump('There was an error', $throwable->getMessage()); + }, 10, 2); + require_once ABSPATH . 'wp-config.php'; } } diff --git a/tests/src/IntegrationTestsExtension.php b/tests/src/IntegrationTestsExtension.php index f9aa5c7..ce58406 100644 --- a/tests/src/IntegrationTestsExtension.php +++ b/tests/src/IntegrationTestsExtension.php @@ -150,6 +150,7 @@ public function executeBeforeFirstTest(): void static::runWpCliCommand(['config', 'set', 'SAVEQUERIES', 'true']); static::resetDb(); + $this->createThemesFolder(); } /** @@ -187,6 +188,21 @@ private function resetWpConfig(bool $end): bool return true; } + private function createThemesFolder(): void + { + $themeDir = ABSPATH . 'wp-content/themes'; + $result = true; + if (!\is_dir($themeDir)) { + $result = \mkdir($themeDir, 0755, true); + } + + if ($result === false) { + throw new \Exception("Failed to create test theme at $themeDir"); + } + + \fwrite(STDOUT, "Test theme created at $themeDir\n"); + } + /** * @param string $paramName * @return string diff --git a/tests/unit/ChannelsTest.php b/tests/unit/ChannelsTest.php index 9556f3d..79a826f 100644 --- a/tests/unit/ChannelsTest.php +++ b/tests/unit/ChannelsTest.php @@ -19,6 +19,7 @@ use Inpsyde\Wonolog\Data\Emergency; use Inpsyde\Wonolog\Data\Info; use Inpsyde\Wonolog\Factory; +use Inpsyde\Wonolog\Levels; use Inpsyde\Wonolog\Registry\HandlersRegistry; use Inpsyde\Wonolog\Tests\UnitTestCase; use Monolog\Handler\BufferHandler; @@ -116,7 +117,7 @@ public function testBlackList(): void $channels ->withIgnorePattern('~annoying') ->withIgnorePattern('^prefix') - ->withIgnorePattern('ABC: [0-9]+', Logger::ALERT) + ->withIgnorePattern('ABC: [0-9]+', Levels::ALERT) ->withIgnorePattern('cron "[^"]+"', null, Channels::CRON); static::assertTrue($channels->isIgnored(new Debug('~~annoying~~', Channels::DEBUG))); diff --git a/tests/unit/ConfiguratorTest.php b/tests/unit/ConfiguratorTest.php index 37c6f11..c5381b0 100644 --- a/tests/unit/ConfiguratorTest.php +++ b/tests/unit/ConfiguratorTest.php @@ -47,7 +47,7 @@ public function testConfiguratorDisabledViaEnvVar(): void * @test * @runInSeparateProcess */ - public function testConfiguratorDisabledViaConstant() + public function testConfiguratorDisabledViaConstant(): void { Monkey\Actions\expectDone(Configurator::ACTION_SETUP)->never(); Monkey\Actions\expectDone(Configurator::ACTION_LOADED)->never(); @@ -60,7 +60,7 @@ public function testConfiguratorDisabledViaConstant() * @test * @runInSeparateProcess */ - public function testConfiguratorDisabledViaFilter() + public function testConfiguratorDisabledViaFilter(): void { Monkey\Actions\expectDone(Configurator::ACTION_SETUP)->never(); Monkey\Actions\expectDone(Configurator::ACTION_LOADED)->never(); @@ -76,7 +76,7 @@ public function testConfiguratorDisabledViaFilter() * @test * @runInSeparateProcess */ - public function testConfiguratorDisabledBecauseNoHandlers() + public function testConfiguratorDisabledBecauseNoHandlers(): void { Monkey\Actions\expectDone(Configurator::ACTION_SETUP)->once(); Monkey\Actions\expectDone(Configurator::ACTION_LOADED)->never(); @@ -93,7 +93,7 @@ public function testConfiguratorDisabledBecauseNoHandlers() * @test * @runInSeparateProcess */ - public function testEnabledViaCustomHandler() + public function testEnabledViaCustomHandler(): void { Monkey\Functions\when('remove_all_actions')->justReturn(); Monkey\Actions\expectDone(Configurator::ACTION_SETUP)->once(); @@ -112,7 +112,7 @@ public function testEnabledViaCustomHandler() * @test * @runInSeparateProcess */ - public function testFallbackHandlerDisabledInOneChannel() + public function testFallbackHandlerDisabledInOneChannel(): void { Monkey\Functions\when('remove_all_actions')->justReturn(); Monkey\Actions\expectDone(Configurator::ACTION_SETUP)->once(); @@ -152,7 +152,7 @@ public function __construct(Factory $factory) * @test * @runInSeparateProcess */ - public function testFallbackHandlerEnabledInSpecificChannels() + public function testFallbackHandlerEnabledInSpecificChannels(): void { Monkey\Functions\when('remove_all_actions')->justReturn(); Monkey\Actions\expectDone(Configurator::ACTION_SETUP)->once(); @@ -190,7 +190,7 @@ public function __construct(Factory $factory) * @test * @runInSeparateProcess */ - public function testWpContextProcessorEnabledInSpecificChannels() + public function testWpContextProcessorEnabledInSpecificChannels(): void { Monkey\Functions\when('remove_all_actions')->justReturn(); Monkey\Actions\expectDone(Configurator::ACTION_SETUP)->once(); @@ -227,7 +227,7 @@ public function __construct(Factory $factory) * @test * @runInSeparateProcess */ - public function testWpContextProcessorDisabledInSpecificChannels() + public function testWpContextProcessorDisabledInSpecificChannels(): void { Monkey\Functions\when('remove_all_actions')->justReturn(); Monkey\Actions\expectDone(Configurator::ACTION_SETUP)->once(); @@ -264,7 +264,7 @@ public function __construct(Factory $factory) * @test * @runInSeparateProcess */ - public function testDefaultHookListenersOptIn() + public function testDefaultHookListenersOptIn(): void { Monkey\Functions\when('remove_all_actions')->justReturn(); Monkey\Actions\expectDone(Configurator::ACTION_SETUP)->once(); @@ -292,7 +292,7 @@ public function testDefaultHookListenersOptIn() * @test * @runInSeparateProcess */ - public function testDefaultHookListenersOptOut() + public function testDefaultHookListenersOptOut(): void { Monkey\Functions\when('remove_all_actions')->justReturn(); Monkey\Actions\expectDone(Configurator::ACTION_SETUP)->once(); diff --git a/tests/unit/Data/CustomLogDataTest.php b/tests/unit/Data/CustomLogDataTest.php index 0890cb3..2da18f2 100644 --- a/tests/unit/Data/CustomLogDataTest.php +++ b/tests/unit/Data/CustomLogDataTest.php @@ -23,6 +23,7 @@ use Inpsyde\Wonolog\Data\LogData; use Inpsyde\Wonolog\Data\Notice; use Inpsyde\Wonolog\Data\Warning; +use Inpsyde\Wonolog\Levels; use Inpsyde\Wonolog\Tests\UnitTestCase; use Monolog\Logger; @@ -46,14 +47,14 @@ public function testLevels(int $expectedLevel, LogData $log): void public function dataProviderLogLevels(): array { return [ - [Logger::ALERT, new Alert('test', Channels::DEBUG)], - [Logger::CRITICAL, new Critical('test', Channels::DEBUG)], - [Logger::DEBUG, new Debug('test', Channels::DEBUG)], - [Logger::EMERGENCY, new Emergency('test', Channels::DEBUG)], - [Logger::ERROR, new Error('test', Channels::DEBUG)], - [Logger::INFO, new Info('test', Channels::DEBUG)], - [Logger::NOTICE, new Notice('test', Channels::DEBUG)], - [Logger::WARNING, new Warning('test', Channels::DEBUG)], + [Levels::ALERT, new Alert('test', Channels::DEBUG)], + [Levels::CRITICAL, new Critical('test', Channels::DEBUG)], + [Levels::DEBUG, new Debug('test', Channels::DEBUG)], + [Levels::EMERGENCY, new Emergency('test', Channels::DEBUG)], + [Levels::ERROR, new Error('test', Channels::DEBUG)], + [Levels::INFO, new Info('test', Channels::DEBUG)], + [Levels::NOTICE, new Notice('test', Channels::DEBUG)], + [Levels::WARNING, new Warning('test', Channels::DEBUG)], ]; } } diff --git a/tests/unit/Data/FailedLoginTest.php b/tests/unit/Data/FailedLoginTest.php index ba5f6b8..1c9c1da 100644 --- a/tests/unit/Data/FailedLoginTest.php +++ b/tests/unit/Data/FailedLoginTest.php @@ -14,6 +14,7 @@ namespace Inpsyde\Wonolog\Tests\Unit\Data; use Inpsyde\Wonolog\Channels; +use Inpsyde\Wonolog\Levels; use Inpsyde\Wonolog\Tests\UnitTestCase; use Inpsyde\Wonolog\Data\FailedLogin; use Brain\Monkey\Functions; @@ -66,23 +67,23 @@ public function testData(): void } $expectedLoggedLevels = [ - 3 => Logger::NOTICE, - 23 => Logger::NOTICE, - 43 => Logger::NOTICE, - 63 => Logger::NOTICE, - 83 => Logger::NOTICE, - 183 => Logger::WARNING, - 283 => Logger::WARNING, - 383 => Logger::WARNING, - 483 => Logger::WARNING, - 583 => Logger::WARNING, - 683 => Logger::ERROR, - 783 => Logger::ERROR, - 883 => Logger::ERROR, - 983 => Logger::ERROR, - 1183 => Logger::CRITICAL, - 1383 => Logger::CRITICAL, - 1583 => Logger::CRITICAL, + 3 => Levels::NOTICE, + 23 => Levels::NOTICE, + 43 => Levels::NOTICE, + 63 => Levels::NOTICE, + 83 => Levels::NOTICE, + 183 => Levels::WARNING, + 283 => Levels::WARNING, + 383 => Levels::WARNING, + 483 => Levels::WARNING, + 583 => Levels::WARNING, + 683 => Levels::ERROR, + 783 => Levels::ERROR, + 883 => Levels::ERROR, + 983 => Levels::ERROR, + 1183 => Levels::CRITICAL, + 1383 => Levels::CRITICAL, + 1583 => Levels::CRITICAL, ]; static::assertSame($expectedLoggedLevels, $logged); diff --git a/tests/unit/Data/LogTest.php b/tests/unit/Data/LogTest.php index a6a48bb..a2c79e6 100644 --- a/tests/unit/Data/LogTest.php +++ b/tests/unit/Data/LogTest.php @@ -16,6 +16,7 @@ use Inpsyde\Wonolog\Channels; use Inpsyde\Wonolog\Data\Log; use Inpsyde\Wonolog\Data\LogData; +use Inpsyde\Wonolog\Levels; use Inpsyde\Wonolog\Tests\UnitTestCase; use Monolog\Logger; @@ -26,12 +27,12 @@ class LogTest extends UnitTestCase */ public function testBasicProperties(): void { - $log = new Log('message', Logger::EMERGENCY, Channels::DEBUG, ['foo']); + $log = new Log('message', Levels::EMERGENCY, Channels::DEBUG, ['foo']); static::assertSame(Channels::DEBUG, $log->channel()); static::assertSame('message', $log->message()); static::assertSame(['foo'], $log->context()); - static::assertSame(Logger::EMERGENCY, $log->level()); + static::assertSame(Levels::EMERGENCY, $log->level()); } /** @@ -50,7 +51,7 @@ public function testFromWpError(): void static::assertSame(Channels::DEBUG, $log->channel()); static::assertSame('Error!', $log->message()); static::assertSame(['!'], $log->context()); - static::assertSame(Logger::NOTICE, $log->level()); + static::assertSame(Levels::NOTICE, $log->level()); } /** @@ -64,12 +65,12 @@ public function testFromWpErrorWithExplicitLevel(): void $error->allows('get_error_data')->andReturn(['!']); $error->allows('get_error_codes')->andReturn(['x']); - $log = Log::fromWpError($error, Logger::DEBUG); + $log = Log::fromWpError($error, Levels::DEBUG); static::assertSame(Channels::DEBUG, $log->channel()); static::assertSame('Error!', $log->message()); static::assertSame(['!'], $log->context()); - static::assertSame(Logger::DEBUG, $log->level()); + static::assertSame(Levels::DEBUG, $log->level()); } /** @@ -83,12 +84,12 @@ public function testFromWpErrorWithExplicitLevelAndChannel(): void $error->allows('get_error_data')->andReturn(['!']); $error->allows('get_error_codes')->andReturn(['x']); - $log = Log::fromWpError($error, Logger::DEBUG, Channels::DB); + $log = Log::fromWpError($error, Levels::DEBUG, Channels::DB); static::assertSame(Channels::DB, $log->channel()); static::assertSame('Error!', $log->message()); static::assertSame(['!'], $log->context()); - static::assertSame(Logger::NOTICE, $log->level()); + static::assertSame(Levels::NOTICE, $log->level()); } /** @@ -106,7 +107,7 @@ public function testFromThrowable(): void static::assertSame(Channels::DEBUG, $log->channel()); static::assertSame('Fail!, Fail!', $log->message()); - static::assertSame(Logger::ERROR, $log->level()); + static::assertSame(Levels::ERROR, $log->level()); static::assertArrayHasKey('throwable', $context); static::assertSame($context['throwable']['class'], \Exception::class); static::assertSame($context['throwable']['file'], __FILE__); @@ -121,7 +122,7 @@ public function testFromThrowableWithExplicitLevel(): void { $exception = new \Exception('Fail!, Fail!', 123); - $log = Log::fromThrowable($exception, Logger::DEBUG); + $log = Log::fromThrowable($exception, Levels::DEBUG); static::assertInstanceOf(Log::class, $log); $context = $log->context(); @@ -129,7 +130,7 @@ public function testFromThrowableWithExplicitLevel(): void static::assertSame(Channels::DEBUG, $log->channel()); static::assertSame('Fail!, Fail!', $log->message()); - static::assertSame(Logger::DEBUG, $log->level()); + static::assertSame(Levels::DEBUG, $log->level()); static::assertArrayHasKey('throwable', $context); static::assertSame($context['throwable']['class'], \Exception::class); static::assertSame($context['throwable']['file'], __FILE__); @@ -144,7 +145,7 @@ public function testFromThrowableWithExplicitLevelAndChannel(): void { $exception = new \Exception('Fail!, Fail!', 123); - $log = Log::fromThrowable($exception, Logger::NOTICE, Channels::HTTP); + $log = Log::fromThrowable($exception, Levels::NOTICE, Channels::HTTP); static::assertInstanceOf(Log::class, $log); $context = $log->context(); @@ -152,7 +153,7 @@ public function testFromThrowableWithExplicitLevelAndChannel(): void static::assertSame(Channels::HTTP, $log->channel()); static::assertSame('Fail!, Fail!', $log->message()); - static::assertSame(Logger::NOTICE, $log->level()); + static::assertSame(Levels::NOTICE, $log->level()); static::assertArrayHasKey('throwable', $context); static::assertSame($context['throwable']['class'], \Exception::class); static::assertSame($context['throwable']['file'], __FILE__); @@ -168,7 +169,7 @@ public function testFromArray(): void $log = Log::fromArray( [ LogData::MESSAGE => 'message', - LogData::LEVEL => Logger::EMERGENCY, + LogData::LEVEL => Levels::EMERGENCY, LogData::CHANNEL => Channels::HTTP, LogData::CONTEXT => ['foo'], ] @@ -177,7 +178,7 @@ public function testFromArray(): void static::assertSame(Channels::HTTP, $log->channel()); static::assertSame('message', $log->message()); static::assertSame(['foo'], $log->context()); - static::assertSame(Logger::EMERGENCY, $log->level()); + static::assertSame(Levels::EMERGENCY, $log->level()); } /** @@ -195,6 +196,6 @@ public function testFromArrayMerged(): void static::assertSame(Channels::DEBUG, $log->channel()); static::assertSame('message', $log->message()); static::assertSame(['foo'], $log->context()); - static::assertSame(Logger::DEBUG, $log->level()); + static::assertSame(Levels::DEBUG, $log->level()); } } diff --git a/tests/unit/DefaultHandler/PassthroughFormatterTest.php b/tests/unit/DefaultHandler/PassthroughFormatterTest.php new file mode 100644 index 0000000..2e4e648 --- /dev/null +++ b/tests/unit/DefaultHandler/PassthroughFormatterTest.php @@ -0,0 +1,64 @@ + 'Test log message', + 'context' => ['foo' => 'bar'], + 'level' => 'info', + ]; + + $formatted = $formatter->format($record); + + $this->assertSame($record, $formatted); + } + + public function testFormatReturnsSameLogRecordObjectIfAvailable(): void + { + if (MonologUtils::version() < 3 || !class_exists(LogRecord::class)) { + $this->markTestSkipped('Monolog\LogRecord not available in this version of Monolog.'); + } + + $formatter = new PassthroughFormatter(); + + /** @var LogRecord $logRecord */ + $logRecord = new LogRecord( + datetime: new \DateTimeImmutable(), + channel: 'test', + level: \Monolog\Level::Info, + message: 'A log message', + context: ['foo' => 'bar'], + ); + + $formatted = $formatter->format($logRecord); + + $this->assertSame($logRecord, $formatted); + } + + public function testFormatBatchReturnsSameArray(): void + { + $formatter = new PassthroughFormatter(); + + $records = [ + ['message' => 'First message'], + ['message' => 'Second message'], + ]; + + $formattedBatch = $formatter->formatBatch($records); + + $this->assertSame($records, $formattedBatch); + } +} diff --git a/tests/unit/HookListener/CronDebugListenerTest.php b/tests/unit/HookListener/CronDebugListenerTest.php index 0df22a5..811ccd3 100644 --- a/tests/unit/HookListener/CronDebugListenerTest.php +++ b/tests/unit/HookListener/CronDebugListenerTest.php @@ -15,6 +15,7 @@ use Brain\Monkey; use Inpsyde\Wonolog\Data\LogData; +use Inpsyde\Wonolog\Levels; use Inpsyde\Wonolog\LogActionUpdater; use Inpsyde\Wonolog\Tests\UnitTestCase; use Inpsyde\Wonolog\HookListener\CronDebugListener; @@ -89,11 +90,11 @@ static function ($profileCallback) use (&$cb3): void { ->andReturnUsing( static function (LogData $log) use (&$logs): void { $logs[] = $log->message(); - static::assertSame(Logger::NOTICE, $log->level()); + static::assertSame(Levels::NOTICE, $log->level()); } ); - (new CronDebugListener(Logger::NOTICE))->update('wp_loaded', [], $updater); + (new CronDebugListener(Levels::NOTICE))->update('wp_loaded', [], $updater); static::assertIsCallable($cb1); static::assertIsCallable($cb2); diff --git a/tests/unit/HookListener/HttpApiListenerTest.php b/tests/unit/HookListener/HttpApiListenerTest.php index 309e97f..ef7f19b 100644 --- a/tests/unit/HookListener/HttpApiListenerTest.php +++ b/tests/unit/HookListener/HttpApiListenerTest.php @@ -15,6 +15,7 @@ use Inpsyde\Wonolog\Channels; use Inpsyde\Wonolog\Data\LogData; +use Inpsyde\Wonolog\Levels; use Inpsyde\Wonolog\LogActionUpdater; use Inpsyde\Wonolog\Tests\UnitTestCase; use Inpsyde\Wonolog\HookListener\HttpApiListener; @@ -36,7 +37,7 @@ public function testLogDoneOnWpError(): void static function (LogData $log): void { static::assertSame('WP HTTP API Error: Test!', $log->message()); static::assertSame(Channels::HTTP, $log->channel()); - static::assertSame(Logger::ERROR, $log->level()); + static::assertSame(Levels::ERROR, $log->level()); static::assertSame( [ 'transport' => 'TestClass', @@ -87,7 +88,7 @@ public function testLogDoneOnBadResponse(): void ->andReturnUsing( static function (LogData $log): void { static::assertSame(Channels::HTTP, $log->channel()); - static::assertSame(Logger::ERROR, $log->level()); + static::assertSame(Levels::ERROR, $log->level()); static::assertSame( 'WP HTTP API Error: Internal Server Error - Response code: 500', $log->message() @@ -186,7 +187,7 @@ static function (LogData $log) { static::assertSame('Cron request', $log->message()); static::assertSame(Channels::DEBUG, $log->channel()); - static::assertSame(Logger::DEBUG, $log->level()); + static::assertSame(Levels::DEBUG, $log->level()); static::assertSame( [ 'transport' => 'TestClass', diff --git a/tests/unit/HookListener/MailerListenerTest.php b/tests/unit/HookListener/MailerListenerTest.php index d470d4f..071639d 100644 --- a/tests/unit/HookListener/MailerListenerTest.php +++ b/tests/unit/HookListener/MailerListenerTest.php @@ -18,6 +18,7 @@ use Inpsyde\Wonolog\Data\Debug; use Inpsyde\Wonolog\Data\LogData; use Inpsyde\Wonolog\HookListener\MailerListener; +use Inpsyde\Wonolog\Levels; use Inpsyde\Wonolog\LogActionUpdater; use Inpsyde\Wonolog\Tests\UnitTestCase; use Monolog\Logger; @@ -76,7 +77,7 @@ public function testOnMailFailed(): void ->andReturnUsing( static function (LogData $log): void { static::assertInstanceOf(LogData::class, $log); - static::assertSame(Logger::ERROR, $log->level()); + static::assertSame(Levels::ERROR, $log->level()); static::assertSame(Channels::HTTP, $log->channel()); } ); diff --git a/tests/unit/HookListener/WpDieHandlerListenerTest.php b/tests/unit/HookListener/WpDieHandlerListenerTest.php index 7b74a4e..8715ffc 100644 --- a/tests/unit/HookListener/WpDieHandlerListenerTest.php +++ b/tests/unit/HookListener/WpDieHandlerListenerTest.php @@ -16,6 +16,7 @@ use Inpsyde\Wonolog\Channels; use Inpsyde\Wonolog\Data\LogData; use Inpsyde\Wonolog\HookListener\WpDieHandlerListener; +use Inpsyde\Wonolog\Levels; use Inpsyde\Wonolog\LogActionUpdater; use Inpsyde\Wonolog\Tests\UnitTestCase; use Monolog\Logger; @@ -30,12 +31,12 @@ public function testLogDoneOnBail(): void require_once getenv('TESTS_PATH') . '/stubs/wpdb.php'; $wpdb = new \wpdb('user', 'password', 'db', 'host'); - $wpdb->wp_die_listener = new WpDieHandlerListener(Logger::CRITICAL); + $wpdb->wp_die_listener = new WpDieHandlerListener(Levels::CRITICAL); $updater = \Mockery::mock(LogActionUpdater::class); $updater->expects('update') ->andReturnUsing(static function (LogData $log): void { - static::assertSame(Logger::CRITICAL, $log->level()); + static::assertSame(Levels::CRITICAL, $log->level()); static::assertSame('Bailed!', $log->message()); static::assertSame(Channels::DB, $log->channel()); }); @@ -58,7 +59,7 @@ public function testLogDoneOnPrintError(): void $updater = \Mockery::mock(LogActionUpdater::class); $updater->expects('update') ->andReturnUsing(static function (LogData $log): void { - static::assertSame(Logger::CRITICAL, $log->level()); + static::assertSame(Levels::CRITICAL, $log->level()); static::assertSame('Error!', $log->message()); static::assertSame(Channels::DB, $log->channel()); }); diff --git a/tests/unit/HookLogFactoryTest.php b/tests/unit/HookLogFactoryTest.php index 174b328..15acbd5 100644 --- a/tests/unit/HookLogFactoryTest.php +++ b/tests/unit/HookLogFactoryTest.php @@ -19,6 +19,7 @@ use Inpsyde\Wonolog\Data\Error; use Inpsyde\Wonolog\Data\LogData; use Inpsyde\Wonolog\HookLogFactory; +use Inpsyde\Wonolog\Levels; use Inpsyde\Wonolog\Tests\UnitTestCase; use Monolog\Logger; use Psr\Log\LogLevel; @@ -83,19 +84,19 @@ public function testLogsFromArgumentsReturnsGivenLogDataWithRaisedLevel(): void $args = compact('first', 'second'); $factory = HookLogFactory::new(); - $logs = $factory->logsFromHookArguments($args, Logger::WARNING); + $logs = $factory->logsFromHookArguments($args, Levels::WARNING); static::assertIsArray($logs); static::assertCount(2, $logs); static::assertInstanceOf(LogData::class, $first); - static::assertSame(Logger::WARNING, $logs[0]->level()); + static::assertSame(Levels::WARNING, $logs[0]->level()); static::assertSame($logs[0]->message(), $first->message()); static::assertSame($logs[0]->channel(), $first->channel()); static::assertSame($logs[0]->context(), $first->context()); static::assertInstanceOf(LogData::class, $second); - static::assertSame(Logger::ERROR, $logs[1]->level()); + static::assertSame(Levels::ERROR, $logs[1]->level()); static::assertSame($logs[1]->message(), $second->message()); static::assertSame($logs[1]->channel(), $second->channel()); static::assertSame($logs[1]->context(), $second->context()); @@ -141,7 +142,7 @@ public function testLogsFromWpError(): void static::assertInstanceOf(LogData::class, $log); static::assertSame($log->message(), 'Foo!'); - static::assertSame($log->level(), Logger::NOTICE); + static::assertSame($log->level(), Levels::NOTICE); static::assertSame($log->channel(), Channels::DB); static::assertSame($log->context(), ['db broken']); } @@ -170,7 +171,7 @@ public function testLogsFromWpErrorAndChannelInErrorData(): void static::assertInstanceOf(LogData::class, $log); static::assertSame('Error!', $log->message()); - static::assertSame(Logger::ERROR, $log->level()); + static::assertSame(Levels::ERROR, $log->level()); static::assertSame(Channels::SECURITY, $log->channel()); static::assertSame(['some', 'data'], $log->context()); } @@ -193,7 +194,7 @@ public function testLogsFromThrowable(): void static::assertInstanceOf(LogData::class, $log); static::assertSame($log->message(), 'Foo!'); - static::assertSame($log->level(), Logger::ERROR); + static::assertSame($log->level(), Levels::ERROR); static::assertSame($log->channel(), Channels::DEBUG); static::assertIsArray($log->context()); } @@ -205,13 +206,13 @@ public function testLogsFromArray(): void { $data = [ 'message' => 'Hello!', - 'level' => Logger::NOTICE, + 'level' => Levels::NOTICE, 'channel' => Channels::SECURITY, 'context' => ['foo', 'bar'], ]; $factory = HookLogFactory::new(); - $logs = $factory->logsFromHookArguments([$data, 'x', 'y'], Logger::DEBUG); + $logs = $factory->logsFromHookArguments([$data, 'x', 'y'], Levels::DEBUG); static::assertIsArray($logs); static::assertCount(1, $logs); @@ -221,7 +222,7 @@ public function testLogsFromArray(): void static::assertInstanceOf(LogData::class, $log); static::assertSame($log->message(), 'Hello!'); - static::assertSame($log->level(), Logger::NOTICE); + static::assertSame($log->level(), Levels::NOTICE); static::assertSame($log->channel(), Channels::SECURITY); static::assertIsArray(['foo', 'bar']); } @@ -233,13 +234,13 @@ public function testLogsFromArrayAndRaisedLevel(): void { $data = [ 'message' => 'Hello!', - 'level' => Logger::DEBUG, + 'level' => Levels::DEBUG, 'channel' => Channels::SECURITY, 'context' => ['foo', 'bar'], ]; $factory = HookLogFactory::new(); - $logs = $factory->logsFromHookArguments([$data, 600, 'y'], Logger::NOTICE); + $logs = $factory->logsFromHookArguments([$data, 600, 'y'], Levels::NOTICE); static::assertIsArray($logs); static::assertCount(1, $logs); @@ -249,7 +250,7 @@ public function testLogsFromArrayAndRaisedLevel(): void static::assertInstanceOf(LogData::class, $log); static::assertSame($log->message(), 'Hello!'); - static::assertSame($log->level(), Logger::NOTICE); + static::assertSame($log->level(), Levels::NOTICE); static::assertSame($log->channel(), Channels::SECURITY); static::assertIsArray(['foo', 'bar']); } @@ -266,12 +267,12 @@ public function testLogsFromArrayDifferentLevelFormat(): void $monologNumData = [ 'message' => 'Monolog numeric level format', - 'level' => Logger::NOTICE, + 'level' => Levels::NOTICE, ]; $monologStringData = [ 'message' => 'Monolog string level format', - 'level' => Logger::getLevelName(Logger::INFO), + 'level' => Logger::getLevelName(Levels::INFO), ]; $factory = HookLogFactory::new(); @@ -279,8 +280,8 @@ public function testLogsFromArrayDifferentLevelFormat(): void $monologNum = $factory->logsFromHookArguments([$monologNumData]); $monologString = $factory->logsFromHookArguments([$monologStringData]); - static::assertSame($psr[0]->level(), Logger::WARNING); - static::assertSame($monologNum[0]->level(), Logger::NOTICE); - static::assertSame($monologString[0]->level(), Logger::INFO); + static::assertSame($psr[0]->level(), Levels::WARNING); + static::assertSame($monologNum[0]->level(), Levels::NOTICE); + static::assertSame($monologString[0]->level(), Levels::INFO); } } diff --git a/tests/unit/LevelsTest.php b/tests/unit/LevelsTest.php new file mode 100644 index 0000000..bc87fc8 --- /dev/null +++ b/tests/unit/LevelsTest.php @@ -0,0 +1,17 @@ + 'bar']); + $log = new Log('Test me', Levels::EMERGENCY, Channels::SECURITY, ['foo' => 'bar']); $logger = new TestLogger(); @@ -143,7 +144,7 @@ public function testUpdateLogsUsingPsrLoggerWithMaskedInput(): void ] ]; - $log = new Log('Test me', Logger::EMERGENCY, Channels::SECURITY, $context); + $log = new Log('Test me', Levels::EMERGENCY, Channels::SECURITY, $context); $logger = new TestLogger(); diff --git a/tests/unit/LogLevelTest.php b/tests/unit/LogLevelTest.php index c41c10b..859fb3b 100644 --- a/tests/unit/LogLevelTest.php +++ b/tests/unit/LogLevelTest.php @@ -13,6 +13,7 @@ namespace Inpsyde\Wonolog\Tests\Unit; +use Inpsyde\Wonolog\Levels; use Inpsyde\Wonolog\LogLevel; use Inpsyde\Wonolog\Tests\UnitTestCase; use Monolog\Logger; @@ -38,7 +39,7 @@ public function testDefaultLevelByEnvString(): void { putenv('WONOLOG_DEFAULT_MIN_LEVEL=CRITICAL'); - static::assertSame(Logger::CRITICAL, LogLevel::defaultMinLevel()); + static::assertSame(Levels::CRITICAL, LogLevel::defaultMinLevel()); } /** @@ -48,7 +49,7 @@ public function testDefaultLevelByEnvNum(): void { putenv('WONOLOG_DEFAULT_MIN_LEVEL=500'); - static::assertSame(Logger::CRITICAL, LogLevel::defaultMinLevel()); + static::assertSame(Levels::CRITICAL, LogLevel::defaultMinLevel()); } /** @@ -56,7 +57,7 @@ public function testDefaultLevelByEnvNum(): void */ public function testDefaultLevelByConstantNone(): void { - static::assertSame(Logger::WARNING, LogLevel::defaultMinLevel()); + static::assertSame(Levels::WARNING, LogLevel::defaultMinLevel()); } /** @@ -66,7 +67,7 @@ public function testDefaultLevelByConstantLog(): void { define('WP_DEBUG_LOG', true); - static::assertSame(Logger::DEBUG, LogLevel::defaultMinLevel()); + static::assertSame(Levels::DEBUG, LogLevel::defaultMinLevel()); } /** @@ -77,7 +78,7 @@ public function testDefaultLevelByConstantDebug(): void define('WP_DEBUG', true); static::assertFalse(defined('WP_DEBUG_LOG')); - static::assertSame(Logger::DEBUG, LogLevel::defaultMinLevel()); + static::assertSame(Levels::DEBUG, LogLevel::defaultMinLevel()); } /** @@ -88,7 +89,7 @@ public function testDefaultLevelByConstantLogFalse(): void define('WP_DEBUG_LOG', false); define('WP_DEBUG', true); - static::assertSame(Logger::WARNING, LogLevel::defaultMinLevel()); + static::assertSame(Levels::WARNING, LogLevel::defaultMinLevel()); } /** @@ -100,7 +101,7 @@ public function testDefaultLevelByEnvOverConstants(): void define('WP_DEBUG_LOG', false); define('WP_DEBUG', true); - static::assertSame(Logger::EMERGENCY, LogLevel::defaultMinLevel()); + static::assertSame(Levels::EMERGENCY, LogLevel::defaultMinLevel()); } /** @@ -123,14 +124,14 @@ public function testNormalizeLevelNormalizeToClosestValidLevel(): void */ public function testCheckLevelAcceptsDefinedLevelStrings(): void { - static::assertSame(Logger::CRITICAL, LogLevel::normalizeLevel('CRITICAL')); - static::assertSame(Logger::ERROR, LogLevel::normalizeLevel('error')); - static::assertSame(Logger::DEBUG, LogLevel::normalizeLevel('Debug')); - static::assertSame(Logger::ALERT, LogLevel::normalizeLevel('aLeRt')); - static::assertSame(Logger::EMERGENCY, LogLevel::normalizeLevel('emeRGEncy')); - static::assertSame(Logger::INFO, LogLevel::normalizeLevel(' INFO ')); - static::assertSame(Logger::NOTICE, LogLevel::normalizeLevel(' nOtiCE')); - static::assertSame(Logger::WARNING, LogLevel::normalizeLevel('Warning ')); + static::assertSame(Levels::CRITICAL, LogLevel::normalizeLevel('CRITICAL')); + static::assertSame(Levels::ERROR, LogLevel::normalizeLevel('error')); + static::assertSame(Levels::DEBUG, LogLevel::normalizeLevel('Debug')); + static::assertSame(Levels::ALERT, LogLevel::normalizeLevel('aLeRt')); + static::assertSame(Levels::EMERGENCY, LogLevel::normalizeLevel('emeRGEncy')); + static::assertSame(Levels::INFO, LogLevel::normalizeLevel(' INFO ')); + static::assertSame(Levels::NOTICE, LogLevel::normalizeLevel(' nOtiCE')); + static::assertSame(Levels::WARNING, LogLevel::normalizeLevel('Warning ')); static::assertNull(LogLevel::normalizeLevel('MEH')); } } diff --git a/tests/unit/PhpErrorHandlerTest.php b/tests/unit/PhpErrorHandlerTest.php index b2c6a69..6304f18 100644 --- a/tests/unit/PhpErrorHandlerTest.php +++ b/tests/unit/PhpErrorHandlerTest.php @@ -15,6 +15,7 @@ use Inpsyde\Wonolog\Channels; use Inpsyde\Wonolog\Data\LogData; +use Inpsyde\Wonolog\Levels; use Inpsyde\Wonolog\LogActionUpdater; use Inpsyde\Wonolog\PhpErrorController; use Inpsyde\Wonolog\Tests\UnitTestCase; @@ -42,7 +43,7 @@ public function testOnErrorNotice(): void $updater->expects('update')->andReturnUsing( static function (LogData $log): void { static::assertSame(Channels::PHP_ERROR, $log->channel()); - static::assertSame(Logger::NOTICE, $log->level()); + static::assertSame(Levels::NOTICE, $log->level()); static::assertSame('Meh!', $log->message()); $context = $log->context(); static::assertArrayHasKey('line', $context); @@ -66,7 +67,7 @@ public function testOnErrorFatal(): void $updater->expects('update')->andReturnUsing( static function (LogData $log): void { static::assertSame(Channels::PHP_ERROR, $log->channel()); - static::assertSame(Logger::WARNING, $log->level()); + static::assertSame(Levels::WARNING, $log->level()); static::assertSame('Warning!', $log->message()); $context = $log->context(); static::assertArrayHasKey('line', $context); @@ -90,7 +91,7 @@ public function testOnException(): void $updater->expects('update')->andReturnUsing( static function (LogData $log): void { static::assertSame(Channels::PHP_ERROR, $log->channel()); - static::assertSame(Logger::CRITICAL, $log->level()); + static::assertSame(Levels::CRITICAL, $log->level()); static::assertSame('Exception!', $log->message()); $context = $log->context(); static::assertArrayHasKey('line', $context); diff --git a/tests/unit/Processor/NullProcessorTest.php b/tests/unit/Processor/NullProcessorTest.php new file mode 100644 index 0000000..f9f17eb --- /dev/null +++ b/tests/unit/Processor/NullProcessorTest.php @@ -0,0 +1,77 @@ +justReturn(); + } + + public function testProcessesLogRecordCorrectly(): void + { + if (MonologUtils::version() < 3) { + $this->markTestSkipped('We support LogRecord from monolog 3'); + } + $processor = new NullProcessor(); + $message = 'mymessage'; + $level = Levels::ERROR; + $channel = 'mychannel'; + $context = [ + 'foo' => 'bar', + ]; + /** @var LogRecord $record */ + $record = new LogRecord( + new \DateTimeImmutable(), + $channel, + /** @phpstan-ignore-next-line class.notFound */ + Level::fromValue($level), + $message, + $context + ); + /** @var LogRecord $processedRecord */ + $processedRecord = $processor($record); + + static::assertInstanceOf(LogRecord::class, $processor($record)); + static::assertEquals($processedRecord->context, $context); + static::assertEquals($processedRecord->message, $message); + static::assertEquals($processedRecord->channel, $channel); + static::assertInstanceOf(Level::class, $record->level); + static::assertEquals($processedRecord->level->value, $level); + } + + public function testProcessesArrayCorrectly(): void + { + $processor = new NullProcessor(); + $message = 'mymessage'; + $level = Levels::ERROR; + $channel = 'mychannel'; // TODO: should we add this to the Record? looking for symmetry with LogRecord Model + $context = [ + 'foo' => 'bar', + ]; + /** @var array $record */ + $record = compact('message', 'context', 'level'); + /** @var array $record */ + $processedRecord = $processor($record); + + static::assertIsArray($processedRecord); + static::assertEquals($processedRecord['message'], $message); + static::assertEquals($processedRecord['context'], $context); + static::assertEquals($processedRecord['level'], $level); + } +} diff --git a/tests/unit/Processor/WpContextProcessorTest.php b/tests/unit/Processor/WpContextProcessorTest.php index 4c2940d..18928a8 100644 --- a/tests/unit/Processor/WpContextProcessorTest.php +++ b/tests/unit/Processor/WpContextProcessorTest.php @@ -14,8 +14,12 @@ namespace Inpsyde\Wonolog\Tests\Unit\Processor; use Brain\Monkey\Functions; +use Inpsyde\Wonolog\Levels; +use Inpsyde\Wonolog\MonologUtils; use Inpsyde\Wonolog\Processor\WpContextProcessor; +use Inpsyde\Wonolog\RecordFactory; use Inpsyde\Wonolog\Tests\UnitTestCase; +use Monolog\LogRecord; class WpContextProcessorTest extends UnitTestCase { @@ -31,7 +35,7 @@ protected function setUp(): void /** * @test */ - public function testAdminBeforeInitSingleSite():void + public function testAdminBeforeInitSingleSite(): void { Functions\when('is_admin')->justReturn(true); Functions\when('is_multisite')->justReturn(false); @@ -86,6 +90,11 @@ public function testFrontendBeforeInitSingleSite(): void $this->assertEquals($expected, $actual); } + private function whenRecordIsTestMessage(string $type): string + { + return 'when $record is of type ' . $type; + } + /** * @test */ @@ -102,21 +111,43 @@ public function testAdminAfterInitSingleSite(): void $processor = WpContextProcessor::new(); - $actual = $processor([]); - $expected = [ - 'extra' => [ - 'wp' => [ - 'doing_cron' => false, - 'doing_ajax' => false, - 'is_admin' => true, - 'doing_rest' => false, - 'user_id' => 1, - ], + 'wp' => [ + 'doing_cron' => false, + 'doing_ajax' => false, + 'is_admin' => true, + 'doing_rest' => false, + 'user_id' => 1, ], ]; - $this->assertEquals($expected, $actual); + $processedRecord = $processor([]); + $this->assertEquals( + $expected, + $processedRecord['extra'], + $this->whenRecordIsTestMessage('array') + ); + if (MonologUtils::version() < 3) { + return; + } + $processedRecord = $processor($this->buildLogRecord()); + $this->assertEquals( + $expected, + $processedRecord->extra, + $this->whenRecordIsTestMessage(LogRecord::class) + ); + } + + /** + * @return LogRecord + */ + private static function buildLogRecord(): LogRecord + { + return RecordFactory::createRecordV3( + 'foo log msg', + Levels::DEBUG, + 'default' + ); } /** @@ -140,21 +171,31 @@ static function (string $str): string { $processor = WpContextProcessor::new(); - $actual = $processor([]); - $expected = [ - 'extra' => [ - 'wp' => [ - 'doing_cron' => false, - 'doing_ajax' => false, - 'is_admin' => false, - 'doing_rest' => true, - 'user_id' => 1, - ], + 'wp' => [ + 'doing_cron' => false, + 'doing_ajax' => false, + 'is_admin' => false, + 'doing_rest' => true, + 'user_id' => 1, ], ]; - $this->assertEquals($expected, $actual); + $processedRecord = $processor([]); + $this->assertEquals( + $expected, + $processedRecord['extra'], + $this->whenRecordIsTestMessage('array') + ); + if (MonologUtils::version() < 3) { + return; + } + $processedRecord = $processor($this->buildLogRecord()); + $this->assertEquals( + $expected, + $processedRecord->extra, + $this->whenRecordIsTestMessage(LogRecord::class) + ); } /** @@ -174,21 +215,31 @@ public function testFrontendAfterParseRequestSingleSite(): void $processor = WpContextProcessor::new(); - $actual = $processor([]); - $expected = [ - 'extra' => [ - 'wp' => [ - 'doing_cron' => false, - 'doing_ajax' => false, - 'is_admin' => false, - 'doing_rest' => false, - 'user_id' => 1, - ], + 'wp' => [ + 'doing_cron' => false, + 'doing_ajax' => false, + 'is_admin' => false, + 'doing_rest' => false, + 'user_id' => 1, ], ]; - $this->assertEquals($expected, $actual); + $processedRecord = $processor([]); + $this->assertEquals( + $expected, + $processedRecord['extra'], + $this->whenRecordIsTestMessage('array') + ); + if (MonologUtils::version() < 3) { + return; + } + $processedRecord = $processor($this->buildLogRecord()); + $this->assertEquals( + $expected, + $processedRecord->extra, + $this->whenRecordIsTestMessage(LogRecord::class) + ); } public function testFrontendAfterParseRequestMultiSite(): void @@ -206,23 +257,33 @@ public function testFrontendAfterParseRequestMultiSite(): void $processor = WpContextProcessor::new(); - $actual = $processor([]); - $expected = [ - 'extra' => [ - 'wp' => [ - 'doing_cron' => false, - 'doing_ajax' => false, - 'is_admin' => false, - 'doing_rest' => false, - 'user_id' => 1, - 'ms_switched' => true, - 'site_id' => 2, - 'network_id' => 3, - ], + 'wp' => [ + 'doing_cron' => false, + 'doing_ajax' => false, + 'is_admin' => false, + 'doing_rest' => false, + 'user_id' => 1, + 'ms_switched' => true, + 'site_id' => 2, + 'network_id' => 3, ], ]; - $this->assertEquals($expected, $actual); + $processedRecord = $processor([]); + $this->assertEquals( + $expected, + $processedRecord['extra'], + $this->whenRecordIsTestMessage('array') + ); + if (MonologUtils::version() < 3) { + return; + } + $processedRecord = $processor($this->buildLogRecord()); + $this->assertEquals( + $expected, + $processedRecord->extra, + $this->whenRecordIsTestMessage(LogRecord::class) + ); } } diff --git a/tests/unit/RecordFactoryTest.php b/tests/unit/RecordFactoryTest.php new file mode 100644 index 0000000..6beb951 --- /dev/null +++ b/tests/unit/RecordFactoryTest.php @@ -0,0 +1,40 @@ + 'bar', + ]; + $recordFactory = new RecordFactory(); + $createdRecord = $recordFactory->createRecord($message, $level, $channel, $context); + if (MonologUtils::version() >= 3) { + static::assertInstanceOf(LogRecord::class, $createdRecord); + static::assertEquals($createdRecord->context, $context); + static::assertEquals($createdRecord->message, $message); + static::assertEquals($createdRecord->channel, $channel); + static::assertInstanceOf(Level::class, $createdRecord->level); + static::assertEquals($createdRecord->level->value, $level); + return; + } + static::assertIsArray($createdRecord); + static::assertEquals($createdRecord['message'], $message); + static::assertEquals($createdRecord['context'], $context); + static::assertEquals($createdRecord['level'], $level); + } +}