Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
WebapiAbstract.php
Go to the documentation of this file.
1 <?php
7 
12 
19 abstract class WebapiAbstract extends \PHPUnit\Framework\TestCase
20 {
33  const ADAPTER_SOAP = 'soap';
34  const ADAPTER_REST = 'rest';
42  protected $_appCache;
43 
49  protected $_modelsToDelete = [];
50 
56  protected static $_fixturesNamespace;
57 
63  protected static $_fixtures = [];
64 
70  protected static $_methodLevelFixtures = [];
71 
77  protected static $_classLevelFixtures = [];
78 
84  protected $_origConfigValues = [];
85 
91  protected $_webApiAdapters;
92 
98  protected $_webApiAdaptersMap = [
99  self::ADAPTER_SOAP => \Magento\TestFramework\TestCase\Webapi\Adapter\Soap::class,
100  self::ADAPTER_REST => \Magento\TestFramework\TestCase\Webapi\Adapter\Rest::class,
101  ];
102 
106  public static function setUpBeforeClass()
107  {
108  parent::setUpBeforeClass();
110  }
111 
117  public static function tearDownAfterClass()
118  {
119  //clear garbage in memory
120  gc_collect_cycles();
121 
122  $fixtureNamespace = self::_getFixtureNamespace();
123  if (isset(self::$_classLevelFixtures[$fixtureNamespace])
124  && count(self::$_classLevelFixtures[$fixtureNamespace])
125  ) {
126  self::_deleteFixtures(self::$_classLevelFixtures[$fixtureNamespace]);
127  }
128 
129  //ever disable secure area on class down
130  self::_enableSecureArea(false);
132  parent::tearDownAfterClass();
133  }
134 
141  protected function tearDown()
142  {
143  $fixtureNamespace = self::_getFixtureNamespace();
144  if (isset(self::$_methodLevelFixtures[$fixtureNamespace])
145  && count(self::$_methodLevelFixtures[$fixtureNamespace])
146  ) {
147  self::_deleteFixtures(self::$_methodLevelFixtures[$fixtureNamespace]);
148  }
149  $this->_callModelsDelete();
150  $this->_restoreAppConfig();
151  parent::tearDown();
152  }
153 
165  protected function _webApiCall(
166  $serviceInfo,
167  $arguments = [],
168  $webApiAdapterCode = null,
169  $storeCode = null,
170  $integration = null
171  ) {
172  if ($webApiAdapterCode === null) {
174  $webApiAdapterCode = strtolower(TESTS_WEB_API_ADAPTER);
175  }
176  return $this->_getWebApiAdapter($webApiAdapterCode)->call($serviceInfo, $arguments, $storeCode, $integration);
177  }
178 
182  protected function _markTestAsSoapOnly($message = null)
183  {
184  if (TESTS_WEB_API_ADAPTER != self::ADAPTER_SOAP) {
185  $this->markTestSkipped($message ? $message : "The test is intended to be executed for SOAP adapter only.");
186  }
187  }
188 
192  protected function _markTestAsRestOnly($message = null)
193  {
194  if (TESTS_WEB_API_ADAPTER != self::ADAPTER_REST) {
195  $this->markTestSkipped($message ? $message : "The test is intended to be executed for REST adapter only.");
196  }
197  }
198 
207  public static function setFixture($key, $fixture, $tearDown = self::AUTO_TEAR_DOWN_AFTER_METHOD)
208  {
209  $fixturesNamespace = self::_getFixtureNamespace();
210  if (!isset(self::$_fixtures[$fixturesNamespace])) {
211  self::$_fixtures[$fixturesNamespace] = [];
212  }
213  self::$_fixtures[$fixturesNamespace][$key] = $fixture;
214  if ($tearDown == self::AUTO_TEAR_DOWN_AFTER_METHOD) {
215  if (!isset(self::$_methodLevelFixtures[$fixturesNamespace])) {
216  self::$_methodLevelFixtures[$fixturesNamespace] = [];
217  }
218  self::$_methodLevelFixtures[$fixturesNamespace][] = $key;
219  } else {
220  if ($tearDown == self::AUTO_TEAR_DOWN_AFTER_CLASS) {
221  if (!isset(self::$_classLevelFixtures[$fixturesNamespace])) {
222  self::$_classLevelFixtures[$fixturesNamespace] = [];
223  }
224  self::$_classLevelFixtures[$fixturesNamespace][] = $key;
225  }
226  }
227  }
228 
235  public static function getFixture($key)
236  {
237  $fixturesNamespace = self::_getFixtureNamespace();
238  if (array_key_exists($key, self::$_fixtures[$fixturesNamespace])) {
239  return self::$_fixtures[$fixturesNamespace][$key];
240  }
241  return null;
242  }
243 
251  public static function callModelDelete($model, $secure = false)
252  {
253  if ($model instanceof \Magento\Framework\Model\AbstractModel && $model->getId()) {
254  if ($secure) {
255  self::_enableSecureArea();
256  }
257  $model->delete();
258  if ($secure) {
259  self::_enableSecureArea(false);
260  }
261  }
262  }
263 
271  public function addModelToDelete($model, $secure = false)
272  {
273  $this->_modelsToDelete[] = ['model' => $model, 'secure' => $secure];
274  return $this;
275  }
276 
284  protected function _getWebApiAdapter($webApiAdapterCode)
285  {
286  if (!isset($this->_webApiAdapters[$webApiAdapterCode])) {
287  if (!isset($this->_webApiAdaptersMap[$webApiAdapterCode])) {
288  throw new \LogicException(
289  sprintf('Declaration of the requested Web API adapter "%s" was not found.', $webApiAdapterCode)
290  );
291  }
292  $this->_webApiAdapters[$webApiAdapterCode] = Bootstrap::getObjectManager()->get(
293  $this->_webApiAdaptersMap[$webApiAdapterCode]
294  );
295  }
296  return $this->_webApiAdapters[$webApiAdapterCode];
297  }
298 
304  protected static function _setFixtureNamespace()
305  {
306  if (self::$_fixturesNamespace !== null) {
307  throw new \RuntimeException('Fixture namespace is already set.');
308  }
309  self::$_fixturesNamespace = uniqid();
310  }
311 
315  protected static function _unsetFixtureNamespace()
316  {
317  $fixturesNamespace = self::_getFixtureNamespace();
318  unset(self::$_fixtures[$fixturesNamespace]);
319  self::$_fixturesNamespace = null;
320  }
321 
328  protected static function _getFixtureNamespace()
329  {
330  $fixtureNamespace = self::$_fixturesNamespace;
331  if ($fixtureNamespace === null) {
332  throw new \RuntimeException('Fixture namespace must be set.');
333  }
334  return $fixtureNamespace;
335  }
336 
343  protected static function _enableSecureArea($flag = true)
344  {
347 
348  $objectManager->get(\Magento\Framework\Registry::class)->unregister('isSecureArea');
349  if ($flag) {
350  $objectManager->get(\Magento\Framework\Registry::class)->register('isSecureArea', $flag);
351  }
352  }
353 
359  protected function _callModelsDelete()
360  {
361  if ($this->_modelsToDelete) {
362  foreach ($this->_modelsToDelete as $key => $modelData) {
364  $model = $modelData['model'];
365  $this->callModelDelete($model, $modelData['secure']);
366  unset($this->_modelsToDelete[$key]);
367  }
368  }
369  return $this;
370  }
371 
378  protected function _assertMessagesEqual($expectedMessages, $receivedMessages)
379  {
380  foreach ($receivedMessages as $message) {
381  $this->assertContains($message, $expectedMessages, "Unexpected message: '{$message}'");
382  }
383  $expectedErrorsCount = count($expectedMessages);
384  $this->assertCount($expectedErrorsCount, $receivedMessages, 'Invalid messages quantity received');
385  }
386 
392  protected static function _deleteFixtures($fixtures)
393  {
394  foreach ($fixtures as $fixture) {
395  self::deleteFixture($fixture, true);
396  }
397  }
398 
406  public static function deleteFixture($key, $secure = false)
407  {
408  $fixturesNamespace = self::_getFixtureNamespace();
409  if (array_key_exists($key, self::$_fixtures[$fixturesNamespace])) {
410  self::callModelDelete(self::$_fixtures[$fixturesNamespace][$key], $secure);
411  unset(self::$_fixtures[$fixturesNamespace][$key]);
412  }
413  }
414 
422  protected function _getAppCache()
423  {
424  if (null === $this->_appCache) {
425  //set application path
428  $config = $objectManager->get(\Magento\Framework\App\Config\ScopeConfigInterface::class);
429  $options = $config->getOptions();
430  $currentCacheDir = $options->getCacheDir();
431  $currentEtcDir = $options->getEtcDir();
433  $filesystem = $objectManager->get(\Magento\Framework\Filesystem::class);
434  $options->setCacheDir($filesystem->getDirectoryRead(DirectoryList::CACHE)->getAbsolutePath());
435  $options->setEtcDir($filesystem->getDirectoryRead(DirectoryList::CONFIG)->getAbsolutePath());
436 
437  $this->_appCache = $objectManager->get(\Magento\Framework\App\Cache::class);
438 
439  //revert paths options
440  $options->setCacheDir($currentCacheDir);
441  $options->setEtcDir($currentEtcDir);
442  }
443  return $this->_appCache;
444  }
445 
451  protected function _cleanAppConfigCache()
452  {
453  return $this->_getAppCache()->clean(\Magento\Framework\App\Config::CACHE_TAG);
454  }
455 
467  protected function _updateAppConfig(
468  $path,
469  $value,
470  $cleanAppCache = true,
471  $updateLocalConfig = false,
472  $restore = false
473  ) {
474  list($section, $group, $node) = explode('/', $path);
475 
476  if (!$section || !$group || !$node) {
477  throw new \RuntimeException(
478  sprintf('Config path must have view as "section/group/node" but now it "%s"', $path)
479  );
480  }
481 
484  $config = $objectManager->create(\Magento\Config\Model\Config::class);
485  $data[$group]['fields'][$node]['value'] = $value;
486  $config->setSection($section)->setGroups($data)->save();
487 
488  if ($restore && !isset($this->_origConfigValues[$path])) {
489  $this->_origConfigValues[$path] = (string)$objectManager->get(
490  \Magento\Framework\App\Config\ScopeConfigInterface::class
491  )->getNode(
492  $path,
493  'default'
494  );
495  }
496 
497  //refresh local cache
498  if ($cleanAppCache) {
499  if ($updateLocalConfig) {
500  $objectManager->get(\Magento\Framework\App\Config\ReinitableConfigInterface::class)->reinit();
501  $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->reinitStores();
502  }
503 
504  if (!$this->_cleanAppConfigCache()) {
505  throw new \RuntimeException('Application configuration cache cannot be cleaned.');
506  }
507  }
508 
509  return $this;
510  }
511 
515  protected function _restoreAppConfig()
516  {
517  foreach ($this->_origConfigValues as $configPath => $origValue) {
518  $this->_updateAppConfig($configPath, $origValue, true, true);
519  }
520  }
521 
536  public function processRestExceptionResult(\Exception $e)
537  {
538  $error = json_decode($e->getMessage(), true);
539  //Remove line breaks and replace with space
540  $error['message'] = trim(preg_replace('/\s+/', ' ', $error['message']));
541  // remove trace and type, will only be present if server is in dev mode
542  unset($error['trace']);
543  unset($error['type']);
544  return $error;
545  }
546 
557  protected function checkSoapFault(
558  $soapFault,
559  $expectedMessage,
560  $expectedFaultCode,
561  $expectedErrorParams = [],
562  $expectedWrappedErrors = [],
563  $traceString = null
564  ) {
565  $this->assertContains($expectedMessage, $soapFault->getMessage(), "Fault message is invalid.");
566 
567  $errorDetailsNode = 'GenericFault';
568  $errorDetails = isset($soapFault->detail->$errorDetailsNode) ? $soapFault->detail->$errorDetailsNode : null;
569  if (!empty($expectedErrorParams) || !empty($expectedWrappedErrors)) {
571  $this->assertNotNull($errorDetails, "Details must be present.");
572  $this->_checkFaultParams($expectedErrorParams, $errorDetails);
573  $this->_checkWrappedErrors($expectedWrappedErrors, $errorDetails);
574  }
575 
576  if ($traceString) {
578  $traceNode = Fault::NODE_DETAIL_TRACE;
580  ->get(\Magento\Framework\App\State::class)
581  ->getMode();
582  if ($mode == \Magento\Framework\App\State::MODE_DEVELOPER) {
584  $this->assertContains(
585  $traceString,
586  $errorDetails->$traceNode,
587  'Trace Information is incorrect.'
588  );
589  } else {
590  $this->assertNull($errorDetails, "Details are not expected.");
591  }
592  }
593 
595  $this->assertNotNull($soapFault->faultcode, "Fault code must not be empty.");
596  $this->assertEquals($expectedFaultCode, $soapFault->faultcode, "Fault code is invalid.");
597  }
598 
605  protected function _checkFaultParams($expectedErrorParams, $errorDetails)
606  {
607  $paramsNode = Fault::NODE_DETAIL_PARAMETERS;
608  if ($expectedErrorParams) {
609  $paramNode = Fault::NODE_DETAIL_PARAMETER;
612  $actualParams = [];
613  if (isset($errorDetails->$paramsNode->$paramNode)) {
614  if (is_array($errorDetails->$paramsNode->$paramNode)) {
615  foreach ($errorDetails->$paramsNode->$paramNode as $param) {
616  $actualParams[$param->$paramKey] = $param->$paramValue;
617  }
618  } else {
619  $param = $errorDetails->$paramsNode->$paramNode;
620  $actualParams[$param->$paramKey] = $param->$paramValue;
621  }
622  }
623  $this->assertEquals(
624  $expectedErrorParams,
625  $actualParams,
626  "Parameters in fault details are invalid."
627  );
628  } else {
629  $this->assertFalse(isset($errorDetails->$paramsNode), "Parameters are not expected in fault details.");
630  }
631  }
632 
639  protected function _checkWrappedErrors($expectedWrappedErrors, $errorDetails)
640  {
641  $wrappedErrorsNode = Fault::NODE_DETAIL_WRAPPED_ERRORS;
642  if ($expectedWrappedErrors) {
643  $wrappedErrorNode = Fault::NODE_DETAIL_WRAPPED_ERROR;
644  $actualWrappedErrors = [];
645  if (isset($errorDetails->$wrappedErrorsNode->$wrappedErrorNode)) {
646  $errorNode = $errorDetails->$wrappedErrorsNode->$wrappedErrorNode;
647  if (is_array($errorNode)) {
648  foreach ($errorNode as $error) {
649  $actualWrappedErrors[] = $this->getActualWrappedErrors($error);
650  }
651  } else {
652  $actualWrappedErrors[] = $this->getActualWrappedErrors($errorNode);
653  }
654  }
655  $this->assertEquals(
656  $expectedWrappedErrors,
657  $actualWrappedErrors,
658  "Wrapped errors in fault details are invalid."
659  );
660  } else {
661  $this->assertFalse(
662  isset($errorDetails->$wrappedErrorsNode),
663  "Wrapped errors are not expected in fault details."
664  );
665  }
666  }
667 
672  private function getActualWrappedErrors(\stdClass $errorNode)
673  {
674  $actualParameters = [];
675  $parameterNode = $errorNode->parameters->parameter;
676  if (is_array($parameterNode)) {
677  foreach ($parameterNode as $parameter) {
678  $actualParameters[$parameter->key] = $parameter->value;
679  }
680  } else {
681  $actualParameters[$parameterNode->key] = $parameterNode->value;
682  }
683  return [
684  'message' => $errorNode->message,
685  // Can not rename on parameters due to Backward Compatibility
686  'params' => $actualParameters,
687  ];
688  }
689 }
$objectManager
Definition: bootstrap.php:17
_webApiCall( $serviceInfo, $arguments=[], $webApiAdapterCode=null, $storeCode=null, $integration=null)
$config
Definition: fraud_order.php:17
$group
Definition: sections.phtml:16
$message
$storeCode
Definition: indexer.php:15
_checkWrappedErrors($expectedWrappedErrors, $errorDetails)
$value
Definition: gender.phtml:16
checkSoapFault( $soapFault, $expectedMessage, $expectedFaultCode, $expectedErrorParams=[], $expectedWrappedErrors=[], $traceString=null)
if($exist=($block->getProductCollection() && $block->getProductCollection() ->getSize())) $mode
Definition: grid.phtml:15
static setFixture($key, $fixture, $tearDown=self::AUTO_TEAR_DOWN_AFTER_METHOD)
$arguments
_checkFaultParams($expectedErrorParams, $errorDetails)
static callModelDelete($model, $secure=false)
$filesystem
_assertMessagesEqual($expectedMessages, $receivedMessages)