Skip to content

Commit 460a249

Browse files
committed
api functional test for WebapiAsync
1 parent 48104dc commit 460a249

File tree

1 file changed

+363
-0
lines changed

1 file changed

+363
-0
lines changed
Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\WebapiAsync\Model;
10+
11+
use Magento\Catalog\Api\Data\ProductInterface;
12+
use Magento\Framework\Exception\NotFoundException;
13+
use Magento\TestFramework\MessageQueue\PreconditionFailedException;
14+
use Magento\TestFramework\MessageQueue\PublisherConsumerController;
15+
use Magento\TestFramework\MessageQueue\EnvironmentPreconditionException;
16+
use Magento\TestFramework\TestCase\WebapiAbstract;
17+
use Magento\TestFramework\Helper\Bootstrap;
18+
use Magento\Catalog\Model\ResourceModel\Product\Collection;
19+
use Magento\Framework\Phrase;
20+
use Magento\Framework\Registry;
21+
use Magento\Framework\Webapi\Exception;
22+
use Magento\Catalog\Api\ProductRepositoryInterface;
23+
use Magento\Catalog\Api\Data\ProductInterface as Product;
24+
25+
/**
26+
* Check async request for multistore product creation service, scheduling bulk
27+
* to rabbitmq running consumers and check async.operation.add consumer check
28+
* if product was created by async requests
29+
*
30+
* @magentoAppIsolation enabled
31+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
32+
*/
33+
class AsyncScheduleMultiStoreTest extends WebapiAbstract
34+
{
35+
const SERVICE_NAME = 'catalogProductRepositoryV1';
36+
const SERVICE_VERSION = 'V1';
37+
const REST_RESOURCE_PATH = '/V1/products';
38+
const ASYNC_RESOURCE_PATH = '/async/V1/products';
39+
const ASYNC_CONSUMER_NAME = 'async.operations.all';
40+
41+
const STORE_CODE_FROM_FIXTURE = 'fixturestore';
42+
const STORE_NAME_FROM_FIXTURE = 'Fixture Store';
43+
44+
const STORE_CODE_ALL = 'all';
45+
const STORE_CODE_DEFAULT = 'default';
46+
47+
private $stores = [
48+
self::STORE_CODE_DEFAULT,
49+
self::STORE_CODE_ALL,
50+
self::STORE_CODE_FROM_FIXTURE,
51+
];
52+
53+
const KEY_TIER_PRICES = 'tier_prices';
54+
const KEY_SPECIAL_PRICE = 'special_price';
55+
const KEY_CATEGORY_LINKS = 'category_links';
56+
57+
const BULK_UUID_KEY = 'bulk_uuid';
58+
59+
protected $consumers = [
60+
self::ASYNC_CONSUMER_NAME,
61+
];
62+
63+
/**
64+
* @var string[]
65+
*/
66+
private $skus = [];
67+
68+
/**
69+
* @var PublisherConsumerController
70+
*/
71+
private $publisherConsumerController;
72+
73+
/**
74+
* @var ProductRepositoryInterface
75+
*/
76+
private $productRepository;
77+
78+
/**
79+
* @var \Magento\Framework\ObjectManagerInterface
80+
*/
81+
private $objectManager;
82+
83+
/**
84+
* @var Registry
85+
*/
86+
private $registry;
87+
88+
protected function setUp()
89+
{
90+
$this->objectManager = Bootstrap::getObjectManager();
91+
$this->logFilePath = TESTS_TEMP_DIR . "/MessageQueueTestLog.txt";
92+
$this->registry = $this->objectManager->get(Registry::class);
93+
94+
$params = array_merge_recursive(
95+
\Magento\TestFramework\Helper\Bootstrap::getInstance()->getAppInitParams(),
96+
['MAGE_DIRS' => ['cache' => ['path' => TESTS_TEMP_DIR . '/cache']]]
97+
);
98+
99+
/** @var PublisherConsumerController publisherConsumerController */
100+
$this->publisherConsumerController = $this->objectManager->create(PublisherConsumerController::class, [
101+
'consumers' => $this->consumers,
102+
'logFilePath' => $this->logFilePath,
103+
'appInitParams' => $params,
104+
]);
105+
$this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
106+
107+
try {
108+
$this->publisherConsumerController->initialize();
109+
} catch (EnvironmentPreconditionException $e) {
110+
$this->markTestSkipped($e->getMessage());
111+
} catch (PreconditionFailedException $e) {
112+
$this->fail(
113+
$e->getMessage()
114+
);
115+
}
116+
117+
parent::setUp();
118+
}
119+
120+
/**
121+
* @dataProvider storeProvider
122+
* @magentoApiDataFixture Magento/Store/_files/core_fixturestore.php
123+
*/
124+
public function testAsyncScheduleBulkMultistore($storeCode)
125+
{
126+
$product = $this->getProductData();
127+
$this->_markTestAsRestOnly();
128+
129+
/** @var $store \Magento\Store\Model\Group */
130+
$store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class);
131+
$store->load(self::STORE_CODE_FROM_FIXTURE);
132+
$this->assertEquals(
133+
self::STORE_NAME_FROM_FIXTURE,
134+
$store->getName(),
135+
'Precondition failed: fixture store was not created.'
136+
);
137+
138+
try {
139+
/** @var \Magento\Catalog\Model\Product $productModel */
140+
$productModel = Bootstrap::getObjectManager()->create(
141+
\Magento\Catalog\Model\Product::class,
142+
['data' => $product['product']]
143+
);
144+
$this->productRepository->save($productModel);
145+
} catch (\Exception $e) {
146+
$this->fail("Precondition failed: product was not created.");
147+
}
148+
149+
$this->asyncScheduleAndTest($product, $storeCode);
150+
$this->clearProducts();
151+
}
152+
153+
private function asyncScheduleAndTest($product, $storeCode = null)
154+
{
155+
$sku = $product['product'][Product::SKU];
156+
$productName = $product['product'][Product::NAME];
157+
$newProductName = $product['product'][Product::NAME] . $storeCode;
158+
159+
$this->skus[] = $sku;
160+
161+
$product['product'][Product::NAME] = $newProductName;
162+
$product['product'][Product::TYPE_ID] = 'virtual';
163+
164+
$response = $this->updateProductAsync($product, $sku, $storeCode);
165+
166+
$this->assertArrayHasKey(self::BULK_UUID_KEY, $response);
167+
$this->assertNotNull($response[self::BULK_UUID_KEY]);
168+
169+
$this->assertCount(1, $response['request_items']);
170+
$this->assertEquals('accepted', $response['request_items'][0]['status']);
171+
$this->assertFalse($response['errors']);
172+
173+
//assert one products is created
174+
try {
175+
$this->publisherConsumerController->waitForAsynchronousResult(
176+
[$this, 'assertProductCreation'],
177+
[$product]
178+
);
179+
} catch (PreconditionFailedException $e) {
180+
$this->fail("Not all products were created");
181+
}
182+
183+
$requestData = ['id' => $sku, 'sku' => $sku];
184+
185+
foreach ($this->stores as $checkingStore) {
186+
$serviceInfo = [
187+
'rest' => [
188+
'resourcePath' => self::REST_RESOURCE_PATH . '/' . $sku,
189+
'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET
190+
]
191+
];
192+
$storeResponse = $this->_webApiCall($serviceInfo, $requestData, null, $checkingStore);
193+
if ($checkingStore == $storeCode || $storeCode == self::STORE_CODE_ALL) {
194+
$this->assertEquals(
195+
$newProductName,
196+
$storeResponse[Product::NAME],
197+
sprintf(
198+
'Product name in %s store is invalid after updating in store %s.',
199+
$checkingStore,
200+
$storeCode
201+
)
202+
);
203+
} else {
204+
$this->assertEquals(
205+
$productName,
206+
$storeResponse[Product::NAME],
207+
sprintf(
208+
'Product name in %s store is invalid after updating in store %s.',
209+
$checkingStore,
210+
$storeCode
211+
)
212+
);
213+
}
214+
}
215+
}
216+
217+
public function tearDown()
218+
{
219+
$this->clearProducts();
220+
$this->publisherConsumerController->stopConsumers();
221+
parent::tearDown();
222+
}
223+
224+
private function clearProducts()
225+
{
226+
$size = $this->objectManager->create(Collection::class)
227+
->addAttributeToFilter('sku', ['in' => $this->skus])
228+
->load()
229+
->getSize();
230+
231+
if ($size == 0) {
232+
return;
233+
}
234+
235+
$this->registry->unregister('isSecureArea');
236+
$this->registry->register('isSecureArea', true);
237+
try {
238+
foreach ($this->skus as $sku) {
239+
$this->productRepository->deleteById($sku);
240+
}
241+
} catch (\Exception $e) {
242+
throw $e;
243+
//nothing to delete
244+
}
245+
$this->registry->unregister('isSecureArea');
246+
247+
$size = $this->objectManager->create(Collection::class)
248+
->addAttributeToFilter('sku', ['in' => $this->skus])
249+
->load()
250+
->getSize();
251+
252+
if ($size > 0) {
253+
throw new Exception(new Phrase("Collection size after clearing the products: %size", ['size' => $size]));
254+
}
255+
$this->skus = [];
256+
}
257+
258+
/**
259+
* @return array
260+
*/
261+
public function getProductData()
262+
{
263+
$productBuilder = function ($data) {
264+
return array_replace_recursive(
265+
$this->getSimpleProductData(),
266+
$data
267+
);
268+
};
269+
270+
return [
271+
'product' =>
272+
$productBuilder([
273+
ProductInterface::TYPE_ID => 'simple',
274+
ProductInterface::SKU => 'multistore-sku-test-1',
275+
ProductInterface::NAME => 'Test Name ',
276+
]),
277+
];
278+
}
279+
280+
public function storeProvider()
281+
{
282+
$dataSets = [];
283+
foreach ($this->stores as $store) {
284+
$dataSets[$store] = [$store];
285+
}
286+
return $dataSets;
287+
}
288+
289+
/**
290+
* Get Simple Product Data
291+
*
292+
* @param array $productData
293+
* @return array
294+
*/
295+
private function getSimpleProductData($productData = [])
296+
{
297+
return [
298+
ProductInterface::SKU => isset($productData[ProductInterface::SKU])
299+
? $productData[ProductInterface::SKU] : uniqid('sku-', true),
300+
ProductInterface::NAME => isset($productData[ProductInterface::NAME])
301+
? $productData[ProductInterface::NAME] : uniqid('sku-', true),
302+
ProductInterface::VISIBILITY => 4,
303+
ProductInterface::TYPE_ID => 'simple',
304+
ProductInterface::PRICE => 3.62,
305+
ProductInterface::STATUS => 1,
306+
ProductInterface::TYPE_ID => 'simple',
307+
ProductInterface::ATTRIBUTE_SET_ID => 4,
308+
];
309+
}
310+
311+
/**
312+
* @param $requestData
313+
* @param string|null $storeCode
314+
* @return mixed
315+
*/
316+
private function updateProductAsync($requestData, $sku, $storeCode = null)
317+
{
318+
$serviceInfo = [
319+
'rest' => [
320+
'resourcePath' => self::ASYNC_RESOURCE_PATH . '/' . $sku,
321+
'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT,
322+
],
323+
];
324+
325+
return $this->_webApiCall($serviceInfo, $requestData, null, $storeCode);
326+
}
327+
328+
public function assertProductCreation($product)
329+
{
330+
$sku = $product['product'][Product::SKU];
331+
$collection = $this->objectManager->create(Collection::class)
332+
->addAttributeToFilter(Product::SKU, ['eq' => $sku])
333+
->addAttributeToFilter(Product::TYPE_ID, ['eq' => 'virtual'])
334+
->load();
335+
$size = $collection->getSize();
336+
337+
return $size > 0;
338+
}
339+
340+
/**
341+
* Remove test store
342+
*/
343+
public static function tearDownAfterClass()
344+
{
345+
parent::tearDownAfterClass();
346+
/** @var \Magento\Framework\Registry $registry */
347+
$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
348+
->get(\Magento\Framework\Registry::class);
349+
350+
$registry->unregister('isSecureArea');
351+
$registry->register('isSecureArea', true);
352+
353+
/** @var $store \Magento\Store\Model\Store */
354+
$store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class);
355+
$store->load('fixturestore');
356+
if ($store->getId()) {
357+
$store->delete();
358+
}
359+
360+
$registry->unregister('isSecureArea');
361+
$registry->register('isSecureArea', false);
362+
}
363+
}

0 commit comments

Comments
 (0)