From 46bf3d3e1d121d6ea085f05f3ff400b8e411e00a Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Sat, 6 Oct 2018 10:50:20 +1000 Subject: [PATCH] autobots, roll out --- .gitignore | 2 + composer.json | 40 ++++ license.txt | 21 ++ phpunit.xml | 20 ++ readme.md | 145 ++++++++++++++ src/ChannelFake.php | 28 +++ src/LogFake.php | 401 ++++++++++++++++++++++++++++++++++++++ tests/FakeLogTest.php | 434 ++++++++++++++++++++++++++++++++++++++++++ tests/helpers.php | 42 ++++ 9 files changed, 1133 insertions(+) create mode 100644 .gitignore create mode 100644 composer.json create mode 100644 license.txt create mode 100644 phpunit.xml create mode 100644 readme.md create mode 100644 src/ChannelFake.php create mode 100644 src/LogFake.php create mode 100644 tests/FakeLogTest.php create mode 100644 tests/helpers.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4f4acd3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +vendor/ +composer.lock \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..752fc9e --- /dev/null +++ b/composer.json @@ -0,0 +1,40 @@ +{ + "name": "timacdonald/log-fake", + "description": "A drop in fake logger for testing with the Laravel framework.", + "license": "MIT", + "keywords": [ + "fake", + "log", + "logger", + "testing", + "laravel" + ], + "authors": [ + { + "name": "Tim MacDonald", + "email": "hello@timacdonald.me", + "homepage": "https://timacdonald.me" + } + ], + "require": { + "php": "^7.1.3", + "illuminate/config": "~5.6", + "illuminate/container": "~5.6", + "illuminate/support": "~5.6", + "phpunit/phpunit": "^7.0", + "psr/log": "^1.0" + }, + "autoload": { + "psr-4": { + "TiMacDonald\\Log\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + }, + "files": [ + "tests/helpers.php" + ] + } +} diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..68279fd --- /dev/null +++ b/license.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Tim MacDonald + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..7bdbb9e --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,20 @@ + + + + + ./tests/ + + + + + app/ + + + \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..2ce8827 --- /dev/null +++ b/readme.md @@ -0,0 +1,145 @@ +# Log fake for Laravel + +[![Latest Stable Version](https://poser.pugx.org/timacdonald/log-fake/v/stable)](https://packagist.org/packages/timacdonald/log-fake) [![Total Downloads](https://poser.pugx.org/timacdonald/log-fake/downloads)](https://packagist.org/packages/timacdonald/log-fake) [![License](https://poser.pugx.org/timacdonald/log-fake/license)](https://packagist.org/packages/timacdonald/log-fake) + +A bunch of Laravel facades / services are able to be faked, such as the Dispatcher with `Bus::fake()`, to help with testing and assertions. This package gives you the ability to fake the logger in your app, and includes the ability to make assertions against channels and stacks introduced in logging overhaul in Laravel `5.6`. + +## Installation + +You can install using [composer](https://getcomposer.org/) from [Packagist](https://packagist.org/packages/timacdonald/log-fake) + +``` +$ composer require timacdonald/log-fake +``` + +## Basic usage + +```php +use TiMacDonald\Log\LogFake; +use Illuminate\Support\Facades\Log; + +//... + +Log::swap(new LogFake); + +Log::info('Donuts have arrived'); + +Log::assertLogged('info', function ($message, $context) { + return str_contains($message, 'Donuts'); +}); +``` + +## Channels + +If you are logging to a specific channel in your app, such as Slack with `Log::channel('slack')->critical('It is 5pm, go home')`, you need to also prefix your assertions in the same manner. + +```php +use TiMacDonald\Log\LogFake; +use Illuminate\Support\Facades\Log; + +//... + +Log::swap(new LogFake); + +Log::channel('slack')->alert('It is 5pm, go home'); + +Log::channel('slack')->assertLogged('alert'); // ✅ passes + +// without the channel prefix... + +Log::assertLogged('alert'); // ❌ fails +``` + +## Stacks + +If you are logging to a stack in your app, like with channels, you will need to prefix your assertions. Note that the order of the stack does not matter. + +```php +use TiMacDonald\Log\LogFake; +use Illuminate\Support\Facades\Log; + +//... + +Log::swap(new LogFake); + +Log::stack(['bugsnag', 'sentry'])->critical('Perform evasive maneuvers'); + + +Log::stack(['bugsnag', 'sentry'])->assertLogged('critical'); // ✅ passes + +// without the stack prefix... + +Log::assertLogged('critical'); // ❌ fails +``` + +## Available assertions + +All assertions are relative to the channel or stack as shown in the previous examples. + +### assertLogged($level, $callback = null) + +```php +Log::assertLogged('info'); + +Log::channel('slack')->assertLogged('alert'); + +Log::stack(['bugsnag', 'sentry'])->assertLogged('critical'); + +// with a callback + +Log::assertLogged('info', function ($message, $context) { + return str_contains($message, 'Donuts'); +}); + +Log::channel('slack')->assertLogged('alert', function ($message, $context) { + return str_contains($message, '5pm'); +}); + +Log::stack(['bugsnag', 'sentry'])->assertLogged('critical', function ($message, $context) { + return str_contains($message, 'evasive maneuvers'); +}); +``` + +### assertLoggedTimes($level, $times = 1) + +```php +Log::assertLoggedTimes('info', 5); + +Log::channel('slack')->assertLoggedTimes('alert', 5); + +Log::stack(['bugsnag', 'sentry'])->assertLoggedTimes('critical', 5); +``` + +### assertNotLogged($level, $callback = null) + +```php +Log::assertNotLogged('info'); + +Log::channel('slack')->assertNotLogged('alert'); + +Log::stack(['bugsnag', 'sentry'])->assertNotLogged('critical'); + +// with a callback + +Log::assertNotLogged('info', function ($message, $context) { + return str_contains($message, 'Donuts'); +}); + +Log::channel('slack')->assertNotLogged('alert' , function ($message, $context) { + return str_contains($message, '5pm'); +}); + +Log::stack(['bugsnag', 'sentry'])->assertNotLogged('critical', function ($message, $context) { + return str_contains($message, 'evasive maneuvers'); +}); +``` + +### assertNothingLogged() + +```php +Log::assertNothingLogged('info'); + +Log::channel('slack')->assertNothingLogged('alert'); + +Log::stack(['bugsnag', 'sentry'])->assertNothingLogged('critical'); +``` \ No newline at end of file diff --git a/src/ChannelFake.php b/src/ChannelFake.php new file mode 100644 index 0000000..d480aef --- /dev/null +++ b/src/ChannelFake.php @@ -0,0 +1,28 @@ +log = $log; + + $this->name = $name; + } + + public function __call($method, $arguments) + { + $this->log->setCurrentChannel($this->name); + + $result = $this->log->{$method}(...$arguments); + + $this->log->setCurrentChannel(null); + + return $result; + } +} diff --git a/src/LogFake.php b/src/LogFake.php new file mode 100644 index 0000000..87a6257 --- /dev/null +++ b/src/LogFake.php @@ -0,0 +1,401 @@ +assertLoggedTimes($level, $callback); + } + + PHPUnit::assertTrue( + $this->logged($level, $callback)->count() > 0, + "The expected log with level [{$level}] was not logged in {$this->currentChannel()}." + ); + } + + /** + * Assert if a log was created a number of times. + * + * @param string $level + * @param int $times + * @return void + */ + public function assertLoggedTimes($level, $times = 1) + { + PHPUnit::assertTrue( + ($count = $this->logged($level)->count()) === $times, + "The expected log with level [{$level}] was logged {$count} times instead of {$times} times in {$this->currentChannel()}." + ); + } + + /** + * Determine if a log was not created based on a truth-test callback. + * + * @param string $level + * @param callable|null $callback + * @return void + */ + public function assertNotLogged($level, $callback = null) + { + PHPUnit::assertTrue( + $this->logged($level, $callback)->count() === 0, + "The unexpected log with level [{$level}] was logged in {$this->currentChannel()}." + ); + } + + /** + * Assert that no logs were created. + * + * @return void + */ + public function assertNothingLogged() + { + PHPUnit::assertTrue($this->logsInCurrentChannel()->isEmpty(), "Logs were created in {$this->currentChannel()}."); + } + + /** + * Get all of the logs matching a truth-test callback. + * + * @param string $level + * @param callable|null $callback + * @return \Illuminate\Support\Collection + */ + public function logged($level, $callback = null) + { + $callback = $callback ?: function () { + return true; + }; + + return $this->logsOfLevel($level)->filter(function ($log) use ($callback) { + return $callback($log['message'], $log['context']); + }); + } + + /** + * Determine if the given log level has been created. + * + * @param string $level + * @return bool + */ + public function hasLogged($level) + { + return $this->logsOfLevel($level)->isNotEmpty(); + } + + /** + * Determine if the given log level has not been created. + * + * @param string $level + * @return bool + */ + public function hasNotLogged($level) + { + return ! $this->hasLogged($level); + } + + /** + * Get all of the created logs for a given level. + * + * @param string $type + * @return \Illuminate\Support\Collection + */ + protected function logsOfLevel($level) + { + return $this->logsInCurrentChannel()->filter(function ($log) use ($level) { + return $log['level'] === $level; + }); + } + + /** + * Get all of the created logs for the current channel. + * + * @return \Illuminate\Support\Collection + */ + protected function logsInCurrentChannel() + { + return Collection::make($this->logs)->filter(function ($log) { + return $this->currentChannelIs($log['channel']); + }); + } + + /** + * Log an emergency message to the logs. + * + * @param string $message + * @param array $context + * @return void + */ + public function emergency($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * Log an alert message to the logs. + * + * @param string $message + * @param array $context + * @return void + */ + public function alert($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * Log a critical message to the logs. + * + * @param string $message + * @param array $context + * @return void + */ + public function critical($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * Log an error message to the logs. + * + * @param string $message + * @param array $context + * @return void + */ + public function error($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * Log a warning message to the logs. + * + * @param string $message + * @param array $context + * @return void + */ + public function warning($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * Log a notice to the logs. + * + * @param string $message + * @param array $context + * @return void + */ + public function notice($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * Log an informational message to the logs. + * + * @param string $message + * @param array $context + * @return void + */ + public function info($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * Log a debug message to the logs. + * + * @param string $message + * @param array $context + * @return void + */ + public function debug($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * Log a message to the logs. + * + * @param string $level + * @param string $message + * @param array $context + * @return void + */ + public function log($level, $message, array $context = []) + { + $this->logs[] = [ + 'level' => $level, + 'message' => $message, + 'context' => $context, + 'channel' => $this->currentChannel(), + ]; + } + + /** + * Dynamically pass log calls into the writer. + * + * @param string $level + * @param string $message + * @param array $context + * @return void + */ + public function write($level, $message, array $context = []) + { + $this->log($level, $message, $context); + } + + /** + * Get a log channel instance. + * + * @param string|null $channel + * @return ChannelFake + */ + public function channel($channel = null) + { + return $this->driver($channel); + } + + /** + * Get a log driver instance. + * + * @param string|null $driver + * @return ChannelFake + */ + public function driver($driver = null) + { + return new ChannelFake($this, $driver); + } + + /** + * Create a new, on-demand aggregate logger instance. + * + * @param array $channels + * @param string|null $channel + * @return ChannelFake + */ + public function stack(array $channels, $channel = null) + { + return $this->driver('Stack:'.$this->createStackChannelName($channels, $channel)); + } + + /** + * Create a stack based channel name. + * + * @param array $channels + * @param string|null $channel + * @return string + */ + protected function createStackChannelName($channels, $channel) + { + return collect($channels)->sort()->prepend($channel ?? 'default_testing_stack_channel')->implode('.'); + } + + /** + * Set the current channel being logged to. + * + * @param string $name + * @return void + */ + public function setCurrentChannel($name) + { + $this->currentChannel = $name; + } + + /** + * Get the current channel being logged to. + * + * @return string + */ + public function currentChannel() + { + return $this->currentChannel ?? $this->getDefaultDriver(); + } + + /** + * Determine if in provided channel. + * + * @return string + */ + protected function currentChannelIs($channel) + { + return $this->currentChannel() === $channel; + } + + /** + * Get the default log driver name. + * + * @return string + */ + public function getDefaultDriver() + { + return config('logging.default'); + } + + /** + * Set the default log driver name. + * + * @param string $name + * @return void + */ + public function setDefaultDriver($name) + { + config()->set('logging.default', $name); + } + + /** + * Get the underlying logger implementation. + * + * @return $this + */ + public function getLogger() + { + return $this; + } + + public function listen() + { + // + } + + public function extend() + { + // + } + + public function getEventDispatcher() + { + // + } + + public function setEventDispatcher() + { + // + } +} diff --git a/tests/FakeLogTest.php b/tests/FakeLogTest.php new file mode 100644 index 0000000..4ef5459 --- /dev/null +++ b/tests/FakeLogTest.php @@ -0,0 +1,434 @@ +singleton('config', function () { + return new Config(['logging' => ['default' => 'stack']]); + }); + } + + public function testAssertLogged() + { + $log = new LogFake; + + try { + $log->assertLogged('info'); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The expected log with level [info] was not logged in stack.')); + } + + try { + $log->channel('channel')->assertLogged('info'); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The expected log with level [info] was not logged in channel.')); + } + + try { + $log->stack(['channel'], 'name')->assertLogged('info'); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The expected log with level [info] was not logged in Stack:name.channel.')); + } + } + + public function testAssertLoggedWithNumericCallback() + { + $log = new LogFake; + + try { + $log->assertLogged('info', 1); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The expected log with level [info] was logged 0 times instead of 1 times in stack.')); + } + + try { + $log->channel('channel')->assertLogged('info', 1); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The expected log with level [info] was logged 0 times instead of 1 times in channel.')); + } + + try { + $log->stack(['channel'], 'name')->assertLogged('info', 1); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The expected log with level [info] was logged 0 times instead of 1 times in Stack:name.channel.')); + } + } + + public function testAssertLoggedWithCallback() + { + $log = new LogFake; + + try { + $log->assertLogged('info', function ($message) { + return true; + }); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The expected log with level [info] was not logged in stack.')); + } + + try { + $log->channel('channel')->assertLogged('info', function ($message) { + return true; + }); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The expected log with level [info] was not logged in channel.')); + } + + try { + $log->stack(['channel'], 'name')->assertLogged('info', function ($message) { + return true; + }); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The expected log with level [info] was not logged in Stack:name.channel.')); + } + } + + public function testAssertLoggedTimes() + { + $log = new LogFake; + + try { + $log->assertLoggedTimes('info', 1); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The expected log with level [info] was logged 0 times instead of 1 times in stack.')); + } + + try { + $log->channel('channel')->assertLoggedTimes('info', 1); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The expected log with level [info] was logged 0 times instead of 1 times in channel.')); + } + + try { + $log->stack(['channel'], 'name')->assertLoggedTimes('info', 1); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The expected log with level [info] was logged 0 times instead of 1 times in Stack:name.channel.')); + } + } + + public function testAssertNotLogged() + { + $log = new LogFake; + $log->info($this->message); + $log->channel('channel')->info($this->message); + $log->stack(['channel'], 'name')->info($this->message); + + try { + $log->assertNotLogged('info'); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The unexpected log with level [info] was logged in stack.')); + } + + try { + $log->channel('channel')->assertNotLogged('info'); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The unexpected log with level [info] was logged in channel.')); + } + + try { + $log->stack(['channel'], 'name')->assertNotLogged('info'); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The unexpected log with level [info] was logged in Stack:name.channel.')); + } + } + + public function testAssertNotLoggedWithCallback() + { + $log = new LogFake; + $log->info($this->message); + $log->channel('channel')->info($this->message); + $log->stack(['channel'], 'name')->info($this->message); + + try { + $log->assertNotLogged('info', function ($message) { + return true; + }); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The unexpected log with level [info] was logged in stack.')); + } + + try { + $log->channel('channel')->assertNotLogged('info', function ($message) { + return true; + }); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The unexpected log with level [info] was logged in channel.')); + } + + try { + $log->stack(['channel'], 'name')->assertNotLogged('info', function ($message) { + return true; + }); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('The unexpected log with level [info] was logged in Stack:name.channel.')); + } + } + + public function testAssertNothingLogged() + { + $log = new LogFake; + $log->info($this->message); + $log->channel('channel')->info($this->message); + $log->stack(['channel'], 'name')->info($this->message); + + try { + $log->assertNothingLogged(); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('Logs were created in stack.')); + } + + try { + $log->channel('channel')->assertNothingLogged(); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('Logs were created in channel.')); + } + + try { + $log->stack(['channel'], 'name')->assertNothingLogged(); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertThat($e, new ExceptionMessage('Logs were created in Stack:name.channel.')); + } + } + + public function testLogged() + { + $log = new LogFake; + + $this->assertTrue($log->logged('info')->isEmpty()); + $log->info($this->message); + $this->assertFalse($log->logged('info')->isEmpty()); + + $this->assertTrue($log->channel('channel')->logged('info')->isEmpty()); + $log->channel('channel')->info($this->message); + $this->assertFalse($log->channel('channel')->logged('info')->isEmpty()); + + $this->assertTrue($log->stack(['channel'], 'name')->logged('info')->isEmpty()); + $log->stack(['channel'], 'name')->info($this->message); + $this->assertFalse($log->stack(['channel'], 'name')->logged('info')->isEmpty()); + } + + public function testLoggedWithCallback() + { + $log = new LogFake; + + $this->assertTrue($log->logged('info', function () { + return true; + })->isEmpty()); + $log->info($this->message); + $this->assertFalse($log->logged('info', function () { + return true; + })->isEmpty()); + + $this->assertTrue($log->channel('channel')->logged('info', function () { + return true; + })->isEmpty()); + $log->channel('channel')->info($this->message); + $this->assertFalse($log->channel('channel')->logged('info', function () { + return true; + })->isEmpty()); + + $this->assertTrue($log->stack(['channel'], 'name')->logged('info', function () { + return true; + })->isEmpty()); + $log->stack(['channel'], 'name')->info($this->message); + $this->assertFalse($log->stack(['channel'], 'name')->logged('info', function () { + return true; + })->isEmpty()); + } + + public function testHasLogged() + { + $log = new LogFake; + + $this->assertFalse($log->hasLogged('info')); + $log->info($this->message); + $this->assertTrue($log->hasLogged('info')); + + $this->assertFalse($log->channel('channel')->hasLogged('info')); + $log->channel('channel')->info($this->message); + $this->assertTrue($log->channel('channel')->hasLogged('info')); + + $this->assertFalse($log->stack(['channel'], 'name')->hasLogged('info')); + $log->stack(['channel'], 'name')->info($this->message); + $this->assertTrue($log->stack(['channel'], 'name')->hasLogged('info')); + } + + public function testHasNotLogged() + { + $log = new LogFake; + + $this->assertTrue($log->hasNotLogged('info')); + $log->info($this->message); + $this->assertFalse($log->hasNotLogged('info')); + + $this->assertTrue($log->channel('channel')->hasNotLogged('info')); + $log->channel('channel')->info($this->message); + $this->assertFalse($log->channel('channel')->hasNotLogged('info')); + + $this->assertTrue($log->stack(['channel'], 'name')->hasNotLogged('info')); + $log->stack(['channel'], 'name')->info($this->message); + $this->assertFalse($log->stack(['channel'], 'name')->hasNotLogged('info')); + } + + public function testLoggingLevelMethods() + { + $log = new LogFake; + + $log->emergency('emergency log'); + $log->alert('alert log'); + $log->critical('critical log'); + $log->error('error log'); + $log->warning('warning log'); + $log->info('info log'); + $log->debug('debug log'); + $log->log('custom', 'custom log'); + $log->write('custom_2', 'custom log 2'); + + $log->assertLogged('emergency', function ($message, $context) { + return $message === 'emergency log'; + }); + $log->assertLogged('alert', function ($message, $context) { + return $message === 'alert log'; + }); + $log->assertLogged('critical', function ($message, $context) { + return $message === 'critical log'; + }); + $log->assertLogged('error', function ($message, $context) { + return $message === 'error log'; + }); + $log->assertLogged('warning', function ($message, $context) { + return $message === 'warning log'; + }); + $log->assertLogged('info', function ($message, $context) { + return $message === 'info log'; + }); + $log->assertLogged('debug', function ($message, $context) { + return $message === 'debug log'; + }); + $log->assertLogged('custom', function ($message, $context) { + return $message === 'custom log'; + }); + $log->assertLogged('custom_2', function ($message, $context) { + return $message === 'custom log 2'; + }); + } + + public function assertChannelAndDriverMethodsCanBeUsedInterchangably() + { + $log = new LogFake; + + $log->driver('channel')->info($this->message); + $log->channel('channel')->assertLogged('info', function ($message) { + return true; + }); + } + + public function testCurrentStackIsTakenIntoAccount() + { + $log = new LogFake; + + $log->stack(['bugsnag', 'sentry'], 'dev_team')->info($this->message); + + $log->assertNotLogged('info'); + $log->stack(['bugsnag', 'sentry'], 'dev_team')->assertLogged('info'); + } + + public function testCanHaveStackChannelsInAnyOrder() + { + $log = new LogFake; + + $log->stack(['bugsnag', 'sentry'], 'dev_team')->info($this->message); + + $log->assertNotLogged('info'); + $log->stack(['sentry', 'bugsnag'], 'dev_team')->assertLogged('info'); + } + + public function testDifferentiatesBetweenStacksWithANameAndThoseWithout() + { + $log = new LogFake; + + $log->stack(['bugsnag', 'sentry'], 'dev_team')->info($this->message); + $log->stack(['bugsnag', 'sentry'])->alert($this->message); + + $log->stack(['sentry', 'bugsnag'], 'dev_team')->assertNotLogged('alert'); + $log->stack(['sentry', 'bugsnag'])->assertNotLogged('info'); + } + + public function testDifferentiatesBetweenStacksAndChannelsWithTheSameName() + { + $log = new LogFake; + + $log->stack(['bugsnag', 'sentry'])->info($this->message); + $log->channel('bugsnag.sentry')->alert($this->message); + + $log->stack(['bugsnag', 'sentry'])->assertNotLogged('alert'); + $log->channel('bugsnag.sentry')->assertNotLogged('info'); + + $log->stack(['bugsnag', 'sentry'], 'name')->info($this->message); + $log->channel('name.bugsnag.sentry')->alert($this->message); + + $log->stack(['name', 'bugsnag', 'sentry'])->assertNotLogged('alert'); + $log->channel('name.bugsnag.sentry')->assertNotLogged('info'); + } + + public function testAssertLoggedInStackDotNotatesSortedChannels() + { + $this->assertSame('Stack:name.a.b.c', (new LogFake)->stack(['c','b', 'a'], 'name')->currentChannel()); + } + + public function testClosuresProvideMessageAndContext() + { + $log = new LogFake; + $log->info($this->message, ['key' => 'expected']); + + $items = $log->logged('info', function ($message, $context) { + $this->assertSame(['key' => 'expected'], $context); + return true; + }); + $this->assertTrue($items->isNotEmpty()); + $log->assertLogged('info', function ($message, $context) { + $this->assertSame(['key' => 'expected'], $context); + return true; + }); + $log->assertNotLogged('info', function ($message, $context) { + $this->assertSame(['key' => 'expected'], $context); + return false; + }); + } +} diff --git a/tests/helpers.php b/tests/helpers.php new file mode 100644 index 0000000..8a08a96 --- /dev/null +++ b/tests/helpers.php @@ -0,0 +1,42 @@ +make($abstract, $parameters); + } +} + +if (! function_exists('config')) { + /** + * Get / set the specified configuration value. + * + * If an array is passed as the key, we will assume you want to set an array of values. + * + * @param array|string $key + * @param mixed $default + * @return mixed|\Illuminate\Config\Repository + */ + function config($key = null, $default = null) + { + if (is_null($key)) { + return app('config'); + } + if (is_array($key)) { + return app('config')->set($key); + } + return app('config')->get($key, $default); + } +}