Skip to content

Commit b2e0d91

Browse files
committed
ACPT-1643: Fix Inventory salable performance issue
- Extend test coverage;
1 parent c03cb07 commit b2e0d91

File tree

3 files changed

+199
-125
lines changed

3 files changed

+199
-125
lines changed

InventoryConfigurableProduct/Test/Integration/Plugin/Model/Product/Type/Configurable/IsSalableOptionPluginTest.php

Lines changed: 0 additions & 122 deletions
This file was deleted.
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
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\InventoryConfigurableProduct\Test\Unit\Plugin\Model\Product\Type\Configurable;
9+
10+
use Magento\Catalog\Model\Product;
11+
use Magento\CatalogInventory\Api\StockConfigurationInterface;
12+
use Magento\InventoryApi\Api\Data\StockInterface;
13+
use Magento\InventoryConfigurableProduct\Plugin\Model\Product\Type\Configurable\IsSalableOptionPlugin;
14+
use Magento\InventorySalesApi\Api\AreProductsSalableInterface;
15+
use Magento\InventorySalesApi\Api\Data\IsProductSalableResultInterface;
16+
use Magento\InventorySalesApi\Api\Data\SalesChannelInterface;
17+
use Magento\InventorySalesApi\Api\StockResolverInterface;
18+
use Magento\Store\Api\Data\WebsiteInterface;
19+
use Magento\Store\Model\StoreManagerInterface;
20+
use PHPUnit\Framework\MockObject\MockObject;
21+
use PHPUnit\Framework\TestCase;
22+
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
23+
24+
class IsSalableOptionPluginTest extends TestCase
25+
{
26+
/**
27+
* @var IsSalableOptionPlugin
28+
*/
29+
private IsSalableOptionPlugin $plugin;
30+
31+
/**
32+
* @var AreProductsSalableInterface|MockObject
33+
*/
34+
private $areProductsSalableMock;
35+
36+
/**
37+
* @var StoreManagerInterface|MockObject
38+
*/
39+
private $storeManagerMock;
40+
41+
/**
42+
* @var StockResolverInterface|MockObject
43+
*/
44+
private $stockResolverMock;
45+
46+
/**
47+
* @var StockConfigurationInterface|MockObject
48+
*/
49+
private $stockConfigurationMock;
50+
51+
/**
52+
* @var Configurable|MockObject
53+
*/
54+
private $configurableMock;
55+
56+
/**
57+
* @var WebsiteInterface|MockObject
58+
*/
59+
private $websiteMock;
60+
61+
/**
62+
* @var StockInterface|MockObject
63+
*/
64+
private $stockMock;
65+
66+
/**
67+
* @inheritdoc
68+
*/
69+
protected function setUp(): void
70+
{
71+
$this->areProductsSalableMock = $this->createMock(AreProductsSalableInterface::class);
72+
$this->storeManagerMock = $this->createMock(StoreManagerInterface::class);
73+
$this->stockResolverMock = $this->createMock(StockResolverInterface::class);
74+
$this->stockConfigurationMock = $this->createMock(StockConfigurationInterface::class);
75+
$this->configurableMock = $this->createMock(Configurable::class);
76+
$this->websiteMock = $this->createMock(WebsiteInterface::class);
77+
$this->stockMock = $this->createMock(StockInterface::class);
78+
79+
$this->plugin = new IsSalableOptionPlugin(
80+
$this->areProductsSalableMock,
81+
$this->storeManagerMock,
82+
$this->stockResolverMock,
83+
$this->stockConfigurationMock
84+
);
85+
}
86+
87+
public function testAllProductsAreSalable()
88+
{
89+
$products = $this->createProducts(['sku1' => true, 'sku2' => true]);
90+
$this->mockAreProductsSalable(['sku1' => true, 'sku2' => true]);
91+
$this->createAdditionalMocks();
92+
93+
$result = $this->plugin->afterGetUsedProducts($this->configurableMock, $products);
94+
95+
$this->assertEquals(2, count($result));
96+
foreach ($result as $product) {
97+
$this->assertEquals(1, $product->getIsSalable());
98+
}
99+
}
100+
101+
/**
102+
* @dataProvider productSalabilityDataProvider
103+
*/
104+
public function testSomeProductsAreNotSalable(bool $isShowOutOfStock, int $expectedCount)
105+
{
106+
$this->stockConfigurationMock->method('isShowOutOfStock')->willReturn($isShowOutOfStock);
107+
108+
$products = $this->createProducts(['sku1' => true, 'sku2' => false, 'sku3' => true]);
109+
$this->mockAreProductsSalable(['sku1' => true, 'sku2' => false, 'sku3' => true]);
110+
$this->createAdditionalMocks();
111+
112+
$result = $this->plugin->afterGetUsedProducts($this->configurableMock, $products);
113+
114+
$this->assertEquals($expectedCount, count($result));
115+
foreach ($result as $product) {
116+
if ($product->getSku() === 'sku2') {
117+
$this->assertEquals(0, $product->getIsSalable());
118+
} else {
119+
$this->assertEquals(1, $product->getIsSalable());
120+
}
121+
}
122+
}
123+
124+
public function productSalabilityDataProvider(): array
125+
{
126+
return [
127+
'Hide Out Of Stock' => [false, 2],
128+
'Show Out Of Stock' => [true, 3],
129+
];
130+
}
131+
132+
133+
public function testNoProductsAreSalable()
134+
{
135+
$products = $this->createProducts(['sku1' => false, 'sku2' => false]);
136+
$this->mockAreProductsSalable(['sku1' => false, 'sku2' => false]);
137+
$this->createAdditionalMocks();
138+
139+
$result = $this->plugin->afterGetUsedProducts($this->configurableMock, $products);
140+
141+
$this->assertEquals(0, count($result));
142+
foreach ($result as $product) {
143+
$this->assertEquals(0, $product->getIsSalable());
144+
}
145+
}
146+
147+
public function testEmptyProductsArray()
148+
{
149+
$products = [];
150+
151+
$result = $this->plugin->afterGetUsedProducts($this->configurableMock, $products);
152+
153+
$this->assertIsArray($result);
154+
$this->assertEmpty($result);
155+
}
156+
157+
158+
private function createProducts(array $productData): array
159+
{
160+
return array_map(function ($sku, $isSalable) {
161+
$productMock = $this->createMock(Product::class);
162+
$productMock->method('getSku')->willReturn($sku);
163+
$productMock->method('getIsSalable')->willReturn($isSalable);
164+
return $productMock;
165+
}, array_keys($productData), $productData);
166+
}
167+
168+
private function mockAreProductsSalable(array $skus): void
169+
{
170+
$salableResults = [];
171+
172+
// Handle a map of SKUs to their salable statuses
173+
foreach ($skus as $sku => $isSalable) {
174+
$salableResultMock = $this->createMock(IsProductSalableResultInterface::class);
175+
$salableResultMock->method('getSku')->willReturn($sku);
176+
$salableResultMock->method('isSalable')->willReturn($isSalable);
177+
$salableResults[] = $salableResultMock;
178+
}
179+
180+
$this->areProductsSalableMock->method('execute')->willReturn($salableResults);
181+
}
182+
183+
private function createAdditionalMocks(): void
184+
{
185+
$this->storeManagerMock->expects($this->once())
186+
->method('getWebsite')
187+
->willReturn($this->websiteMock);
188+
$this->websiteMock->expects($this->once())
189+
->method('getCode')
190+
->willReturn('website_code');
191+
$this->stockResolverMock->expects($this->once())
192+
->method('execute')
193+
->with(SalesChannelInterface::TYPE_WEBSITE, 'website_code')
194+
->willReturn($this->stockMock);
195+
$this->stockMock->expects($this->once())
196+
->method('getStockId')
197+
->willReturn(1);
198+
}
199+
}

