281 lines
9.1 KiB
PHP
281 lines
9.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Logging;
|
|
|
|
use Icefox\DTO\Log;
|
|
use Psr\Log\LogLevel;
|
|
use Psr\Log\NullLogger;
|
|
use Tests\Classes\PrimitiveData;
|
|
use Tests\TestCase;
|
|
|
|
describe('logger resolution', function () {
|
|
|
|
afterEach(function () {
|
|
config()->set('dto.log.logger', NullLogger::class);
|
|
});
|
|
|
|
it('uses NullLogger as fallback when logger config is null', function () {
|
|
config()->set('dto.log.logger', null);
|
|
$log = new Log();
|
|
expect($log->logger)->toBeInstanceOf(NullLogger::class);
|
|
});
|
|
|
|
it('uses NullLogger as fallback when logger config is invalid', function () {
|
|
config()->set('dto.log.logger', 'NonExistentLoggerClass');
|
|
$log = new Log();
|
|
expect($log->logger)->toBeInstanceOf(NullLogger::class);
|
|
});
|
|
|
|
it('instantiates logger from class name via Laravel container', function () {
|
|
config()->set('dto.log.logger', CustomLogger::class);
|
|
$log = new Log();
|
|
expect($log->logger)->toBeInstanceOf(CustomLogger::class);
|
|
});
|
|
|
|
it('uses logger object directly when provided', function () {
|
|
$customLogger = new CustomLogger();
|
|
config()->set('dto.log.logger', $customLogger);
|
|
$log = new Log();
|
|
expect($log->logger)->toBe($customLogger);
|
|
});
|
|
|
|
it('invokes callable to get logger instance', function () {
|
|
config()->set('dto.log.logger', function () {
|
|
return new CustomLogger();
|
|
});
|
|
|
|
$log = new Log();
|
|
|
|
expect($log->logger)->toBeInstanceOf(CustomLogger::class);
|
|
});
|
|
});
|
|
|
|
describe('log level configuration', function () {
|
|
|
|
beforeEach(function () {
|
|
$this->customLogger = new CustomLogger();
|
|
config()->set('dto.log.logger', $this->customLogger);
|
|
});
|
|
|
|
afterEach(function () {
|
|
config()->set('dto.log.logger', NullLogger::class);
|
|
config()->set('dto.log.rules', LogLevel::DEBUG);
|
|
config()->set('dto.log.input', LogLevel::DEBUG);
|
|
config()->set('dto.log.raw_input', LogLevel::DEBUG);
|
|
config()->set('dto.log.validation_errors', LogLevel::INFO);
|
|
});
|
|
|
|
it('logs rules at configured level', function () {
|
|
config()->set('dto.log.rules', LogLevel::INFO);
|
|
|
|
$log = new Log();
|
|
$log->rules(['field' => ['required']]);
|
|
|
|
expect($this->customLogger->hasLog(LogLevel::INFO, 'field'))->toBeTrue();
|
|
});
|
|
|
|
it('logs input at configured level', function () {
|
|
config()->set('dto.log.input', LogLevel::INFO);
|
|
|
|
$log = new Log();
|
|
$log->input(['field' => 'value']);
|
|
|
|
expect($this->customLogger->hasLog(LogLevel::INFO, 'value'))->toBeTrue();
|
|
});
|
|
|
|
it('logs raw input at configured level', function () {
|
|
config()->set('dto.log.raw_input', LogLevel::ERROR);
|
|
|
|
$log = new Log();
|
|
$log->inputRaw(['field' => 'raw_value']);
|
|
|
|
expect($this->customLogger->hasLog(LogLevel::ERROR, 'raw_value'))->toBeTrue();
|
|
});
|
|
|
|
it('logs validation errors at configured level', function () {
|
|
config()->set('dto.log.validation_errors', LogLevel::ERROR);
|
|
|
|
$log = new Log();
|
|
$log->validationErrors(['field' => ['The field is required.']]);
|
|
|
|
expect($this->customLogger->hasLog(LogLevel::ERROR, 'required'))->toBeTrue();
|
|
});
|
|
|
|
it('allows different log levels for each log type', function () {
|
|
config()->set('dto.log.rules', LogLevel::DEBUG);
|
|
config()->set('dto.log.input', LogLevel::INFO);
|
|
config()->set('dto.log.raw_input', LogLevel::INFO);
|
|
|
|
$log = new Log();
|
|
|
|
$log->rules(['rules_field' => ['required']]);
|
|
$log->input(['input_field' => 'value']);
|
|
$log->inputRaw(['raw_field' => 'raw_value']);
|
|
|
|
expect($this->customLogger->hasLog(LogLevel::DEBUG, 'rules_field'))->toBeTrue();
|
|
expect($this->customLogger->hasLog(LogLevel::INFO, 'input_field'))->toBeTrue();
|
|
expect($this->customLogger->hasLog(LogLevel::INFO, 'raw_field'))->toBeTrue();
|
|
});
|
|
|
|
it('defaults to DEBUG level when not configured', function () {
|
|
config()->set('dto.log.rules', null);
|
|
config()->set('dto.log.input', null);
|
|
config()->set('dto.log.raw_input', null);
|
|
|
|
$customLogger = new CustomLogger();
|
|
config()->set('dto.log.logger', $customLogger);
|
|
|
|
$log = new Log();
|
|
|
|
$log->rules(['field' => ['required']]);
|
|
$log->input(['field' => 'value']);
|
|
$log->inputRaw(['field' => 'raw_value']);
|
|
|
|
expect(count($customLogger->logs))->toBe(3);
|
|
expect($customLogger->logs[0]['level'])->toBe(LogLevel::DEBUG);
|
|
expect($customLogger->logs[1]['level'])->toBe(LogLevel::DEBUG);
|
|
expect($customLogger->logs[2]['level'])->toBe(LogLevel::DEBUG);
|
|
});
|
|
});
|
|
|
|
describe('integration with DataObject', function () {
|
|
|
|
beforeEach(function () {
|
|
$this->customLogger = new CustomLogger();
|
|
config()->set('dto.log.logger', $this->customLogger);
|
|
config()->set('dto.log.rules', LogLevel::DEBUG);
|
|
config()->set('dto.log.input', LogLevel::DEBUG);
|
|
config()->set('dto.log.raw_input', LogLevel::DEBUG);
|
|
});
|
|
|
|
afterEach(function () {
|
|
config()->set('dto.log.logger', NullLogger::class);
|
|
});
|
|
|
|
it('logs raw input during fromArray execution', function () {
|
|
PrimitiveData::fromArray([
|
|
'string' => 'test',
|
|
'int' => 42,
|
|
'float' => 3.14,
|
|
'bool' => true,
|
|
]);
|
|
|
|
expect($this->customLogger->hasLog(LogLevel::DEBUG, 'raw_input'))->toBeFalse();
|
|
expect($this->customLogger->hasLog(LogLevel::DEBUG, 'string'))->toBeTrue();
|
|
expect($this->customLogger->hasLog(LogLevel::DEBUG, '42'))->toBeTrue();
|
|
});
|
|
|
|
it('logs rules during fromArray execution', function () {
|
|
PrimitiveData::fromArray([
|
|
'string' => 'test',
|
|
'int' => 42,
|
|
'float' => 3.14,
|
|
'bool' => true,
|
|
]);
|
|
|
|
expect($this->customLogger->hasLog(LogLevel::DEBUG, 'required'))->toBeTrue();
|
|
});
|
|
|
|
it('logs processed input during fromArray execution', function () {
|
|
PrimitiveData::fromArray([
|
|
'string' => 'test',
|
|
'int' => 42,
|
|
'float' => 3.14,
|
|
'bool' => true,
|
|
]);
|
|
|
|
expect($this->customLogger->hasLog(LogLevel::DEBUG, 'test'))->toBeTrue();
|
|
});
|
|
|
|
it('captures all three log types during successful fromArray', function () {
|
|
PrimitiveData::fromArray([
|
|
'string' => 'integration_test',
|
|
'int' => 123,
|
|
'float' => 9.99,
|
|
'bool' => false,
|
|
]);
|
|
|
|
$rawInputLogged = false;
|
|
$rulesLogged = false;
|
|
$inputLogged = false;
|
|
|
|
foreach ($this->customLogger->logs as $log) {
|
|
if (str_contains($log['message'], 'string')) {
|
|
$rawInputLogged = true;
|
|
}
|
|
if (str_contains($log['message'], 'required')) {
|
|
$rulesLogged = true;
|
|
}
|
|
if (str_contains($log['message'], 'integration_test')) {
|
|
$inputLogged = true;
|
|
}
|
|
}
|
|
|
|
expect($rawInputLogged)->toBeTrue('Raw input should be logged');
|
|
expect($rulesLogged)->toBeTrue('Rules should be logged');
|
|
expect($inputLogged)->toBeTrue('Processed input should be logged');
|
|
});
|
|
|
|
it('logs even when validation fails', function () {
|
|
try {
|
|
PrimitiveData::fromArray([
|
|
'int' => 42,
|
|
'float' => 3.14,
|
|
'bool' => true,
|
|
]);
|
|
} catch (\Illuminate\Validation\ValidationException $e) {
|
|
// Expected
|
|
}
|
|
|
|
expect($this->customLogger->hasLog(LogLevel::DEBUG, 'required'))->toBeTrue();
|
|
expect($this->customLogger->hasLog(LogLevel::DEBUG, '42'))->toBeTrue();
|
|
});
|
|
|
|
it('logs validation errors when validation fails', function () {
|
|
config()->set('dto.log.validation_errors', LogLevel::ERROR);
|
|
|
|
try {
|
|
PrimitiveData::fromArray([
|
|
'int' => 42,
|
|
'float' => 3.14,
|
|
'bool' => true,
|
|
]);
|
|
} catch (\Illuminate\Validation\ValidationException $e) {
|
|
// Expected
|
|
}
|
|
|
|
expect($this->customLogger->hasLog(LogLevel::ERROR, 'string'))->toBeTrue();
|
|
expect($this->customLogger->hasLog(LogLevel::ERROR, 'required'))->toBeTrue();
|
|
});
|
|
});
|
|
|
|
describe('logging with NullLogger', function () {
|
|
|
|
it('does not throw when logging with NullLogger', function () {
|
|
config()->set('dto.log.logger', NullLogger::class);
|
|
|
|
$log = new Log();
|
|
|
|
expect(function () use ($log) {
|
|
$log->rules(['field' => ['required']]);
|
|
$log->input(['field' => 'value']);
|
|
$log->inputRaw(['field' => 'raw_value']);
|
|
})->not->toThrow(\Throwable::class);
|
|
});
|
|
|
|
it('does not affect DataObject behavior when using NullLogger', function () {
|
|
config()->set('dto.log.logger', NullLogger::class);
|
|
|
|
$object = PrimitiveData::fromArray([
|
|
'string' => 'test',
|
|
'int' => 42,
|
|
'float' => 3.14,
|
|
'bool' => true,
|
|
]);
|
|
|
|
expect($object)->toBeInstanceOf(PrimitiveData::class);
|
|
expect($object->string)->toBe('test');
|
|
});
|
|
});
|