Skip to content

Commit 7b5d93c

Browse files
authored
Merge pull request #101 from magento-tsg/MQE-2528
[Sidecar] MQE-2528: Create automated test for: [MSI] "Tables data updated when SKU of Simple product has been changed from Admin"
2 parents e7200a3 + 1a0fae8 commit 7b5d93c

File tree

3 files changed

+592
-0
lines changed

3 files changed

+592
-0
lines changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\InventoryCatalog\Test\Integration\Plugin\Catalog\Model\ResourceModel\Product;
9+
10+
use Magento\Catalog\Api\ProductRepositoryInterface;
11+
use Magento\Catalog\Model\ResourceModel\Product as ResourceProduct;
12+
use Magento\Framework\Exception\NoSuchEntityException;
13+
use Magento\Framework\MessageQueue\MessageEncoder;
14+
use Magento\Framework\MessageQueue\QueueFactoryInterface;
15+
use Magento\Framework\MessageQueue\QueueInterface;
16+
use Magento\Framework\ObjectManagerInterface;
17+
use Magento\InventoryApi\Api\GetSourceItemsBySkuInterface;
18+
use Magento\InventoryCatalog\Model\DeleteSourceItemsBySkus;
19+
use Magento\InventoryCatalog\Plugin\Catalog\Model\ResourceModel\Product\ProcessSourceItemsPlugin;
20+
use Magento\InventoryLowQuantityNotification\Model\ResourceModel\SourceItemConfiguration\GetBySku;
21+
use Magento\TestFramework\Helper\Bootstrap;
22+
use Magento\TestFramework\Interception\PluginList;
23+
use Magento\TestFramework\MysqlMq\DeleteTopicRelatedMessages;
24+
use PHPUnit\Framework\TestCase;
25+
26+
/**
27+
* Checks that source items and low stock quantity notification will be removed after product sku has been updated.
28+
*
29+
* @see \Magento\InventoryCatalog\Plugin\Catalog\Model\ResourceModel\Product\ProcessSourceItemsPlugin
30+
* @magentoAppArea adminhtml
31+
*/
32+
class ProcessSourceItemsPluginTest extends TestCase
33+
{
34+
/** @var ObjectManagerInterface */
35+
private $objectManager;
36+
37+
/** @var DeleteTopicRelatedMessages */
38+
private $deleteTopicMessages;
39+
40+
/** @var QueueInterface */
41+
private $queue;
42+
43+
/** @var MessageEncoder */
44+
private $messageEncoder;
45+
46+
/** @var DeleteSourceItemsBySkus */
47+
private $handler;
48+
49+
/** @var string */
50+
private $currentSku;
51+
52+
/** @var string */
53+
private $newSku;
54+
55+
/** @var GetSourceItemsBySkuInterface */
56+
private $getSourceItemsBySku;
57+
58+
/** @var GetBySku */
59+
private $getSourceItemConfigurationsBySku;
60+
61+
/** @var ProductRepositoryInterface */
62+
private $productRepository;
63+
64+
/**
65+
* @inheritDoc
66+
*/
67+
protected function setUp(): void
68+
{
69+
parent::setUp();
70+
71+
$this->objectManager = Bootstrap::getObjectManager();
72+
$this->deleteTopicMessages = $this->objectManager->get(DeleteTopicRelatedMessages::class);
73+
$this->queue = $this->objectManager->get(QueueFactoryInterface::class)->create(
74+
'inventory.source.items.cleanup',
75+
'db'
76+
);
77+
$this->messageEncoder = $this->objectManager->get(MessageEncoder::class);
78+
$this->handler = $this->objectManager->get(DeleteSourceItemsBySkus::class);
79+
$this->getSourceItemsBySku = $this->objectManager->get(GetSourceItemsBySkuInterface::class);
80+
$this->getSourceItemConfigurationsBySku = $this->objectManager->get(GetBySku::class);
81+
$this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
82+
$this->productRepository->cleanCache();
83+
}
84+
85+
/**
86+
* @inheritdoc
87+
*/
88+
protected function tearDown(): void
89+
{
90+
if ($this->newSku) {
91+
$this->deleteProductBySku($this->newSku);
92+
}
93+
94+
parent::tearDown();
95+
}
96+
97+
/**
98+
* @return void
99+
*/
100+
public function testProcessSourceItemsPluginIsRegistered(): void
101+
{
102+
$pluginInfo = $this->objectManager->get(PluginList::class)->get(ResourceProduct::class);
103+
$this->assertSame(
104+
ProcessSourceItemsPlugin::class,
105+
$pluginInfo['process_source_items_after_product_save']['instance']
106+
);
107+
}
108+
109+
/**
110+
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
111+
* @magentoDataFixture Magento_InventoryApi::Test/_files/sources.php
112+
* @magentoDataFixture Magento_InventoryApi::Test/_files/stocks.php
113+
* @magentoDataFixture Magento_InventoryApi::Test/_files/source_items.php
114+
* @magentoDataFixture Magento_InventoryApi::Test/_files/stock_source_links.php
115+
* @magentoDataFixture Magento_InventoryLowQuantityNotificationApi::Test/_files/source_item_configuration.php
116+
* @magentoConfigFixture default/cataloginventory/options/synchronize_with_catalog 1
117+
* @magentoDbIsolation disabled
118+
* @return void
119+
*/
120+
public function testUpdateProductSkuSynchronizeWithCatalog(): void
121+
{
122+
$this->deleteTopicMessages->execute('inventory.source.items.cleanup');
123+
$this->currentSku = 'SKU-1';
124+
$this->newSku = 'SKU-1-new';
125+
$this->updateProductSku($this->currentSku, $this->newSku);
126+
$this->processMessages('inventory.source.items.cleanup');
127+
self::assertEmpty($this->getSourceItemsBySku->execute($this->currentSku));
128+
self::assertEmpty($this->getSourceItemConfigurationsBySku->execute($this->currentSku));
129+
}
130+
131+
/**
132+
* Process topic messages
133+
*
134+
* @param string $topicName
135+
* @return void
136+
*/
137+
private function processMessages(string $topicName): void
138+
{
139+
$envelope = $this->queue->dequeue();
140+
$decodedMessage = $this->messageEncoder->decode($topicName, $envelope->getBody());
141+
$this->handler->execute($decodedMessage);
142+
}
143+
144+
/**
145+
* Update product sku
146+
*
147+
* @param string $productSku
148+
* @param string $newSku
149+
* @return void
150+
*/
151+
private function updateProductSku(string $productSku, string $newSku): void
152+
{
153+
$product = $this->productRepository->get($productSku);
154+
$product->setSku($newSku);
155+
$this->productRepository->save($product);
156+
}
157+
158+
/**
159+
* Delete product by sku in secure area
160+
*
161+
* @param string $sku
162+
* @return void
163+
*/
164+
private function deleteProductBySku(string $sku): void
165+
{
166+
try {
167+
$product = $this->productRepository->get($sku);
168+
$this->productRepository->delete($product);
169+
} catch (NoSuchEntityException $exception) {
170+
// product doesn't exist;
171+
}
172+
}
173+
}
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\InventoryCatalogAdminUi\Test\Integration\Observer;
9+
10+
use Magento\Catalog\Api\Data\ProductInterface;
11+
use Magento\Catalog\Api\ProductRepositoryInterface;
12+
use Magento\Catalog\Controller\Adminhtml\Product\Save;
13+
use Magento\Framework\DataObjectFactory;
14+
use Magento\Framework\Event\Observer;
15+
use Magento\Framework\Exception\NoSuchEntityException;
16+
use Magento\Framework\ObjectManagerInterface;
17+
use Magento\InventoryApi\Api\Data\SourceItemInterface;
18+
use Magento\InventoryCatalog\Model\GetSourceItemsBySkuAndSourceCodes;
19+
use Magento\InventoryCatalogAdminUi\Model\GetSourceItemsDataBySku;
20+
use Magento\InventoryCatalogAdminUi\Observer\ProcessSourceItemsObserver;
21+
use Magento\TestFramework\Helper\Bootstrap;
22+
use PHPUnit\Framework\TestCase;
23+
24+
/**
25+
* Checks that the entries in the inventory_source_item table
26+
* have been updated correctly for the current product
27+
*
28+
* @see \Magento\InventoryCatalogAdminUi\Observer\ProcessSourceItemsObserver
29+
* @magentoAppArea adminhtml
30+
*/
31+
class ProcessSourceItemsObserverTest extends TestCase
32+
{
33+
/** @var ObjectManagerInterface */
34+
private $objectManager;
35+
36+
/** @var ProcessSourceItemsObserver */
37+
private $observer;
38+
39+
/** @var ProductRepositoryInterface */
40+
private $productRepository;
41+
42+
/** @var Save */
43+
private $adminProductSaveController;
44+
45+
/** @var GetSourceItemsDataBySku */
46+
private $getSourceItemsDataBySku;
47+
48+
/** @var GetSourceItemsBySkuAndSourceCodes */
49+
private $getSourceItemsBySkuAndSourceCodes;
50+
51+
/** @var string */
52+
private $currentSku;
53+
54+
/** @var string */
55+
private $newSku;
56+
57+
/** @var DataObjectFactory */
58+
private $dataObjectFactory;
59+
60+
/**
61+
* @inheritdoc
62+
*/
63+
protected function setUp(): void
64+
{
65+
parent::setUp();
66+
67+
$this->objectManager = Bootstrap::getObjectManager();
68+
$this->observer = $this->objectManager->get(ProcessSourceItemsObserver::class);
69+
$this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
70+
$this->productRepository->cleanCache();
71+
$this->adminProductSaveController = $this->objectManager->get(Save::class);
72+
$this->getSourceItemsDataBySku = $this->objectManager->get(GetSourceItemsDataBySku::class);
73+
$this->getSourceItemsBySkuAndSourceCodes = $this->objectManager->get(GetSourceItemsBySkuAndSourceCodes::class);
74+
$this->dataObjectFactory = $this->objectManager->get(DataObjectFactory::class);
75+
}
76+
77+
/**
78+
* @inheritdoc
79+
*/
80+
protected function tearDown(): void
81+
{
82+
if ($this->newSku) {
83+
$this->deleteProductBySku($this->newSku);
84+
}
85+
86+
parent::tearDown();
87+
}
88+
89+
/**
90+
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
91+
* @magentoDataFixture Magento_InventoryApi::Test/_files/sources.php
92+
* @magentoDataFixture Magento_InventoryApi::Test/_files/stocks.php
93+
* @magentoDataFixture Magento_InventoryApi::Test/_files/source_items.php
94+
* @magentoDataFixture Magento_InventoryApi::Test/_files/stock_source_links.php
95+
* @magentoConfigFixture default/cataloginventory/options/synchronize_with_catalog 1
96+
* @magentoDbIsolation disabled
97+
* @return void
98+
*/
99+
public function testUpdateProductSkuInMultipleSourceMode(): void
100+
{
101+
$this->currentSku = 'SKU-1';
102+
$this->newSku = 'SKU-1-new';
103+
$product = $this->productRepository->get($this->currentSku);
104+
$assignedSources = $this->getSourceItemsDataBySku->execute($product->getSku());
105+
106+
$product = $this->updateProductSku($this->currentSku, $this->newSku);
107+
$this->prepareAdminProductSaveController($product, $assignedSources);
108+
$this->observer->execute($this->getEventObserver($product));
109+
$assignedSourceCodes = array_column($assignedSources, SourceItemInterface::SOURCE_CODE);
110+
111+
$this->assertCount(
112+
count($assignedSources),
113+
$this->getSourceItemsBySkuAndSourceCodes->execute($this->currentSku, $assignedSourceCodes),
114+
sprintf('The expected quantity of source item for the current sku: %s is not correct.', $this->currentSku)
115+
);
116+
$this->assertCount(
117+
count($assignedSources),
118+
$this->getSourceItemsBySkuAndSourceCodes->execute($this->newSku, $assignedSourceCodes),
119+
sprintf('The expected quantity of source item for the new sku: %s is not correct.', $this->newSku)
120+
);
121+
}
122+
123+
/**
124+
* Initialize observer event's for tests.
125+
*
126+
* @param ProductInterface $product
127+
* @return Observer
128+
*/
129+
private function getEventObserver(ProductInterface $product): Observer
130+
{
131+
$event = $this->dataObjectFactory->create();
132+
$event->setController($this->adminProductSaveController)
133+
->setProduct($product);
134+
135+
/** @var Observer $eventObserver */
136+
$eventObserver = $this->objectManager->create(Observer::class);
137+
$eventObserver->setEvent($event);
138+
139+
return $eventObserver;
140+
}
141+
142+
/**
143+
* Update product sku
144+
*
145+
* @param string $productSku
146+
* @param string $newSku
147+
* @return ProductInterface
148+
*/
149+
private function updateProductSku(string $productSku, string $newSku): ProductInterface
150+
{
151+
$product = $this->productRepository->get($productSku);
152+
$product->setSku($newSku);
153+
154+
return $this->productRepository->save($product);
155+
}
156+
157+
/**
158+
* Prepare admin product save controller
159+
*
160+
* @param ProductInterface $product
161+
* @param array $assignedSources
162+
* @return void
163+
*/
164+
private function prepareAdminProductSaveController(ProductInterface $product, array $assignedSources): void
165+
{
166+
$this->adminProductSaveController->getRequest()->setParams([
167+
'product' => $product->getData(),
168+
'sources' => [
169+
'assigned_sources' => $assignedSources,
170+
],
171+
]);
172+
}
173+
174+
/**
175+
* Delete product by sku in secure area
176+
*
177+
* @param string $sku
178+
* @return void
179+
*/
180+
private function deleteProductBySku(string $sku): void
181+
{
182+
try {
183+
$product = $this->productRepository->get($sku);
184+
$this->productRepository->delete($product);
185+
} catch (NoSuchEntityException $exception) {
186+
// product doesn't exist;
187+
}
188+
}
189+
}

0 commit comments

Comments
 (0)