InventoryIndexer/Test/Integration/Model/Queue/UpdateIndexSalabilityStatusTest.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
use Magento\Checkout\Test\Fixture\SetShippingAddress as SetShippingAddressFixture;
2121
use Magento\Framework\MessageQueue\ConsumerFactory;
2222
use Magento\Framework\MessageQueue\ConsumerInterface;
23-
use Magento\Indexer\Test\Fixture\Indexer;
2423
use Magento\InventoryApi\Api\Data\StockInterface;
2524
use Magento\InventoryApi\Test\Fixture\DeleteSourceItems as DeleteSourceItemsFixture;
2625
use Magento\InventoryApi\Test\Fixture\Source as SourceFixture;
@@ -87,7 +86,6 @@ protected function setUp(): void
8786
DataFixture(BundleSelectionFixture::class, ['sku' => '$s1.sku$'], 'link1'),
8887
DataFixture(BundleOptionFixture::class, ['product_links' => ['$link1$']], 'opt1'),
8988
DataFixture(BundleProductFixture::class, ['sku' => 'bundle1', '_options' => ['$opt1$']], 'b1'),
90-
DataFixture(Indexer::class, as: 'indexer'),
9189
DataFixture(GuestCartFixture::class, as: 'cart'),
9290
DataFixture(AddProductToCartFixture::class, ['cart_id' => '$cart.id$', 'product_id' => '$s1.id$']),
9391
DataFixture(SetBillingAddressFixture::class, ['cart_id' => '$cart.id$']),
@@ -132,7 +130,6 @@ public function testProductsStatusesAfterBuyingChildProduct(): void
132130
DataFixture(BundleSelectionFixture::class, ['sku' => '$s1.sku$'], 'link1'),
133131
DataFixture(BundleOptionFixture::class, ['product_links' => ['$link1$']], 'opt1'),
134132
DataFixture(BundleProductFixture::class, ['sku' => 'bundle1', '_options' => ['$opt1$']], 'b1'),
135-
DataFixture(Indexer::class, as: 'indexer'),
136133
DataFixture(GuestCartFixture::class, as: 'cart'),
137134
DataFixture(
138135
AddBundleProductToCartFixture::class,

0 commit comments

Comments
 (0)