295 lines
9.6 KiB
PHP
295 lines
9.6 KiB
PHP
<?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');
|
|
});
|