| Current Path : /var/www/clients/client3/web2/web/vendor/magento/framework/Webapi/Test/Unit/ |
| Current File : /var/www/clients/client3/web2/web/vendor/magento/framework/Webapi/Test/Unit/ErrorProcessorTest.php |
<?php
/**
* Test Webapi Error Processor.
*
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magento\Framework\Webapi\Test\Unit;
use Magento\Framework\App\State;
use Magento\Framework\Exception\AuthorizationException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Filesystem;
use Magento\Framework\Json\Encoder;
use Magento\Framework\Phrase;
use Magento\Framework\Webapi\ErrorProcessor;
use Magento\Framework\Webapi\Exception as WebapiException;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ErrorProcessorTest extends TestCase
{
/** @var ErrorProcessor */
protected $_errorProcessor;
/** @var Encoder */
protected $encoderMock;
/** @var MockObject */
protected $_appStateMock;
/** @var LoggerInterface */
protected $_loggerMock;
protected function setUp(): void
{
/** Set up mocks for SUT. */
$this->encoderMock = $this->getMockBuilder(Encoder::class)
->disableOriginalConstructor()
->setMethods(['encode'])
->getMock();
$this->_appStateMock = $this->getMockBuilder(State::class)
->disableOriginalConstructor()
->getMock();
$this->_loggerMock = $this->getMockBuilder(LoggerInterface::class)
->getMock();
$filesystemMock = $this->getMockBuilder(Filesystem::class)
->disableOriginalConstructor()
->getMock();
/** Initialize SUT. */
$this->_errorProcessor = new ErrorProcessor(
$this->encoderMock,
$this->_appStateMock,
$this->_loggerMock,
$filesystemMock
);
parent::setUp();
}
protected function tearDown(): void
{
unset($this->_errorProcessor);
unset($this->encoderMock);
unset($this->_appStateMock);
parent::tearDown();
}
/**
* Test render method in JSON format.
*
* @return void
*/
public function testRenderJson()
{
$_SERVER['HTTP_ACCEPT'] = 'json';
/** Assert that jsonEncode method will be executed once. */
$this->encoderMock->expects(
$this->once()
)->method(
'encode'
)->willReturnCallback(
[$this, 'callbackJsonEncode'], $this->returnArgument(0)
);
/** Init output buffering to catch output via echo function. */
ob_start();
$this->_errorProcessor->renderErrorMessage('Message');
/** Get output buffer. */
$actualResult = ob_get_contents();
ob_end_clean();
$expectedResult = '{"messages":{"error":[{"code":500,"message":"Message"}]}}';
$this->assertEquals($expectedResult, $actualResult, 'Invalid rendering in JSON.');
}
/**
* Callback function for RenderJson and RenderJsonInDeveloperMode tests.
*
* Method encodes data to JSON and returns it.
*
* @param array $data
* @return string
*/
public function callbackJsonEncode($data)
{
return json_encode($data);
}
/**
* Test render method in JSON format with turned on developer mode.
* @return void
*/
public function testRenderJsonInDeveloperMode()
{
$_SERVER['HTTP_ACCEPT'] = 'json';
/** Mock app to return enabled developer mode flag. */
$this->_appStateMock->expects($this->any())->method('getMode')->willReturn('developer');
/** Assert that jsonEncode method will be executed once. */
$this->encoderMock->expects(
$this->once()
)->method(
'encode'
)->willReturnCallback(
[$this, 'callbackJsonEncode'], $this->returnArgument(0)
);
ob_start();
$this->_errorProcessor->renderErrorMessage('Message', 'Message trace.', 401);
$actualResult = ob_get_contents();
ob_end_clean();
$expectedResult = '{"messages":{"error":[{"code":401,"message":"Message","trace":"Message trace."}]}}';
$this->assertEquals($expectedResult, $actualResult, 'Invalid rendering in JSON.');
}
/**
* Test render method in XML format.
* @return void
*/
public function testRenderXml()
{
$_SERVER['HTTP_ACCEPT'] = 'xml';
/** Init output buffering to catch output via echo function. */
ob_start();
$this->_errorProcessor->renderErrorMessage('Message');
/** Get output buffer. */
$actualResult = ob_get_contents();
ob_end_clean();
$expectedResult = '<?xml version="1.0"?><error><messages><error><data_item><code>500</code>' .
'<message><![CDATA[Message]]></message></data_item></error></messages></error>';
$this->assertEquals($expectedResult, $actualResult, 'Invalid rendering in XML.');
}
/**
* Test render method in XML format with turned on developer mode.
* @return void
*/
public function testRenderXmlInDeveloperMode()
{
$_SERVER['HTTP_ACCEPT'] = 'xml';
/** Mock app to return enabled developer mode flag. */
$this->_appStateMock->expects($this->any())->method('getMode')->willReturn('developer');
/** Init output buffering to catch output via echo function. */
ob_start();
$this->_errorProcessor->renderErrorMessage('Message', 'Trace message.', 401);
/** Get output buffer. */
$actualResult = ob_get_contents();
ob_end_clean();
$expectedResult = '<?xml version="1.0"?><error><messages><error><data_item><code>401</code><message>' .
'<![CDATA[Message]]></message><trace><![CDATA[Trace message.]]></trace></data_item></error>' .
'</messages></error>';
$this->assertEquals($expectedResult, $actualResult, 'Invalid rendering in XML with turned on developer mode.');
}
/**
* Test default render format is JSON.
* @return void
*/
public function testRenderDefaultFormat()
{
/** Set undefined rendering format. */
$_SERVER['HTTP_ACCEPT'] = 'undefined';
/** Assert that jsonEncode method will be executed at least once. */
$this->encoderMock->expects($this->atLeastOnce())->method('encode');
$this->_errorProcessor->renderErrorMessage('Message');
}
/**
* Test maskException method with turned on developer mode.
* @return void
*/
public function testMaskExceptionInDeveloperMode()
{
/** Mock app isDeveloperMode to return true. */
$this->_appStateMock->expects($this->once())->method('getMode')->willReturn('developer');
/** Init Logical exception. */
$errorMessage = 'Error Message';
$logicalException = new \LogicException($errorMessage);
/** Assert that Logic exception is converted to WebapiException without message obfuscation. */
$maskedException = $this->_errorProcessor->maskException($logicalException);
$this->assertInstanceOf(\Magento\Framework\Webapi\Exception::class, $maskedException);
$this->assertEquals(
$errorMessage,
$maskedException->getMessage(),
'Exception was masked incorrectly in developer mode.'
);
}
/**
* Test sendResponse method with various exceptions
*
* @param \Exception $exception
* @param int $expectedHttpCode
* @param string $expectedMessage
* @param array $expectedDetails
* @return void
* @dataProvider dataProviderForSendResponseExceptions
*/
public function testMaskException($exception, $expectedHttpCode, $expectedMessage, $expectedDetails)
{
/** Assert that exception was logged. */
// TODO:MAGETWO-21077 $this->_loggerMock->expects($this->once())->method('critical');
$maskedException = $this->_errorProcessor->maskException($exception);
$this->assertMaskedException(
$maskedException,
$expectedHttpCode,
$expectedMessage,
$expectedDetails
);
}
/**
* Test logged exception is the same as the thrown one in production mode
*/
public function testCriticalExceptionStackTrace()
{
$thrownException = new \Exception('', 0);
$this->_loggerMock->expects($this->once())
->method('critical')
->willReturnCallback(
function (\Exception $loggedException) use ($thrownException) {
$this->assertSame($thrownException, $loggedException->getPrevious());
}
);
$this->_errorProcessor->maskException($thrownException);
}
/**
* @return array
*/
public function dataProviderForSendResponseExceptions()
{
return [
'NoSuchEntityException' => [
new NoSuchEntityException(
new Phrase(
'No such entity with %fieldName = %fieldValue, %field2Name = %field2Value',
[
'fieldName' => 'detail1',
'fieldValue' => 'value1',
'field2Name' => 'resource_id',
'field2Value' => 'resource10',
]
)
),
\Magento\Framework\Webapi\Exception::HTTP_NOT_FOUND,
'No such entity with %fieldName = %fieldValue, %field2Name = %field2Value',
[
'fieldName' => 'detail1',
'fieldValue' => 'value1',
'field2Name' => 'resource_id',
'field2Value' => 'resource10',
],
],
'NoSuchEntityException (Empty message)' => [
new NoSuchEntityException(),
WebapiException::HTTP_NOT_FOUND,
'No such entity.',
[],
],
'AuthorizationException' => [
new AuthorizationException(
new Phrase(
'Consumer %consumer_id is not authorized to access %resources',
['consumer_id' => '3', 'resources' => '4']
)
),
WebapiException::HTTP_UNAUTHORIZED,
'Consumer %consumer_id is not authorized to access %resources',
['consumer_id' => '3', 'resources' => '4'],
],
'Exception' => [
new \Exception('Non service exception', 5678),
WebapiException::HTTP_INTERNAL_ERROR,
'Internal Error. Details are available in Magento log file. Report ID:',
[],
]
];
}
/**
* Assert that masked exception contains expected data.
*
* @param \Exception $maskedException
* @param int $expectedHttpCode
* @param string $expectedMessage
* @param array $expectedDetails
* @return void
*/
public function assertMaskedException(
$maskedException,
$expectedHttpCode,
$expectedMessage,
$expectedDetails
) {
/** All masked exceptions must be WebapiException */
$expectedType = \Magento\Framework\Webapi\Exception::class;
$this->assertInstanceOf(
$expectedType,
$maskedException,
"Masked exception type is invalid: expected '{$expectedType}', given '" . get_class(
$maskedException
) . "'."
);
/** @var $maskedException WebapiException */
$this->assertEquals(
$expectedHttpCode,
$maskedException->getHttpCode(),
"Masked exception HTTP code is invalid: expected '{$expectedHttpCode}', " .
"given '{$maskedException->getHttpCode()}'."
);
$this->assertStringContainsString(
$expectedMessage,
$maskedException->getMessage(),
"Masked exception message is invalid: expected '{$expectedMessage}', " .
"given '{$maskedException->getMessage()}'."
);
$this->assertEquals($expectedDetails, $maskedException->getDetails(), "Masked exception details are invalid.");
}
}