forked from apoloz/alfa-bank-sdk
refactor(logging)
This commit is contained in:
294
tests/Feature/LoggingTest.php
Normal file
294
tests/Feature/LoggingTest.php
Normal file
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
|
||||
use Apoloz\AlfaBank\Core\AlfaBankConnector;
|
||||
use Apoloz\AlfaBank\Core\AlfaConfig;
|
||||
use Apoloz\AlfaBank\Http\Middleware\LoggingRequestMiddleware;
|
||||
use Apoloz\AlfaBank\Http\Middleware\LoggingResponseMiddleware;
|
||||
use Apoloz\AlfaBank\Requests\Accounts\GetAccountsRequest;
|
||||
use Apoloz\AlfaBank\Sbp\SbpConnector;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
use Saloon\Http\Faking\MockClient;
|
||||
use Saloon\Http\Faking\MockResponse;
|
||||
|
||||
function createLoggingConfig(): AlfaConfig
|
||||
{
|
||||
$cert = tempnam(sys_get_temp_dir(), 'cert');
|
||||
$key = tempnam(sys_get_temp_dir(), 'key');
|
||||
|
||||
return new AlfaConfig(
|
||||
baseUrl: 'https://test.alfabank.ru',
|
||||
certificatePath: $cert,
|
||||
privateKeyPath: $key
|
||||
);
|
||||
}
|
||||
|
||||
test('AlfaBankConnector logs request with host by default', function (): void {
|
||||
/** @var LoggerInterface&MockObject */
|
||||
$logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$logger->expects($this->exactly(2))
|
||||
->method('info')
|
||||
->with(
|
||||
$this->logicalOr(
|
||||
$this->stringContains('AlfaBank Request'),
|
||||
$this->stringContains('AlfaBank Response')
|
||||
),
|
||||
$this->callback(function (array $context): bool {
|
||||
return isset($context['host']) && $context['host'] === 'https://test.alfabank.ru';
|
||||
})
|
||||
);
|
||||
|
||||
$mockClient = new MockClient([
|
||||
'*' => MockResponse::make(['accounts' => []], 200),
|
||||
]);
|
||||
|
||||
$config = createLoggingConfig();
|
||||
$connector = new AlfaBankConnector($config, loggingEnabled: true, logger: $logger);
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$connector->send(new GetAccountsRequest);
|
||||
});
|
||||
|
||||
test('AlfaBankConnector logs response with host by default', function (): void {
|
||||
/** @var LoggerInterface&MockObject */
|
||||
$logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$calls = [];
|
||||
$logger->expects($this->exactly(2))
|
||||
->method('info')
|
||||
->willReturnCallback(function (string $message, array $context) use (&$calls): void {
|
||||
$calls[] = $message;
|
||||
});
|
||||
|
||||
$mockClient = new MockClient([
|
||||
'*' => MockResponse::make(['accounts' => []], 200),
|
||||
]);
|
||||
|
||||
$config = createLoggingConfig();
|
||||
$connector = new AlfaBankConnector($config, loggingEnabled: true, logger: $logger);
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$connector->send(new GetAccountsRequest);
|
||||
|
||||
expect($calls)->toHaveCount(2);
|
||||
expect($calls[0])->toContain('AlfaBank Request');
|
||||
expect($calls[1])->toContain('AlfaBank Response');
|
||||
});
|
||||
|
||||
test('AlfaBankConnector does not log when loggingEnabled is false', function (): void {
|
||||
/** @var LoggerInterface&MockObject */
|
||||
$logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$logger->expects($this->never())->method('info');
|
||||
|
||||
$mockClient = new MockClient([
|
||||
'*' => MockResponse::make(['accounts' => []], 200),
|
||||
]);
|
||||
|
||||
$config = createLoggingConfig();
|
||||
$connector = new AlfaBankConnector($config, loggingEnabled: false, logger: $logger);
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$connector->send(new GetAccountsRequest);
|
||||
});
|
||||
|
||||
test('withoutLogging disables logging', function (): void {
|
||||
/** @var LoggerInterface&MockObject */
|
||||
$logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$logger->expects($this->never())->method('info');
|
||||
|
||||
$mockClient = new MockClient([
|
||||
'*' => MockResponse::make(['accounts' => []], 200),
|
||||
]);
|
||||
|
||||
$config = createLoggingConfig();
|
||||
$connector = new AlfaBankConnector($config, loggingEnabled: true, logger: $logger);
|
||||
$connector->withoutLogging();
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$connector->send(new GetAccountsRequest);
|
||||
});
|
||||
|
||||
test('withLogging re-enables logging after withoutLogging', function (): void {
|
||||
/** @var LoggerInterface&MockObject */
|
||||
$logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$logger->expects($this->exactly(2))->method('info');
|
||||
|
||||
$mockClient = new MockClient([
|
||||
'*' => MockResponse::make(['accounts' => []], 200),
|
||||
]);
|
||||
|
||||
$config = createLoggingConfig();
|
||||
$connector = new AlfaBankConnector($config, loggingEnabled: false, logger: $logger);
|
||||
$connector->withLogging($logger);
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$connector->send(new GetAccountsRequest);
|
||||
});
|
||||
|
||||
test('SbpConnector logs request with host by default', function (): void {
|
||||
/** @var LoggerInterface&MockObject */
|
||||
$logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$logger->expects($this->exactly(2))
|
||||
->method('info')
|
||||
->with(
|
||||
$this->logicalOr(
|
||||
$this->stringContains('AlfaBank Request'),
|
||||
$this->stringContains('AlfaBank Response')
|
||||
),
|
||||
$this->callback(function (array $context): bool {
|
||||
return isset($context['host']) && $context['host'] === 'https://test.alfabank.ru';
|
||||
})
|
||||
);
|
||||
|
||||
$mockClient = new MockClient([
|
||||
'*' => MockResponse::make([], 200),
|
||||
]);
|
||||
|
||||
$config = createLoggingConfig();
|
||||
$connector = new SbpConnector($config, 'alias', loggingEnabled: true, logger: $logger);
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$connector->send(new class extends \Saloon\Http\Request
|
||||
{
|
||||
protected \Saloon\Enums\Method $method = \Saloon\Enums\Method::GET;
|
||||
|
||||
public function resolveEndpoint(): string
|
||||
{
|
||||
return '/test';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('SbpConnector withoutLogging disables logging', function (): void {
|
||||
/** @var LoggerInterface&MockObject */
|
||||
$logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$logger->expects($this->never())->method('info');
|
||||
|
||||
$mockClient = new MockClient([
|
||||
'*' => MockResponse::make([], 200),
|
||||
]);
|
||||
|
||||
$config = createLoggingConfig();
|
||||
$connector = new SbpConnector($config, 'alias', loggingEnabled: true, logger: $logger);
|
||||
$connector->withoutLogging();
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$connector->send(new class extends \Saloon\Http\Request
|
||||
{
|
||||
protected \Saloon\Enums\Method $method = \Saloon\Enums\Method::GET;
|
||||
|
||||
public function resolveEndpoint(): string
|
||||
{
|
||||
return '/test';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('LoggingRequestMiddleware masks sensitive headers', function (): void {
|
||||
$middleware = new LoggingRequestMiddleware(
|
||||
$this->createMock(LoggerInterface::class)
|
||||
);
|
||||
|
||||
$reflection = new ReflectionMethod($middleware, 'maskHeaders');
|
||||
$result = $reflection->invoke($middleware, [
|
||||
'Authorization' => 'Bearer secret-token',
|
||||
'Accept' => 'application/json',
|
||||
]);
|
||||
|
||||
expect($result['Authorization'])->toBe('Bearer *****');
|
||||
expect($result['Accept'])->toBe('application/json');
|
||||
});
|
||||
|
||||
test('LoggingRequestMiddleware masks sensitive body fields', function (): void {
|
||||
$middleware = new LoggingRequestMiddleware(
|
||||
$this->createMock(LoggerInterface::class)
|
||||
);
|
||||
|
||||
$reflection = new ReflectionMethod($middleware, 'maskBody');
|
||||
$result = $reflection->invoke($middleware, [
|
||||
'pan' => '4276123456789012',
|
||||
'cvc' => '123',
|
||||
'amount' => 1000,
|
||||
'nested' => [
|
||||
'client_secret' => 'super-secret',
|
||||
],
|
||||
]);
|
||||
|
||||
expect($result['pan'])->toBe('*****');
|
||||
expect($result['cvc'])->toBe('*****');
|
||||
expect($result['amount'])->toBe(1000);
|
||||
expect($result['nested']['client_secret'])->toBe('*****');
|
||||
});
|
||||
|
||||
test('AlfaBankConnector without logger uses NullLogger silently', function (): void {
|
||||
$mockClient = new MockClient([
|
||||
'*' => MockResponse::make(['accounts' => []], 200),
|
||||
]);
|
||||
|
||||
$config = createLoggingConfig();
|
||||
$connector = new AlfaBankConnector($config, loggingEnabled: true);
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
expect($connector->send(new GetAccountsRequest))->status()->toBe(200);
|
||||
});
|
||||
|
||||
test('logs non-JSON response body without throwing', function (): void {
|
||||
/** @var LoggerInterface&MockObject */
|
||||
$logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$loggedBody = null;
|
||||
$logger->expects($this->exactly(2))
|
||||
->method('info')
|
||||
->willReturnCallback(function (string $message, array $context) use (&$loggedBody): void {
|
||||
if (str_contains($message, 'Response')) {
|
||||
$loggedBody = $context['body'];
|
||||
}
|
||||
});
|
||||
|
||||
$mockClient = new MockClient([
|
||||
'*' => MockResponse::make('eyJhbGciOiJIUzI1NiJ9...', 200, ['Content-Type' => 'application/jwt']),
|
||||
]);
|
||||
|
||||
$config = createLoggingConfig();
|
||||
$connector = new AlfaBankConnector($config, loggingEnabled: true, logger: $logger);
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$response = $connector->send(new GetAccountsRequest);
|
||||
|
||||
expect($response->status())->toBe(200);
|
||||
expect($loggedBody)->toBe('eyJhbGciOiJIUzI1NiJ9...');
|
||||
});
|
||||
|
||||
test('logs plain text response without throwing', function (): void {
|
||||
/** @var LoggerInterface&MockObject */
|
||||
$logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$loggedBody = null;
|
||||
$logger->expects($this->exactly(2))
|
||||
->method('info')
|
||||
->willReturnCallback(function (string $message, array $context) use (&$loggedBody): void {
|
||||
if (str_contains($message, 'Response')) {
|
||||
$loggedBody = $context['body'];
|
||||
}
|
||||
});
|
||||
|
||||
$mockClient = new MockClient([
|
||||
'*' => MockResponse::make('Internal Server Error', 200, ['Content-Type' => 'text/plain']),
|
||||
]);
|
||||
|
||||
$config = createLoggingConfig();
|
||||
$connector = new AlfaBankConnector($config, loggingEnabled: true, logger: $logger);
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$response = $connector->send(new GetAccountsRequest);
|
||||
|
||||
expect($response->status())->toBe(200);
|
||||
expect($loggedBody)->toBe('Internal Server Error');
|
||||
});
|
||||
Reference in New Issue
Block a user