Adding Logger Tests

This commit is contained in:
Philipp Holzer 2019-03-04 23:39:14 +01:00 committed by Hypolite Petovan
parent 7bebb03f95
commit aee348fa02
9 changed files with 447 additions and 82 deletions

View file

@ -115,7 +115,6 @@ class StreamLogger extends AbstractLogger
$this->checkStream();
$this->stream = fopen($this->url, 'a');
$formattedLog = $this->formatLog($level, $message, $context);
fwrite($this->stream, $formattedLog);
}

View file

@ -4,7 +4,6 @@ namespace Friendica\Util\Logger;
use Friendica\Network\HTTPException\InternalServerErrorException;
use Friendica\Util\Introspection;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel;
/**
@ -117,7 +116,7 @@ class SyslogLogger extends AbstractLogger
public function mapLevelToPriority($level)
{
if (!array_key_exists($level, $this->logLevels)) {
throw new InvalidArgumentException('LogLevel \'' . $level . '\' isn\'t valid.');
throw new \InvalidArgumentException(sprintf('The level "%s" is not valid.', $level));
}
return $this->logLevels[$level];
@ -146,7 +145,7 @@ class SyslogLogger extends AbstractLogger
throw new InternalServerErrorException('Can\'t open syslog for ident "' . $this->channel . '" and facility "' . $this->logFacility . '""');
}
syslog($priority, $message);
$this->syslogWrapper($priority, $message);
}
/**
@ -172,4 +171,15 @@ class SyslogLogger extends AbstractLogger
return $logMessage;
}
/**
* A syslog wrapper to make syslog functionality testable
*
* @param int $level The syslog priority
* @param string $entry The message to send to the syslog function
*/
protected function syslogWrapper($level, $entry)
{
syslog($level, $entry);
}
}

View file

