init
This commit is contained in:
+99
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\WebapiAsync\Model;
|
||||
|
||||
use Magento\Eav\Model\AttributeRepository;
|
||||
use Magento\TestFramework\TestCase\WebapiAbstract;
|
||||
use Magento\TestFramework\Helper\Bootstrap;
|
||||
use Magento\Framework\Webapi\Rest\Request;
|
||||
|
||||
/**
|
||||
* Check async request for configurable products creation service
|
||||
*
|
||||
* @magentoAppIsolation enabled
|
||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
|
||||
*/
|
||||
class AsyncBulkActionConfigurableProductsTest extends WebapiAbstract
|
||||
{
|
||||
private const SERVICE_NAME = 'asyncBulkConfigurableProductsV1';
|
||||
private const ASYNC_BULK_RESOURCE_PATH = '/async/bulk/V1/configurable-products';
|
||||
|
||||
private const BULK_UUID_KEY = 'bulk_uuid';
|
||||
|
||||
/**
|
||||
* @var \Magento\Framework\ObjectManagerInterface
|
||||
*/
|
||||
private $objectManager;
|
||||
|
||||
/**
|
||||
* @var AttributeRepository
|
||||
*/
|
||||
protected $attributeRepository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->objectManager = Bootstrap::getObjectManager();
|
||||
$this->attributeRepository = $this->objectManager->get(\Magento\Eav\Model\AttributeRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
|
||||
* @magentoApiDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testAsyncBulkUpdate()
|
||||
{
|
||||
$productSku = 'configurable';
|
||||
$attribute = $this->attributeRepository->get('catalog_product', 'test_configurable');
|
||||
|
||||
$this->_markTestAsRestOnly();
|
||||
$requestData = [
|
||||
[
|
||||
'sku' => $productSku,
|
||||
'option' => [
|
||||
'attribute_id' => (int)$attribute->getAttributeId(),
|
||||
'label' => 'test_configurable',
|
||||
'position' => 0,
|
||||
'is_use_default' => true,
|
||||
'values' => [
|
||||
[
|
||||
'value_index' => $attribute->getOptions()[1]->getValue()
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
$response = $this->saveConfigurableProductsBySku($requestData, 'options');
|
||||
$this->assertArrayHasKey(self::BULK_UUID_KEY, $response);
|
||||
$this->assertNotNull($response[self::BULK_UUID_KEY]);
|
||||
|
||||
$this->assertCount(1, $response['request_items']);
|
||||
$this->assertEquals('accepted', $response['request_items'][0]['status']);
|
||||
$this->assertFalse($response['errors']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $requestData
|
||||
* @param string $urlParam
|
||||
* @param string|null $storeCode
|
||||
* @return mixed
|
||||
*/
|
||||
private function saveConfigurableProductsBySku($requestData, $urlParam, $storeCode = null)
|
||||
{
|
||||
$serviceInfo = [
|
||||
'rest' => [
|
||||
'resourcePath' => self::ASYNC_BULK_RESOURCE_PATH.'/bySku/'.$urlParam,
|
||||
'httpMethod' => Request::HTTP_METHOD_POST,
|
||||
],
|
||||
];
|
||||
|
||||
return $this->_webApiCall($serviceInfo, $requestData, null, $storeCode);
|
||||
}
|
||||
}
|
||||
+461
@@ -0,0 +1,461 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\WebapiAsync\Model;
|
||||
|
||||
use Magento\Catalog\Api\Data\ProductInterface;
|
||||
use Magento\Framework\Exception\NoSuchEntityException;
|
||||
use Magento\Framework\Exception\NotFoundException;
|
||||
use Magento\TestFramework\MessageQueue\PreconditionFailedException;
|
||||
use Magento\TestFramework\MessageQueue\PublisherConsumerController;
|
||||
use Magento\TestFramework\MessageQueue\EnvironmentPreconditionException;
|
||||
use Magento\TestFramework\TestCase\WebapiAbstract;
|
||||
use Magento\TestFramework\Helper\Bootstrap;
|
||||
use Magento\Catalog\Model\ResourceModel\Product\Collection;
|
||||
use Magento\Framework\Phrase;
|
||||
use Magento\Framework\Registry;
|
||||
use Magento\Framework\Webapi\Exception;
|
||||
use Magento\Catalog\Api\ProductRepositoryInterface;
|
||||
|
||||
/**
|
||||
* Check async request for multiple products creation service,
|
||||
* scheduling bulk to rabbitmq
|
||||
* running consumers and check async.operation.add consumer
|
||||
* check if product was created by async bulk requests
|
||||
*
|
||||
* @magentoAppIsolation enabled
|
||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
|
||||
*/
|
||||
class AsyncBulkScheduleTest extends WebapiAbstract
|
||||
{
|
||||
public const SERVICE_NAME = 'catalogProductRepositoryV1';
|
||||
public const SERVICE_VERSION = 'V1';
|
||||
public const REST_RESOURCE_PATH = '/V1/products';
|
||||
public const ASYNC_BULK_RESOURCE_PATH = '/async/bulk/V1/products';
|
||||
public const ASYNC_CONSUMER_NAME = 'async.operations.all';
|
||||
|
||||
public const KEY_TIER_PRICES = 'tier_prices';
|
||||
public const KEY_SPECIAL_PRICE = 'special_price';
|
||||
public const KEY_CATEGORY_LINKS = 'category_links';
|
||||
|
||||
public const BULK_UUID_KEY = 'bulk_uuid';
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $consumers = [
|
||||
self::ASYNC_CONSUMER_NAME,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $skus = [];
|
||||
|
||||
/**
|
||||
* @var PublisherConsumerController
|
||||
*/
|
||||
private $publisherConsumerController;
|
||||
|
||||
/**
|
||||
* @var ProductRepositoryInterface
|
||||
*/
|
||||
private $productRepository;
|
||||
|
||||
/**
|
||||
* @var \Magento\Framework\ObjectManagerInterface
|
||||
*/
|
||||
private $objectManager;
|
||||
|
||||
/**
|
||||
* @var Registry
|
||||
*/
|
||||
private $registry;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->objectManager = Bootstrap::getObjectManager();
|
||||
$logFilePath = TESTS_TEMP_DIR . "/MessageQueueTestLog.txt";
|
||||
$this->registry = $this->objectManager->get(Registry::class);
|
||||
|
||||
$params = array_merge_recursive(
|
||||
\Magento\TestFramework\Helper\Bootstrap::getInstance()->getAppInitParams(),
|
||||
['MAGE_DIRS' => ['cache' => ['path' => TESTS_TEMP_DIR . '/cache']]]
|
||||
);
|
||||
|
||||
/** @var PublisherConsumerController publisherConsumerController */
|
||||
$this->publisherConsumerController = $this->objectManager->create(PublisherConsumerController::class, [
|
||||
'consumers' => $this->consumers,
|
||||
'logFilePath' => $logFilePath,
|
||||
'appInitParams' => $params,
|
||||
]);
|
||||
$this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
|
||||
|
||||
try {
|
||||
$this->publisherConsumerController->initialize();
|
||||
} catch (EnvironmentPreconditionException $e) {
|
||||
$this->markTestSkipped($e->getMessage());
|
||||
} catch (PreconditionFailedException $e) {
|
||||
$this->fail(
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider productsArrayCreationProvider
|
||||
*/
|
||||
public function testAsyncScheduleBulkMultipleEntities($products)
|
||||
{
|
||||
$this->_markTestAsRestOnly();
|
||||
$this->skus = [];
|
||||
foreach ($products as $product) {
|
||||
$this->skus[] = $product['product'][ProductInterface::SKU];
|
||||
}
|
||||
$this->clearProducts();
|
||||
|
||||
$response = $this->saveProductAsync($products);
|
||||
$this->assertArrayHasKey(self::BULK_UUID_KEY, $response);
|
||||
$this->assertNotNull($response[self::BULK_UUID_KEY]);
|
||||
|
||||
$this->assertCount(2, $response['request_items']);
|
||||
foreach ($products as $key => $product) {
|
||||
$this->assertEquals('accepted', $response['request_items'][$key]['status']);
|
||||
}
|
||||
$this->assertFalse($response['errors']);
|
||||
|
||||
//assert one products is created
|
||||
try {
|
||||
$this->publisherConsumerController->waitForAsynchronousResult(
|
||||
[$this, 'assertProductCreation'],
|
||||
[$products]
|
||||
);
|
||||
} catch (PreconditionFailedException $e) {
|
||||
$this->fail("Not all products were created");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider productSingleCreationProvider
|
||||
*/
|
||||
public function testAsyncScheduleBulkSingleEntity($products)
|
||||
{
|
||||
$this->_markTestAsRestOnly();
|
||||
$this->skus = [];
|
||||
$this->skus[] = $products[0]['product'][ProductInterface::SKU];
|
||||
$this->clearProducts();
|
||||
|
||||
$response = $this->saveProductAsync($products);
|
||||
$this->assertArrayHasKey(self::BULK_UUID_KEY, $response);
|
||||
$this->assertNotNull($response[self::BULK_UUID_KEY]);
|
||||
|
||||
$this->assertCount(1, $response['request_items']);
|
||||
$this->assertEquals('accepted', $response['request_items'][0]['status']);
|
||||
$this->assertFalse($response['errors']);
|
||||
|
||||
//assert one products is created
|
||||
try {
|
||||
$this->publisherConsumerController->waitForAsynchronousResult(
|
||||
[$this, 'assertProductCreation'],
|
||||
[$products]
|
||||
);
|
||||
} catch (PreconditionFailedException $e) {
|
||||
$this->fail("Not all products were created");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider wrongProductCreationProvider
|
||||
*/
|
||||
public function testAsyncScheduleBulkWrongEntity($products)
|
||||
{
|
||||
$this->_markTestAsRestOnly();
|
||||
$this->skus = [];
|
||||
foreach ($products as $product) {
|
||||
$this->skus[] = $product['product'][ProductInterface::SKU];
|
||||
}
|
||||
$this->clearProducts();
|
||||
|
||||
$response = null;
|
||||
try {
|
||||
$response = $this->saveProductAsync($products);
|
||||
} catch (\Exception $e) {
|
||||
$this->assertEquals(400, $e->getCode());
|
||||
}
|
||||
$this->assertNull($response);
|
||||
$this->assertEquals(0, $this->checkProductsCreation());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sku
|
||||
* @param string|null $storeCode
|
||||
* @dataProvider productGetDataProvider
|
||||
*/
|
||||
public function testGETRequestToAsyncBulk($sku, $storeCode = null)
|
||||
{
|
||||
$this->expectException(\Exception::class);
|
||||
$this->expectExceptionMessage('Specified request cannot be processed.');
|
||||
|
||||
$this->_markTestAsRestOnly();
|
||||
$serviceInfo = [
|
||||
'rest' => [
|
||||
'resourcePath' => self::ASYNC_BULK_RESOURCE_PATH . '/' . $sku,
|
||||
'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
|
||||
],
|
||||
];
|
||||
|
||||
$response = null;
|
||||
try {
|
||||
$response = $this->_webApiCall($serviceInfo, [ProductInterface::SKU => $sku], null, $storeCode);
|
||||
} catch (NotFoundException $e) {
|
||||
$this->assertEquals(400, $e->getCode());
|
||||
}
|
||||
$this->assertNull($response);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->clearProducts();
|
||||
$this->publisherConsumerController->stopConsumers();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
private function clearProducts()
|
||||
{
|
||||
$size = $this->objectManager->create(Collection::class)
|
||||
->addAttributeToFilter('sku', ['in' => $this->skus])
|
||||
->load()
|
||||
->getSize();
|
||||
|
||||
if ($size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->registry->unregister('isSecureArea');
|
||||
$this->registry->register('isSecureArea', true);
|
||||
try {
|
||||
foreach ($this->skus as $sku) {
|
||||
$this->productRepository->deleteById($sku);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw $e;
|
||||
//nothing to delete
|
||||
}
|
||||
$this->registry->unregister('isSecureArea');
|
||||
|
||||
$size = $this->objectManager->create(Collection::class)
|
||||
->addAttributeToFilter('sku', ['in' => $this->skus])
|
||||
->load()
|
||||
->getSize();
|
||||
|
||||
if ($size > 0) {
|
||||
throw new Exception(new Phrase("Collection size after clearing the products: %size", ['size' => $size]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function productsArrayCreationProvider()
|
||||
{
|
||||
$productBuilder = function ($data) {
|
||||
return array_replace_recursive(
|
||||
$this->getSimpleProductData(),
|
||||
$data
|
||||
);
|
||||
};
|
||||
|
||||
return
|
||||
[
|
||||
[
|
||||
[
|
||||
[
|
||||
'product' =>
|
||||
$productBuilder([
|
||||
ProductInterface::TYPE_ID => 'simple',
|
||||
ProductInterface::SKU => 'psku-test-1-multiple',
|
||||
]),
|
||||
],
|
||||
[
|
||||
'product' => $productBuilder([
|
||||
ProductInterface::TYPE_ID => 'virtual',
|
||||
ProductInterface::SKU => 'psku-test-2-multiple',
|
||||
]),
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function productSingleCreationProvider()
|
||||
{
|
||||
$productBuilder = function ($data) {
|
||||
return array_replace_recursive(
|
||||
$this->getSimpleProductData(),
|
||||
$data
|
||||
);
|
||||
};
|
||||
|
||||
return
|
||||
[
|
||||
[
|
||||
[
|
||||
[
|
||||
'product' =>
|
||||
$productBuilder([
|
||||
ProductInterface::TYPE_ID => 'simple',
|
||||
ProductInterface::SKU => 'psku-test-1-single',
|
||||
]),
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function wrongProductCreationProvider()
|
||||
{
|
||||
$productBuilder = function ($data) {
|
||||
return array_replace_recursive(
|
||||
$this->getSimpleProductData(),
|
||||
$data
|
||||
);
|
||||
};
|
||||
|
||||
$wrongProductBuilder = function ($data) {
|
||||
return array_replace_recursive(
|
||||
$this->getWrongProductStructureData(),
|
||||
$data
|
||||
);
|
||||
};
|
||||
|
||||
return
|
||||
[
|
||||
[
|
||||
[
|
||||
[
|
||||
'product' =>
|
||||
$productBuilder([
|
||||
ProductInterface::TYPE_ID => 'simple',
|
||||
ProductInterface::SKU => 'psku-test-1-wrong',
|
||||
]),
|
||||
],
|
||||
[
|
||||
'product' => $productBuilder([
|
||||
ProductInterface::TYPE_ID => 'virtual',
|
||||
ProductInterface::SKU => 'psku-test-2-wrong',
|
||||
]),
|
||||
],
|
||||
[
|
||||
'product' =>
|
||||
$wrongProductBuilder([
|
||||
'wrong_attribute' => 'simple',
|
||||
ProductInterface::SKU => 'psku-test-3-wrong',
|
||||
]),
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function productGetDataProvider()
|
||||
{
|
||||
return [
|
||||
['psku-test-1', null],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Simple Product Data
|
||||
*
|
||||
* @param array $productData
|
||||
* @return array
|
||||
*/
|
||||
private function getSimpleProductData($productData = [])
|
||||
{
|
||||
return [
|
||||
ProductInterface::SKU => isset($productData[ProductInterface::SKU])
|
||||
? $productData[ProductInterface::SKU] : uniqid('sku-', true),
|
||||
ProductInterface::NAME => isset($productData[ProductInterface::NAME])
|
||||
? $productData[ProductInterface::NAME] : uniqid('sku-', true),
|
||||
ProductInterface::VISIBILITY => 4,
|
||||
ProductInterface::TYPE_ID => 'simple',
|
||||
ProductInterface::PRICE => 3.62,
|
||||
ProductInterface::STATUS => 1,
|
||||
ProductInterface::ATTRIBUTE_SET_ID => 4,
|
||||
'custom_attributes' => [
|
||||
['attribute_code' => 'cost', 'value' => ''],
|
||||
['attribute_code' => 'description', 'value' => 'Description'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Wrong Simple Product Data without required attributes
|
||||
*
|
||||
* @param array $productData
|
||||
* @return array
|
||||
*/
|
||||
private function getWrongProductStructureData($productData = [])
|
||||
{
|
||||
return [
|
||||
ProductInterface::SKU => isset($productData[ProductInterface::SKU])
|
||||
? $productData[ProductInterface::SKU] : uniqid('sku-', true),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $requestData
|
||||
* @param string|null $storeCode
|
||||
* @return mixed
|
||||
*/
|
||||
private function saveProductAsync($requestData, $storeCode = null)
|
||||
{
|
||||
$serviceInfo = [
|
||||
'rest' => [
|
||||
'resourcePath' => self::ASYNC_BULK_RESOURCE_PATH,
|
||||
'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST,
|
||||
],
|
||||
];
|
||||
|
||||
return $this->_webApiCall($serviceInfo, $requestData, null, $storeCode);
|
||||
}
|
||||
|
||||
public function assertProductCreation()
|
||||
{
|
||||
$collection = $this->objectManager->create(Collection::class)
|
||||
->addAttributeToFilter('sku', ['in' => $this->skus])
|
||||
->load();
|
||||
$size = $collection->getSize();
|
||||
|
||||
return $size == count($this->skus);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function checkProductsCreation()
|
||||
{
|
||||
$collection = $this->objectManager->create(Collection::class)
|
||||
->addAttributeToFilter('sku', ['in' => $this->skus])
|
||||
->load();
|
||||
$size = $collection->getSize();
|
||||
|
||||
return $size;
|
||||
}
|
||||
}
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\WebapiAsync\Model;
|
||||
|
||||
use Magento\Catalog\Api\Data\ProductInterface;
|
||||
use Magento\Framework\Exception\NotFoundException;
|
||||
use Magento\TestFramework\MessageQueue\PreconditionFailedException;
|
||||
use Magento\TestFramework\MessageQueue\PublisherConsumerController;
|
||||
use Magento\TestFramework\MessageQueue\EnvironmentPreconditionException;
|
||||
use Magento\TestFramework\TestCase\WebapiAbstract;
|
||||
use Magento\TestFramework\Helper\Bootstrap;
|
||||
use Magento\Catalog\Model\ResourceModel\Product\Collection;
|
||||
use Magento\Framework\Phrase;
|
||||
use Magento\Framework\Registry;
|
||||
use Magento\Framework\Webapi\Exception;
|
||||
use Magento\Catalog\Api\ProductRepositoryInterface;
|
||||
|
||||
/**
|
||||
* Check async request for product creation service using custom route, scheduling bulk to rabbitmq
|
||||
* running consumers and check async.operation.add consumer
|
||||
* check if product was created by async requests
|
||||
*
|
||||
* @magentoAppIsolation enabled
|
||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
|
||||
*/
|
||||
class AsyncScheduleCustomRouteTest extends WebapiAbstract
|
||||
{
|
||||
const ASYNC_RESOURCE_CUSTOM_PATH = '/asyncProducts';
|
||||
const ASYNC_CONSUMER_NAME = 'async.operations.all';
|
||||
|
||||
const KEY_TIER_PRICES = 'tier_prices';
|
||||
const KEY_SPECIAL_PRICE = 'special_price';
|
||||
const KEY_CATEGORY_LINKS = 'category_links';
|
||||
|
||||
const BULK_UUID_KEY = 'bulk_uuid';
|
||||
|
||||
protected $consumers = [
|
||||
self::ASYNC_CONSUMER_NAME,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $skus = [];
|
||||
|
||||
/**
|
||||
* @var PublisherConsumerController
|
||||
*/
|
||||
private $publisherConsumerController;
|
||||
|
||||
/**
|
||||
* @var ProductRepositoryInterface
|
||||
*/
|
||||
private $productRepository;
|
||||
|
||||
/**
|
||||
* @var \Magento\Framework\ObjectManagerInterface
|
||||
*/
|
||||
private $objectManager;
|
||||
|
||||
/**
|
||||
* @var Registry
|
||||
*/
|
||||
private $registry;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->objectManager = Bootstrap::getObjectManager();
|
||||
$logFilePath = TESTS_TEMP_DIR . "/MessageQueueTestLog.txt";
|
||||
$this->registry = $this->objectManager->get(Registry::class);
|
||||
|
||||
$params = array_merge_recursive(
|
||||
\Magento\TestFramework\Helper\Bootstrap::getInstance()->getAppInitParams(),
|
||||
['MAGE_DIRS' => ['cache' => ['path' => TESTS_TEMP_DIR . '/cache']]]
|
||||
);
|
||||
|
||||
/** @var PublisherConsumerController publisherConsumerController */
|
||||
$this->publisherConsumerController = $this->objectManager->create(PublisherConsumerController::class, [
|
||||
'consumers' => $this->consumers,
|
||||
'logFilePath' => $logFilePath,
|
||||
'appInitParams' => $params,
|
||||
]);
|
||||
$this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
|
||||
|
||||
try {
|
||||
$this->publisherConsumerController->initialize();
|
||||
} catch (EnvironmentPreconditionException $e) {
|
||||
$this->markTestSkipped($e->getMessage());
|
||||
} catch (PreconditionFailedException $e) {
|
||||
$this->fail(
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider productCreationProvider
|
||||
*/
|
||||
public function testAsyncScheduleBulkByCustomRoute($product)
|
||||
{
|
||||
$this->_markTestAsRestOnly();
|
||||
$this->skus[] = $product['product'][ProductInterface::SKU];
|
||||
$this->clearProducts();
|
||||
|
||||
$response = $this->saveProductByCustomRoute($product);
|
||||
$this->assertArrayHasKey(self::BULK_UUID_KEY, $response);
|
||||
$this->assertNotNull($response[self::BULK_UUID_KEY]);
|
||||
|
||||
$this->assertCount(1, $response['request_items']);
|
||||
$this->assertEquals('accepted', $response['request_items'][0]['status']);
|
||||
$this->assertFalse($response['errors']);
|
||||
|
||||
//assert one products is created
|
||||
try {
|
||||
$this->publisherConsumerController->waitForAsynchronousResult(
|
||||
[$this, 'assertProductCreation'],
|
||||
[$product]
|
||||
);
|
||||
} catch (PreconditionFailedException $e) {
|
||||
$this->fail("Not all products were created");
|
||||
}
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->clearProducts();
|
||||
$this->publisherConsumerController->stopConsumers();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
private function clearProducts()
|
||||
{
|
||||
$size = $this->objectManager->create(Collection::class)
|
||||
->addAttributeToFilter('sku', ['in' => $this->skus])
|
||||
->load()
|
||||
->getSize();
|
||||
|
||||
if ($size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->registry->unregister('isSecureArea');
|
||||
$this->registry->register('isSecureArea', true);
|
||||
try {
|
||||
foreach ($this->skus as $sku) {
|
||||
$this->productRepository->deleteById($sku);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw $e;
|
||||
//nothing to delete
|
||||
}
|
||||
$this->registry->unregister('isSecureArea');
|
||||
|
||||
$size = $this->objectManager->create(Collection::class)
|
||||
->addAttributeToFilter('sku', ['in' => $this->skus])
|
||||
->load()
|
||||
->getSize();
|
||||
|
||||
if ($size > 0) {
|
||||
throw new Exception(new Phrase("Collection size after clearing the products: %size", ['size' => $size]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function productCreationProvider()
|
||||
{
|
||||
$productBuilder = function ($data) {
|
||||
return array_replace_recursive(
|
||||
$this->getSimpleProductData(),
|
||||
$data
|
||||
);
|
||||
};
|
||||
|
||||
return [
|
||||
[
|
||||
[
|
||||
'product' =>
|
||||
$productBuilder([
|
||||
ProductInterface::TYPE_ID => 'simple',
|
||||
ProductInterface::SKU => 'psku-test-1',
|
||||
]),
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
'product' => $productBuilder([
|
||||
ProductInterface::TYPE_ID => 'virtual',
|
||||
ProductInterface::SKU => 'psku-test-2',
|
||||
]),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Simple Product Data
|
||||
*
|
||||
* @param array $productData
|
||||
* @return array
|
||||
*/
|
||||
private function getSimpleProductData($productData = [])
|
||||
{
|
||||
return [
|
||||
ProductInterface::SKU => isset($productData[ProductInterface::SKU])
|
||||
? $productData[ProductInterface::SKU] : uniqid('sku-', true),
|
||||
ProductInterface::NAME => isset($productData[ProductInterface::NAME])
|
||||
? $productData[ProductInterface::NAME] : uniqid('sku-', true),
|
||||
ProductInterface::VISIBILITY => 4,
|
||||
ProductInterface::TYPE_ID => 'simple',
|
||||
ProductInterface::PRICE => 3.62,
|
||||
ProductInterface::STATUS => 1,
|
||||
ProductInterface::ATTRIBUTE_SET_ID => 4,
|
||||
'custom_attributes' => [
|
||||
['attribute_code' => 'cost', 'value' => ''],
|
||||
['attribute_code' => 'description', 'value' => 'Description'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $requestData
|
||||
* @param string|null $storeCode
|
||||
* @return mixed
|
||||
*/
|
||||
private function saveProductByCustomRoute($requestData, $storeCode = null)
|
||||
{
|
||||
$serviceInfo = [
|
||||
'rest' => [
|
||||
'resourcePath' => self::ASYNC_RESOURCE_CUSTOM_PATH,
|
||||
'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST,
|
||||
],
|
||||
];
|
||||
|
||||
return $this->_webApiCall($serviceInfo, $requestData, null, $storeCode);
|
||||
}
|
||||
|
||||
public function assertProductCreation()
|
||||
{
|
||||
$collection = $this->objectManager->create(Collection::class)
|
||||
->addAttributeToFilter('sku', ['in' => $this->skus])
|
||||
->load();
|
||||
$size = $collection->getSize();
|
||||
|
||||
return $size == count($this->skus);
|
||||
}
|
||||
}
|
||||
+363
@@ -0,0 +1,363 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\WebapiAsync\Model;
|
||||
|
||||
use Magento\Catalog\Api\Data\ProductInterface;
|
||||
use Magento\Catalog\Api\ProductRepositoryInterface;
|
||||
use Magento\Catalog\Model\ResourceModel\Product\Collection;
|
||||
use Magento\Framework\ObjectManagerInterface;
|
||||
use Magento\Framework\Phrase;
|
||||
use Magento\Framework\Registry;
|
||||
use Magento\Framework\Webapi\Exception;
|
||||
use Magento\Framework\Webapi\Rest\Request;
|
||||
use Magento\Store\Model\Store;
|
||||
use Magento\TestFramework\Helper\Bootstrap;
|
||||
use Magento\TestFramework\MessageQueue\EnvironmentPreconditionException;
|
||||
use Magento\TestFramework\MessageQueue\PreconditionFailedException;
|
||||
use Magento\TestFramework\MessageQueue\PublisherConsumerController;
|
||||
use Magento\TestFramework\TestCase\WebapiAbstract;
|
||||
|
||||
/**
|
||||
* Check async request for multistore product creation service, scheduling bulk
|
||||
* to rabbitmq running consumers and check async.operation.add consumer check
|
||||
* if product was created by async requests
|
||||
*
|
||||
* @magentoAppIsolation enabled
|
||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
|
||||
*/
|
||||
class AsyncScheduleMultiStoreTest extends WebapiAbstract
|
||||
{
|
||||
const SERVICE_NAME = 'catalogProductRepositoryV1';
|
||||
const SERVICE_VERSION = 'V1';
|
||||
const REST_RESOURCE_PATH = '/V1/products';
|
||||
const ASYNC_RESOURCE_PATH = '/async/V1/products';
|
||||
const ASYNC_CONSUMER_NAME = 'async.operations.all';
|
||||
|
||||
const STORE_CODE_FROM_FIXTURE = 'fixturestore';
|
||||
const STORE_NAME_FROM_FIXTURE = 'Fixture Store';
|
||||
|
||||
const STORE_CODE_ALL = 'all';
|
||||
const STORE_CODE_DEFAULT = 'default';
|
||||
|
||||
private $stores = [
|
||||
self::STORE_CODE_DEFAULT,
|
||||
self::STORE_CODE_ALL,
|
||||
self::STORE_CODE_FROM_FIXTURE,
|
||||
];
|
||||
|
||||
const KEY_TIER_PRICES = 'tier_prices';
|
||||
const KEY_SPECIAL_PRICE = 'special_price';
|
||||
const KEY_CATEGORY_LINKS = 'category_links';
|
||||
|
||||
const BULK_UUID_KEY = 'bulk_uuid';
|
||||
|
||||
protected $consumers = [
|
||||
self::ASYNC_CONSUMER_NAME,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $skus = [];
|
||||
|
||||
/**
|
||||
* @var PublisherConsumerController
|
||||
*/
|
||||
private $publisherConsumerController;
|
||||
|
||||
/**
|
||||
* @var ProductRepositoryInterface
|
||||
*/
|
||||
private $productRepository;
|
||||
|
||||
/**
|
||||
* @var ObjectManagerInterface
|
||||
*/
|
||||
private $objectManager;
|
||||
|
||||
/**
|
||||
* @var Registry
|
||||
*/
|
||||
private $registry;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
$logFilePath = TESTS_TEMP_DIR . "/MessageQueueTestLog.txt";
|
||||
$this->objectManager = Bootstrap::getObjectManager();
|
||||
$this->registry = $this->objectManager->get(Registry::class);
|
||||
|
||||
$this->publisherConsumerController = $this->objectManager->create(
|
||||
PublisherConsumerController::class,
|
||||
[
|
||||
'consumers' => $this->consumers,
|
||||
'logFilePath' => $logFilePath,
|
||||
'appInitParams' => Bootstrap::getInstance()->getAppInitParams(),
|
||||
]
|
||||
);
|
||||
$this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
|
||||
|
||||
try {
|
||||
$this->publisherConsumerController->initialize();
|
||||
} catch (EnvironmentPreconditionException $e) {
|
||||
$this->markTestSkipped($e->getMessage());
|
||||
} catch (PreconditionFailedException $e) {
|
||||
$this->fail(
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider storeProvider
|
||||
* @magentoApiDataFixture Magento/Store/_files/core_fixturestore.php
|
||||
* @param string|null $storeCode
|
||||
* @return void
|
||||
*/
|
||||
public function testAsyncScheduleBulkMultistore(?string $storeCode): void
|
||||
{
|
||||
$product = $this->getProductData();
|
||||
$this->_markTestAsRestOnly();
|
||||
|
||||
/** @var Store $store */
|
||||
$store = $this->objectManager->get(Store::class);
|
||||
$store->load(self::STORE_CODE_FROM_FIXTURE);
|
||||
$this->assertEquals(
|
||||
self::STORE_NAME_FROM_FIXTURE,
|
||||
$store->getName(),
|
||||
'Precondition failed: fixture store was not created.'
|
||||
);
|
||||
|
||||
try {
|
||||
/** @var ProductInterface $productModel */
|
||||
$productModel = $this->objectManager->create(
|
||||
ProductInterface::class,
|
||||
['data' => $product['product']]
|
||||
);
|
||||
$this->productRepository->save($productModel);
|
||||
} catch (\Exception $e) {
|
||||
$this->fail("Precondition failed: product was not created.");
|
||||
}
|
||||
|
||||
$this->asyncScheduleAndTest($product, $storeCode);
|
||||
$this->clearProducts();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $product
|
||||
* @param string|null $storeCode
|
||||
* @return void
|
||||
*/
|
||||
private function asyncScheduleAndTest(array $product, $storeCode = null): void
|
||||
{
|
||||
$sku = $product['product'][ProductInterface::SKU];
|
||||
$productName = $product['product'][ProductInterface::NAME];
|
||||
$newProductName = $product['product'][ProductInterface::NAME] . $storeCode;
|
||||
|
||||
$this->skus[] = $sku;
|
||||
|
||||
$product['product'][ProductInterface::NAME] = $newProductName;
|
||||
$product['product'][ProductInterface::TYPE_ID] = 'virtual';
|
||||
|
||||
$response = $this->updateProductAsync($product, $sku, $storeCode);
|
||||
|
||||
$this->assertArrayHasKey(self::BULK_UUID_KEY, $response);
|
||||
$this->assertNotNull($response[self::BULK_UUID_KEY]);
|
||||
|
||||
$this->assertCount(1, $response['request_items']);
|
||||
$this->assertEquals('accepted', $response['request_items'][0]['status']);
|
||||
$this->assertFalse($response['errors']);
|
||||
|
||||
//assert one products is created
|
||||
try {
|
||||
$this->publisherConsumerController->waitForAsynchronousResult(
|
||||
[$this, 'assertProductCreation'],
|
||||
[$product]
|
||||
);
|
||||
} catch (PreconditionFailedException $e) {
|
||||
$this->fail("Not all products were created");
|
||||
}
|
||||
|
||||
$requestData = ['id' => $sku, 'sku' => $sku];
|
||||
|
||||
foreach ($this->stores as $checkingStore) {
|
||||
$serviceInfo = [
|
||||
'rest' => [
|
||||
'resourcePath' => self::REST_RESOURCE_PATH . '/' . $sku,
|
||||
'httpMethod' => Request::HTTP_METHOD_GET,
|
||||
]
|
||||
];
|
||||
$storeResponse = $this->_webApiCall($serviceInfo, $requestData, null, $checkingStore);
|
||||
if ($checkingStore == $storeCode || $storeCode == self::STORE_CODE_ALL) {
|
||||
$this->assertEquals(
|
||||
$newProductName,
|
||||
$storeResponse[ProductInterface::NAME],
|
||||
sprintf(
|
||||
'Product name in %s store is invalid after updating in store %s.',
|
||||
$checkingStore,
|
||||
$storeCode
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
$productName,
|
||||
$storeResponse[ProductInterface::NAME],
|
||||
sprintf(
|
||||
'Product name in %s store is invalid after updating in store %s.',
|
||||
$checkingStore,
|
||||
$storeCode
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->clearProducts();
|
||||
$this->publisherConsumerController->stopConsumers();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function clearProducts(): void
|
||||
{
|
||||
$size = $this->objectManager->create(Collection::class)
|
||||
->addAttributeToFilter('sku', ['in' => $this->skus])
|
||||
->load()
|
||||
->getSize();
|
||||
|
||||
if ($size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->registry->unregister('isSecureArea');
|
||||
$this->registry->register('isSecureArea', true);
|
||||
try {
|
||||
foreach ($this->skus as $sku) {
|
||||
$this->productRepository->deleteById($sku);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw $e;
|
||||
//nothing to delete
|
||||
}
|
||||
$this->registry->unregister('isSecureArea');
|
||||
|
||||
$size = $this->objectManager->create(Collection::class)
|
||||
->addAttributeToFilter('sku', ['in' => $this->skus])
|
||||
->load()
|
||||
->getSize();
|
||||
|
||||
if ($size > 0) {
|
||||
//phpcs:ignore Magento2.Exceptions.DirectThrow
|
||||
throw new Exception(new Phrase("Collection size after clearing the products: %size", ['size' => $size]));
|
||||
}
|
||||
$this->skus = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getProductData(): array
|
||||
{
|
||||
$productBuilder = function ($data) {
|
||||
return array_replace_recursive(
|
||||
$this->getSimpleProductData(),
|
||||
$data
|
||||
);
|
||||
};
|
||||
|
||||
return [
|
||||
'product' => $productBuilder(
|
||||
[
|
||||
ProductInterface::TYPE_ID => 'simple',
|
||||
ProductInterface::SKU => 'multistore-sku-test-1',
|
||||
ProductInterface::NAME => 'Test Name ',
|
||||
]
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function storeProvider(): array
|
||||
{
|
||||
$dataSets = [];
|
||||
foreach ($this->stores as $store) {
|
||||
$dataSets[$store] = [$store];
|
||||
}
|
||||
|
||||
return $dataSets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Simple Product Data
|
||||
*
|
||||
* @param array $productData
|
||||
* @return array
|
||||
*/
|
||||
private function getSimpleProductData($productData = []): array
|
||||
{
|
||||
return [
|
||||
ProductInterface::SKU => isset($productData[ProductInterface::SKU])
|
||||
? $productData[ProductInterface::SKU] : uniqid('sku-', true),
|
||||
ProductInterface::NAME => isset($productData[ProductInterface::NAME])
|
||||
? $productData[ProductInterface::NAME] : uniqid('sku-', true),
|
||||
ProductInterface::VISIBILITY => 4,
|
||||
ProductInterface::TYPE_ID => 'simple',
|
||||
ProductInterface::PRICE => 3.62,
|
||||
ProductInterface::STATUS => 1,
|
||||
ProductInterface::ATTRIBUTE_SET_ID => 4,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $requestData
|
||||
* @param string $sku
|
||||
* @param string|null $storeCode
|
||||
* @return mixed
|
||||
*/
|
||||
private function updateProductAsync(array $requestData, string $sku, $storeCode = null)
|
||||
{
|
||||
$serviceInfo = [
|
||||
'rest' => [
|
||||
'resourcePath' => self::ASYNC_RESOURCE_PATH . '/' . $sku,
|
||||
'httpMethod' => Request::HTTP_METHOD_PUT,
|
||||
],
|
||||
];
|
||||
|
||||
return $this->_webApiCall($serviceInfo, $requestData, null, $storeCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $product
|
||||
* @return bool
|
||||
*/
|
||||
public function assertProductCreation(array $product): bool
|
||||
{
|
||||
$sku = $product['product'][ProductInterface::SKU];
|
||||
$collection = $this->objectManager->create(Collection::class)
|
||||
->addAttributeToFilter(ProductInterface::SKU, ['eq' => $sku])
|
||||
->addAttributeToFilter(ProductInterface::TYPE_ID, ['eq' => 'virtual'])
|
||||
->load();
|
||||
$size = $collection->getSize();
|
||||
|
||||
return $size > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\WebapiAsync\Model;
|
||||
|
||||
use Magento\Catalog\Api\Data\ProductInterface;
|
||||
use Magento\Framework\Exception\NotFoundException;
|
||||
use Magento\TestFramework\MessageQueue\PreconditionFailedException;
|
||||
use Magento\TestFramework\MessageQueue\PublisherConsumerController;
|
||||
use Magento\TestFramework\MessageQueue\EnvironmentPreconditionException;
|
||||
use Magento\TestFramework\TestCase\WebapiAbstract;
|
||||
use Magento\TestFramework\Helper\Bootstrap;
|
||||
use Magento\Catalog\Model\ResourceModel\Product\Collection;
|
||||
use Magento\Framework\Phrase;
|
||||
use Magento\Framework\Registry;
|
||||
use Magento\Framework\Webapi\Exception;
|
||||
use Magento\Catalog\Api\ProductRepositoryInterface;
|
||||
|
||||
/**
|
||||
* Check async request for product creation service, scheduling bulk to rabbitmq
|
||||
* running consumers and check async.operation.add consumer
|
||||
* check if product was created by async requests
|
||||
*
|
||||
* @magentoAppIsolation enabled
|
||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
|
||||
*/
|
||||
class AsyncScheduleTest extends WebapiAbstract
|
||||
{
|
||||
const SERVICE_NAME = 'catalogProductRepositoryV1';
|
||||
const SERVICE_VERSION = 'V1';
|
||||
const REST_RESOURCE_PATH = '/V1/products';
|
||||
const ASYNC_RESOURCE_PATH = '/async/V1/products';
|
||||
const ASYNC_CONSUMER_NAME = 'async.operations.all';
|
||||
|
||||
const KEY_TIER_PRICES = 'tier_prices';
|
||||
const KEY_SPECIAL_PRICE = 'special_price';
|
||||
const KEY_CATEGORY_LINKS = 'category_links';
|
||||
|
||||
const BULK_UUID_KEY = 'bulk_uuid';
|
||||
|
||||
protected $consumers = [
|
||||
self::ASYNC_CONSUMER_NAME,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $skus = [];
|
||||
|
||||
/**
|
||||
* @var PublisherConsumerController
|
||||
*/
|
||||
private $publisherConsumerController;
|
||||
|
||||
/**
|
||||
* @var ProductRepositoryInterface
|
||||
*/
|
||||
private $productRepository;
|
||||
|
||||
/**
|
||||
* @var \Magento\Framework\ObjectManagerInterface
|
||||
*/
|
||||
private $objectManager;
|
||||
|
||||
/**
|
||||
* @var Registry
|
||||
*/
|
||||
private $registry;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->objectManager = Bootstrap::getObjectManager();
|
||||
$logFilePath = TESTS_TEMP_DIR . "/MessageQueueTestLog.txt";
|
||||
$this->registry = $this->objectManager->get(Registry::class);
|
||||
|
||||
$params = array_merge_recursive(
|
||||
\Magento\TestFramework\Helper\Bootstrap::getInstance()->getAppInitParams(),
|
||||
['MAGE_DIRS' => ['cache' => ['path' => TESTS_TEMP_DIR . '/cache']]]
|
||||
);
|
||||
|
||||
/** @var PublisherConsumerController publisherConsumerController */
|
||||
$this->publisherConsumerController = $this->objectManager->create(PublisherConsumerController::class, [
|
||||
'consumers' => $this->consumers,
|
||||
'logFilePath' => $logFilePath,
|
||||
'appInitParams' => $params,
|
||||
]);
|
||||
$this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
|
||||
|
||||
try {
|
||||
$this->publisherConsumerController->initialize();
|
||||
} catch (EnvironmentPreconditionException $e) {
|
||||
$this->markTestSkipped($e->getMessage());
|
||||
} catch (PreconditionFailedException $e) {
|
||||
$this->fail(
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider productCreationProvider
|
||||
*/
|
||||
public function testAsyncScheduleBulk($product)
|
||||
{
|
||||
$this->_markTestAsRestOnly();
|
||||
$this->skus[] = $product['product'][ProductInterface::SKU];
|
||||
$this->clearProducts();
|
||||
|
||||
$response = $this->saveProductAsync($product);
|
||||
$this->assertArrayHasKey(self::BULK_UUID_KEY, $response);
|
||||
$this->assertNotNull($response[self::BULK_UUID_KEY]);
|
||||
|
||||
$this->assertCount(1, $response['request_items']);
|
||||
$this->assertEquals('accepted', $response['request_items'][0]['status']);
|
||||
$this->assertFalse($response['errors']);
|
||||
|
||||
//assert one products is created
|
||||
try {
|
||||
$this->publisherConsumerController->waitForAsynchronousResult(
|
||||
[$this, 'assertProductCreation'],
|
||||
[$product]
|
||||
);
|
||||
} catch (PreconditionFailedException $e) {
|
||||
$this->fail("Not all products were created");
|
||||
}
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->clearProducts();
|
||||
$this->publisherConsumerController->stopConsumers();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
private function clearProducts()
|
||||
{
|
||||
$size = $this->objectManager->create(Collection::class)
|
||||
->addAttributeToFilter('sku', ['in' => $this->skus])
|
||||
->load()
|
||||
->getSize();
|
||||
|
||||
if ($size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->registry->unregister('isSecureArea');
|
||||
$this->registry->register('isSecureArea', true);
|
||||
try {
|
||||
foreach ($this->skus as $sku) {
|
||||
$this->productRepository->deleteById($sku);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw $e;
|
||||
//nothing to delete
|
||||
}
|
||||
$this->registry->unregister('isSecureArea');
|
||||
|
||||
$size = $this->objectManager->create(Collection::class)
|
||||
->addAttributeToFilter('sku', ['in' => $this->skus])
|
||||
->load()
|
||||
->getSize();
|
||||
|
||||
if ($size > 0) {
|
||||
throw new Exception(new Phrase("Collection size after clearing the products: %size", ['size' => $size]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sku
|
||||
* @param string|null $storeCode
|
||||
* @dataProvider productGetDataProvider
|
||||
*/
|
||||
public function testGETRequestToAsync($sku, $storeCode = null)
|
||||
{
|
||||
$this->expectException(\Exception::class);
|
||||
$this->expectExceptionMessage('Specified request cannot be processed.');
|
||||
|
||||
$this->_markTestAsRestOnly();
|
||||
$serviceInfo = [
|
||||
'rest' => [
|
||||
'resourcePath' => self::ASYNC_RESOURCE_PATH . '/' . $sku,
|
||||
'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
|
||||
],
|
||||
];
|
||||
|
||||
$response = null;
|
||||
try {
|
||||
$response = $this->_webApiCall($serviceInfo, [ProductInterface::SKU => $sku], null, $storeCode);
|
||||
} catch (NotFoundException $e) {
|
||||
$this->assertEquals(400, $e->getCode());
|
||||
}
|
||||
$this->assertNull($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function productCreationProvider()
|
||||
{
|
||||
$productBuilder = function ($data) {
|
||||
return array_replace_recursive(
|
||||
$this->getSimpleProductData(),
|
||||
$data
|
||||
);
|
||||
};
|
||||
|
||||
return [
|
||||
[
|
||||
[
|
||||
'product' =>
|
||||
$productBuilder([
|
||||
ProductInterface::TYPE_ID => 'simple',
|
||||
ProductInterface::SKU => 'psku-test-1',
|
||||
]),
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
'product' => $productBuilder([
|
||||
ProductInterface::TYPE_ID => 'virtual',
|
||||
ProductInterface::SKU => 'psku-test-2',
|
||||
]),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function productGetDataProvider()
|
||||
{
|
||||
return [
|
||||
['psku-test-1', null],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Simple Product Data
|
||||
*
|
||||
* @param array $productData
|
||||
* @return array
|
||||
*/
|
||||
private function getSimpleProductData($productData = [])
|
||||
{
|
||||
return [
|
||||
ProductInterface::SKU => isset($productData[ProductInterface::SKU])
|
||||
? $productData[ProductInterface::SKU] : uniqid('sku-', true),
|
||||
ProductInterface::NAME => isset($productData[ProductInterface::NAME])
|
||||
? $productData[ProductInterface::NAME] : uniqid('sku-', true),
|
||||
ProductInterface::VISIBILITY => 4,
|
||||
ProductInterface::TYPE_ID => 'simple',
|
||||
ProductInterface::PRICE => 3.62,
|
||||
ProductInterface::STATUS => 1,
|
||||
ProductInterface::ATTRIBUTE_SET_ID => 4,
|
||||
'custom_attributes' => [
|
||||
['attribute_code' => 'cost', 'value' => ''],
|
||||
['attribute_code' => 'description', 'value' => 'Description'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $requestData
|
||||
* @param string|null $storeCode
|
||||
* @return mixed
|
||||
*/
|
||||
private function saveProductAsync($requestData, $storeCode = null)
|
||||
{
|
||||
$serviceInfo = [
|
||||
'rest' => [
|
||||
'resourcePath' => self::ASYNC_RESOURCE_PATH,
|
||||
'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST,
|
||||
],
|
||||
];
|
||||
|
||||
return $this->_webApiCall($serviceInfo, $requestData, null, $storeCode);
|
||||
}
|
||||
|
||||
public function assertProductCreation()
|
||||
{
|
||||
$collection = $this->objectManager->create(Collection::class)
|
||||
->addAttributeToFilter('sku', ['in' => $this->skus])
|
||||
->load();
|
||||
$size = $collection->getSize();
|
||||
|
||||
return $size == count($this->skus);
|
||||
}
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\WebapiAsync\Model;
|
||||
|
||||
use Magento\Framework\ObjectManagerInterface;
|
||||
use Magento\Framework\Webapi\Rest\Request;
|
||||
use Magento\Sales\Api\Data\OrderInterface;
|
||||
use Magento\Sales\Model\Order;
|
||||
use Magento\TestFramework\Helper\Bootstrap;
|
||||
use Magento\TestFramework\MessageQueue\EnvironmentPreconditionException;
|
||||
use Magento\TestFramework\MessageQueue\PreconditionFailedException;
|
||||
use Magento\TestFramework\MessageQueue\PublisherConsumerController;
|
||||
use Magento\TestFramework\TestCase\WebapiAbstract;
|
||||
|
||||
/**
|
||||
* Test order repository interface via async webapi
|
||||
*/
|
||||
class OrderRepositoryInterfaceTest extends WebapiAbstract
|
||||
{
|
||||
private const ASYNC_BULK_SAVE_ORDER = '/async/bulk/V1/orders';
|
||||
private const ASYNC_SAVE_ORDER = '/async/V1/orders';
|
||||
/**
|
||||
* @var ObjectManagerInterface
|
||||
*/
|
||||
private $objectManager;
|
||||
/**
|
||||
* @var PublisherConsumerController
|
||||
*/
|
||||
private $publisherConsumerController;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->objectManager = Bootstrap::getObjectManager();
|
||||
|
||||
$params = array_merge_recursive(
|
||||
Bootstrap::getInstance()->getAppInitParams(),
|
||||
['MAGE_DIRS' => ['cache' => ['path' => TESTS_TEMP_DIR . '/cache']]]
|
||||
);
|
||||
|
||||
/** @var PublisherConsumerController publisherConsumerController */
|
||||
$this->publisherConsumerController = $this->objectManager->create(
|
||||
PublisherConsumerController::class,
|
||||
[
|
||||
'consumers' => ['async.operations.all'],
|
||||
'logFilePath' => TESTS_TEMP_DIR . "/MessageQueueTestLog.txt",
|
||||
'appInitParams' => $params,
|
||||
]
|
||||
);
|
||||
|
||||
try {
|
||||
$this->publisherConsumerController->initialize();
|
||||
} catch (EnvironmentPreconditionException $e) {
|
||||
$this->markTestSkipped($e->getMessage());
|
||||
} catch (PreconditionFailedException $e) {
|
||||
$this->fail(
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->publisherConsumerController->stopConsumers();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that order is updated successfuly via async webapi
|
||||
*
|
||||
* @magentoApiDataFixture Magento/Sales/_files/order.php
|
||||
* @dataProvider saveDataProvider
|
||||
* @param array $data
|
||||
* @param bool $isBulk
|
||||
* @return void
|
||||
*/
|
||||
public function testSave(array $data, bool $isBulk = true): void
|
||||
{
|
||||
$this->_markTestAsRestOnly();
|
||||
/** @var Order $beforeUpdateOrder */
|
||||
$beforeUpdateOrder = $this->objectManager->get(Order::class)->loadByIncrementId('100000001');
|
||||
$requestData = [
|
||||
'entity' => array_merge($data, [OrderInterface::ENTITY_ID => $beforeUpdateOrder->getEntityId()])
|
||||
];
|
||||
if ($isBulk) {
|
||||
$requestData = [$requestData];
|
||||
}
|
||||
$serviceInfo = [
|
||||
'rest' => [
|
||||
'resourcePath' => $isBulk ? self::ASYNC_BULK_SAVE_ORDER : self::ASYNC_SAVE_ORDER,
|
||||
'httpMethod' => Request::HTTP_METHOD_POST,
|
||||
]
|
||||
];
|
||||
$this->makeAsyncRequest($serviceInfo, $requestData);
|
||||
try {
|
||||
$this->publisherConsumerController->waitForAsynchronousResult(
|
||||
function (Order $beforeUpdateOrder, array $data) {
|
||||
/** @var Order $afterUpdateOrder */
|
||||
$afterUpdateOrder = $this->objectManager->get(Order::class)->load($beforeUpdateOrder->getId());
|
||||
foreach ($data as $attribute => $value) {
|
||||
$getter = 'get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $attribute)));
|
||||
if ($value !== $afterUpdateOrder->$getter()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//check that base_grand_total and grand_total are not overwritten
|
||||
$this->assertEquals(
|
||||
$beforeUpdateOrder->getBaseGrandTotal(),
|
||||
$afterUpdateOrder->getBaseGrandTotal()
|
||||
);
|
||||
$this->assertEquals(
|
||||
$beforeUpdateOrder->getGrandTotal(),
|
||||
$afterUpdateOrder->getGrandTotal()
|
||||
);
|
||||
return true;
|
||||
},
|
||||
[$beforeUpdateOrder, $data]
|
||||
);
|
||||
} catch (PreconditionFailedException $e) {
|
||||
$this->fail("Order update via async webapi failed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for tesSave()
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function saveDataProvider(): array
|
||||
{
|
||||
return [
|
||||
'update order in bulk mode' => [
|
||||
[
|
||||
OrderInterface::CUSTOMER_EMAIL => 'customer.email.modified@magento.test'
|
||||
],
|
||||
true
|
||||
],
|
||||
'update order in single mode' => [
|
||||
[
|
||||
OrderInterface::CUSTOMER_EMAIL => 'customer.email.modified@magento.test'
|
||||
],
|
||||
false
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Make async webapi request
|
||||
*
|
||||
* @param array $serviceInfo
|
||||
* @param array $requestData
|
||||
* @return void
|
||||
*/
|
||||
private function makeAsyncRequest(array $serviceInfo, array $requestData): void
|
||||
{
|
||||
$response = $this->_webApiCall($serviceInfo, $requestData);
|
||||
$this->assertNotEmpty($response['request_items']);
|
||||
foreach ($response['request_items'] as $requestItem) {
|
||||
$this->assertEquals('accepted', $requestItem['status']);
|
||||
}
|
||||
$this->assertFalse($response['errors']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user