@ -0,0 +1,143 @@
<?php
namespace Friendica\Test\src\Util\Logger;
use Friendica\Test\MockedTest;
use Friendica\Util\Introspection;
use Mockery\MockInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
abstract class AbstractLoggerTest extends MockedTest
{
use LoggerDataTrait;
const LOGLINE = '/.* \[.*\]: .* \{.*\"file\":\".*\".*,.*\"line\":\d*,.*\"function\":\".*\".*,.*\"uid\":\".*\".*}/';
const FILE = 'test';
const LINE = 666;
const FUNC = 'myfunction';
/**
* @var Introspection|MockInterface
*/
protected $introspection;
/**
* Returns the content of the current logger instance
*
* @return string
*/
abstract protected function getContent();
/**
* Returns the current logger instance
*
* @param string $level the default loglevel
*
* @return LoggerInterface
*/
abstract protected function getInstance($level = LogLevel::DEBUG);
protected function setUp()
{
parent::setUp();
$this->introspection = \Mockery::mock(Introspection::class);
$this->introspection->shouldReceive('getRecord')->andReturn([
'file' => self::FILE,
'line' => self::LINE,
'function' => self::FUNC
]);
}
public function assertLogline($string)
{
$this->assertRegExp(self::LOGLINE, $string);
}
public function assertLoglineNums($assertNum, $string)
{
$this->assertEquals($assertNum, preg_match_all(self::LOGLINE, $string));
}
/**
* Test if the logger works correctly
*/
public function testNormal()
{
$logger = $this->getInstance();
$logger->emergency('working!');
$logger->alert('working too!');
$logger->debug('and now?');
$logger->notice('message', ['an' => 'context']);
$text = $this->getContent();
$this->assertLogline($text);
$this->assertLoglineNums(4, $text);
}
/**
* Test if a log entry is correctly interpolated
*/
public function testPsrInterpolate()
{
$logger = $this->getInstance();
$logger->emergency('A {psr} test', ['psr' => 'working']);
$logger->alert('An {array} test', ['array' => ['it', 'is', 'working']]);
$text = $this->getContent();
$this->assertContains('A working test', $text);
$this->assertContains('An ["it","is","working"] test', $text);
}
/**
* Test if a log entry contains all necessary information
*/
public function testContainsInformation()
{
$logger = $this->getInstance();
$logger->emergency('A test');
$text = $this->getContent();
$this->assertContains('"file":"' . self::FILE . '"', $text);
$this->assertContains('"line":' . self::LINE, $text);
$this->assertContains('"function":"' . self::FUNC . '"', $text);
}
/**
* Test if the minimum level is working
*/
public function testMinimumLevel()
{
$logger = $this->getInstance(LogLevel::NOTICE);
$logger->emergency('working');
$logger->alert('working');
$logger->error('working');
$logger->warning('working');
$logger->notice('working');
$logger->info('not working');
$logger->debug('not working');
$text = $this->getContent();
$this->assertLoglineNums(5, $text);
}
/**
* Test with different logging data
* @dataProvider dataTests
*/
public function testDifferentTypes($function, $message, array $context)
{
$logger = $this->getInstance();
$logger->$function($message, $context);
$text = $this->getContent();
$this->assertLogline($text);
$this->assertContains(@json_encode($context), $text);
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace Friendica\Test\src\Util\Logger;
trait LoggerDataTrait
{
public function dataTests()
{
return [
'emergency' => [
'function' => 'emergency',
'message' => 'test',
'context' => ['a' => 'context'],
],
'alert' => [
'function' => 'alert',
'message' => 'test {test}',
'context' => ['a' => 'context', 2 => 'so', 'test' => 'works'],
],
'critical' => [
'function' => 'critical',
'message' => 'test crit 2345',
'context' => ['a' => 'context', 'wit' => ['more', 'array']],
],
'error' => [
'function' => 'error',
'message' => 2.554,
'context' => [],
],
'warning' => [
'function' => 'warning',
'message' => 'test warn',
'context' => ['a' => 'context'],
],
'notice' => [
'function' => 'notice',
'message' => 2346,
'context' => ['a' => 'context'],
],
'info' => [
'function' => 'info',
'message' => null,
'context' => ['a' => 'context'],
],
'debug' => [
'function' => 'debug',
'message' => true,
'context' => ['a' => false],
],
];
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace Friendica\Test\src\Util\Logger;
use Friendica\Test\MockedTest;
use Friendica\Util\Logger\ProfilerLogger;
use Friendica\Util\Profiler;
use Mockery\MockInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
class ProfilerLoggerTest extends MockedTest
{
use LoggerDataTrait;
/**
* @var LoggerInterface|MockInterface
*/
private $logger;
/**
* @var Profiler|MockInterface
*/
private $profiler;
protected function setUp()
{
parent::setUp();
$this->logger = \Mockery::mock(LoggerInterface::class);
$this->profiler = \Mockery::mock(Profiler::class);
}
/**
* Test if the profiler is profiling data
* @dataProvider dataTests
*/
public function testProfiling($function, $message, array $context)
{
$logger = new ProfilerLogger($this->logger, $this->profiler);
$this->logger->shouldReceive($function)->with($message, $context)->once();
$this->profiler->shouldReceive('saveTimestamp')->with(\Mockery::any(), 'file', \Mockery::any())->once();
$logger->$function($message, $context);
}
/**
* Test the log() function
*/
public function testProfilingLog()
{
$logger = new ProfilerLogger($this->logger, $this->profiler);
$this->logger->shouldReceive('log')->with(LogLevel::WARNING, 'test', ['a' => 'context'])->once();
$this->profiler->shouldReceive('saveTimestamp')->with(\Mockery::any(), 'file', \Mockery::any())->once();
$logger->log(LogLevel::WARNING, 'test', ['a' => 'context']);
}
}

View file

@ -2,126 +2,89 @@
namespace Friendica\Test\src\Util\Logger;
use Friendica\Test\MockedTest;
use Friendica\Test\Util\VFSTrait;
use Friendica\Util\Introspection;
use Friendica\Util\Logger\StreamLogger;
use Mockery\MockInterface;
use org\bovigo\vfs\vfsStream;
use org\bovigo\vfs\vfsStreamFile;
use Psr\Log\LogLevel;
class StreamLoggerTest extends MockedTest
class StreamLoggerTest extends AbstractLoggerTest
{
const LOGLINE = '/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} .* \[.*\]: .* \{.*\"file\":\".*\".*,.*\"line\":\d*,.*\"function\":\".*\".*,.*\"uid\":\".*\".*,.*\"process_id\":\d*.*\}/';
const FILE = 'test';
const LINE = 666;
const FUNC = 'myfunction';
use VFSTrait;
/**
* @var Introspection|MockInterface
* @var StreamLogger
*/
private $introspection;
private $logger;
/**
* @var vfsStreamFile
*/
private $logfile;
protected function setUp()
{
parent::setUp();
$this->setUpVfsDir();
$this->introspection = \Mockery::mock(Introspection::class);
$this->introspection->shouldReceive('getRecord')->andReturn([
'file' => self::FILE,
'line' => self::LINE,
'function' => self::FUNC
]);
}
public function assertLogline($string)
{
$this->assertRegExp(self::LOGLINE, $string);
}
public function assertLoglineNums($assertNum, $string)
{
$this->assertEquals($assertNum, preg_match_all(self::LOGLINE, $string));
}
public function testNormal()
{
$logfile = vfsStream::newFile('friendica.log')
->at($this->root);
$logger = new StreamLogger('test', $logfile->url(), $this->introspection);
$logger->emergency('working!');
$logger->alert('working too!');
$logger->debug('and now?');
$logger->notice('message', ['an' => 'context']);
$text = $logfile->getContent();
$this->assertLogline($text);
$this->assertLoglineNums(4, $text);
}
/**
* Test if a log entry is correctly interpolated
* {@@inheritdoc}
*/
public function testPsrInterpolate()
protected function getInstance($level = LogLevel::DEBUG)
{
$logfile = vfsStream::newFile('friendica.log')
$this->logfile = vfsStream::newFile('friendica.log')
->at($this->root);
$logger = new StreamLogger('test', $logfile->url(), $this->introspection);
$this->logger = new StreamLogger('test', $this->logfile->url(), $this->introspection, $level);
$logger->emergency('A {psr} test', ['psr' => 'working']);
$logger->alert('An {array} test', ['array' => ['it', 'is', 'working']]);
$text = $logfile->getContent();
$this->assertContains('A working test', $text);
$this->assertContains('An ["it","is","working"] test', $text);
return $this->logger;
}
/**
* Test if a log entry contains all necessary information
* {@inheritdoc}
*/
public function testContainsInformation()
protected function getContent()
{
$logfile = vfsStream::newFile('friendica.log')
->at($this->root);
$logger = new StreamLogger('test', $logfile->url(), $this->introspection);
$logger->emergency('A test');
$text = $logfile->getContent();
$this->assertContains('"process_id":' . getmypid(), $text);
$this->assertContains('"file":"' . self::FILE . '"', $text);
$this->assertContains('"line":' . self::LINE, $text);
$this->assertContains('"function":"' . self::FUNC . '"', $text);
return $this->logfile->getContent();
}
/**
* Test if the minimum level is working
* Test if a stream is working
*/
public function testMinimumLevel()
public function testStream()
{
$logfile = vfsStream::newFile('friendica.log')
->at($this->root);
$logger = new StreamLogger('test', $logfile->url(), $this->introspection, LogLevel::NOTICE);
$filehandler = fopen($logfile->url(), 'ab');
$logger = new StreamLogger('test', $filehandler, $this->introspection);
$logger->emergency('working');
$logger->alert('working');
$logger->error('working');
$logger->warning('working');
$logger->notice('working');
$logger->info('not working');
$logger->debug('not working');
$text = $logfile->getContent();
$this->assertLoglineNums(5, $text);
$this->assertLogline($text);
}
/**
* Test if the close statement is working
*/
public function testClose()
{
$logfile = vfsStream::newFile('friendica.log')
->at($this->root);
$logger = new StreamLogger('test', $logfile->url(), $this->introspection);
$logger->emergency('working');
$logger->close();
// close doesn't affect
$logger->emergency('working too');
$text = $logfile->getContent();
$this->assertLoglineNums(2, $text);
}
/**
@ -187,4 +150,14 @@ class StreamLoggerTest extends MockedTest
$logger->log('NOPE', 'a test');
}
/**
* Test when the file is null
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage A stream must either be a resource or a string.
*/
public function testWrongFile()
{
$logger = new StreamLogger('test', null, $this->introspection);
}
}

View file

@ -0,0 +1,62 @@
<?php
namespace Friendica\Test\src\Util\Logger;
use Friendica\Util\Logger\SyslogLogger;
use Psr\Log\LogLevel;
class SyslogLoggerTest extends AbstractLoggerTest
{
/**
* @var SyslogLoggerWrapper
*/
private $logger;
protected function setUp()
{
parent::setUp();
$this->introspection->shouldReceive('addClasses')->with([SyslogLogger::class]);
}
/**
* {@inheritdoc}
*/
protected function getContent()
{
return $this->logger->getContent();
}
/**
* {@inheritdoc}
*/
protected function getInstance($level = LogLevel::DEBUG)
{
$this->logger = new SyslogLoggerWrapper('test', $this->introspection, $level);
return $this->logger;
}
/**
* Test when the minimum level is not valid
* @expectedException \InvalidArgumentException
* @expectedExceptionMessageRegExp /The level ".*" is not valid./
*/
public function testWrongMinimumLevel()
{
$logger = new SyslogLoggerWrapper('test', $this->introspection, 'NOPE');
}
/**
* Test when the minimum level is not valid
* @expectedException \InvalidArgumentException
* @expectedExceptionMessageRegExp /The level ".*" is not valid./
*/
public function testWrongLogLevel()
{
$logger = new SyslogLoggerWrapper('test', $this->introspection);
$logger->log('NOPE', 'a test');
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace Friendica\Test\src\Util\Logger;
use Friendica\Util\Introspection;
use Friendica\Util\Logger\SyslogLogger;
use Psr\Log\LogLevel;
class SyslogLoggerWrapper extends SyslogLogger
{
private $content;
public function __construct($channel, Introspection $introspection, $level = LogLevel::NOTICE, $logOpts = LOG_PID, $logFacility = LOG_USER)
{
parent::__construct($channel, $introspection, $level, $logOpts, $logFacility);
$this->content = '';
}
/**
* Gets the content from the wrapped Syslog
*
* @return string
*/
public function getContent()
{
return $this->content;
}
/**
* {@inheritdoc}
*/
protected function syslogWrapper($level, $entry)
{
$this->content .= $entry . PHP_EOL;
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace Friendica\Test\src\Util\Logger;
use Friendica\Test\MockedTest;
use Friendica\Util\Logger\VoidLogger;
use Psr\Log\LogLevel;
class VoidLoggerTest extends MockedTest
{
use LoggerDataTrait;
/**
* Test if the profiler is profiling data
* @dataProvider dataTests
*/
public function testNormal($function, $message, array $context)
{
$logger = new VoidLogger();
$logger->$function($message, $context);
}
/**
* Test the log() function
*/
public function testProfilingLog()
{
$logger = new VoidLogger();
$logger->log(LogLevel::WARNING, 'test', ['a' => 'context']);
}
}