Skip to content

Commit d6f629f

Browse files
committed
ACP2E-2033: [Performance issue] Price indexer works slow
- implemented unit test
1 parent 4577c56 commit d6f629f

File tree

1 file changed

+129
-4
lines changed
  • app/code/Magento/Bundle/Test/Unit/Model/ResourceModel/Indexer

1 file changed

+129
-4
lines changed

app/code/Magento/Bundle/Test/Unit/Model/ResourceModel/Indexer/PriceTest.php

Lines changed: 129 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Query\JoinAttributeProcessor;
1515
use Magento\Framework\App\ResourceConnection;
1616
use Magento\Framework\DB\Adapter\AdapterInterface;
17+
use Magento\Framework\EntityManager\EntityMetadataInterface;
1718
use Magento\Framework\EntityManager\MetadataPool;
1819
use Magento\Framework\Event\ManagerInterface;
1920
use Magento\Framework\Module\Manager;
@@ -45,6 +46,11 @@ class PriceTest extends TestCase
4546
*/
4647
private $priceModel;
4748

49+
/**
50+
* @var MetadataPool
51+
*/
52+
private $metadataPool;
53+
4854
/**
4955
* @inheritdoc
5056
*/
@@ -64,7 +70,7 @@ protected function setUp(): void
6470
/** @var TableMaintainer|MockObject $tableMaintainer */
6571
$tableMaintainer = $this->createMock(TableMaintainer::class);
6672
/** @var MetadataPool|MockObject $metadataPool */
67-
$metadataPool = $this->createMock(MetadataPool::class);
73+
$this->metadataPool = $this->createMock(MetadataPool::class);
6874
/** @var BasePriceModifier|MockObject $basePriceModifier */
6975
$basePriceModifier = $this->createMock(BasePriceModifier::class);
7076
/** @var JoinAttributeProcessor|MockObject $joinAttributeProcessor */
@@ -78,7 +84,7 @@ protected function setUp(): void
7884
$this->priceModel = new Price(
7985
$indexTableStructureFactory,
8086
$tableMaintainer,
81-
$metadataPool,
87+
$this->metadataPool,
8288
$this->resourceMock,
8389
$basePriceModifier,
8490
$joinAttributeProcessor,
@@ -89,6 +95,123 @@ protected function setUp(): void
8995
);
9096
}
9197

98+
/**
99+
* @throws \ReflectionException
100+
*/
101+
public function testCalculateDynamicBundleSelectionPrice(): void
102+
{
103+
$entity = 'entity_id';
104+
$price = 'idx.min_price * bs.selection_qty';
105+
//@codingStandardsIgnoreStart
106+
$selectQuery = "SELECT `i`.`entity_id`,
107+
`i`.`customer_group_id`,
108+
`i`.`website_id`,
109+
`bo`.`option_id`,
110+
`bs`.`selection_id`,
111+
IF(bo.type = 'select' OR bo.type = 'radio', 0, 1) AS `group_type`,
112+
`bo`.`required` AS `is_required`,
113+
LEAST(IF(i.special_price > 0 AND i.special_price < 100,
114+
ROUND(idx.min_price * bs.selection_qty * (i.special_price / 100), 4), idx.min_price * bs.selection_qty),
115+
IFNULL((IF(i.tier_percent IS NOT NULL,
116+
ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4), NULL)), idx.min_price *
117+
bs.selection_qty)) AS `price`,
118+
IF(i.tier_percent IS NOT NULL, ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4),
119+
NULL) AS `tier_price`
120+
FROM `catalog_product_index_price_bundle_temp` AS `i`
121+
INNER JOIN `catalog_product_entity` AS `parent_product` ON parent_product.entity_id = i.entity_id AND
122+
(parent_product.created_in <= 1 AND parent_product.updated_in > 1)
123+
INNER JOIN `catalog_product_bundle_option` AS `bo` ON bo.parent_id = parent_product.row_id
124+
INNER JOIN `catalog_product_bundle_selection` AS `bs` ON bs.option_id = bo.option_id
125+
INNER JOIN `catalog_product_index_price_replica` AS `idx`
126+
ON bs.product_id = idx.entity_id AND i.customer_group_id = idx.customer_group_id AND
127+
i.website_id = idx.website_id
128+
INNER JOIN `cataloginventory_stock_status` AS `si` ON si.product_id = bs.product_id
129+
WHERE (i.price_type = 0)
130+
AND (si.stock_status = 1)
131+
ON DUPLICATE KEY UPDATE `entity_id` = VALUES(`entity_id`),
132+
`customer_group_id` = VALUES(`customer_group_id`),
133+
`website_id` = VALUES(`website_id`),
134+
`option_id` = VALUES(`option_id`),
135+
`selection_id` = VALUES(`selection_id`),
136+
`group_type` = VALUES(`group_type`),
137+
`is_required` = VALUES(`is_required`),
138+
`price` = VALUES(`price`),
139+
`tier_price` = VALUES(`tier_price`)";
140+
$processedQuery = "INSERT INTO `catalog_product_index_price_bundle_sel_temp` (,,,,,,,,) SELECT `i`.`entity_id`,
141+
`i`.`customer_group_id`,
142+
`i`.`website_id`,
143+
`bo`.`option_id`,
144+
`bs`.`selection_id`,
145+
IF(bo.type = 'select' OR bo.type = 'radio', 0, 1) AS `group_type`,
146+
`bo`.`required` AS `is_required`,
147+
LEAST(IF(i.special_price > 0 AND i.special_price < 100,
148+
ROUND(idx.min_price * bs.selection_qty * (i.special_price / 100), 4), idx.min_price * bs.selection_qty),
149+
IFNULL((IF(i.tier_percent IS NOT NULL,
150+
ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4), NULL)), idx.min_price *
151+
bs.selection_qty)) AS `price`,
152+
IF(i.tier_percent IS NOT NULL, ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4),
153+
NULL) AS `tier_price`
154+
FROM `catalog_product_index_price_bundle_temp` AS `i`
155+
INNER JOIN `catalog_product_entity` AS `parent_product` ON parent_product.entity_id = i.entity_id AND
156+
(parent_product.created_in <= 1 AND parent_product.updated_in > 1)
157+
INNER JOIN `catalog_product_bundle_option` AS `bo` ON bo.parent_id = parent_product.row_id
158+
INNER JOIN `catalog_product_bundle_selection` AS `bs` ON bs.option_id = bo.option_id
159+
INNER JOIN `catalog_product_index_price_replica` AS `idx` USE INDEX (PRIMARY)
160+
ON bs.product_id = idx.entity_id AND i.customer_group_id = idx.customer_group_id AND
161+
i.website_id = idx.website_id
162+
INNER JOIN `cataloginventory_stock_status` AS `si` ON si.product_id = bs.product_id
163+
WHERE (i.price_type = 0)
164+
AND (si.stock_status = 1)
165+
ON DUPLICATE KEY UPDATE `entity_id` = VALUES(`entity_id`),
166+
`customer_group_id` = VALUES(`customer_group_id`),
167+
`website_id` = VALUES(`website_id`),
168+
`option_id` = VALUES(`option_id`),
169+
`selection_id` = VALUES(`selection_id`),
170+
`group_type` = VALUES(`group_type`),
171+
`is_required` = VALUES(`is_required`),
172+
`price` = VALUES(`price`),
173+
`tier_price` = VALUES(`tier_price`) ON DUPLICATE KEY UPDATE = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES()";
174+
//@codingStandardsIgnoreEnd
175+
$this->connectionMock->expects($this->exactly(3))
176+
->method('getCheckSql')
177+
->withConsecutive(
178+
[
179+
'i.special_price > 0 AND i.special_price < 100',
180+
'ROUND(' . $price . ' * (i.special_price / 100), 4)',
181+
$price
182+
],
183+
[
184+
'i.tier_percent IS NOT NULL',
185+
'ROUND((1 - i.tier_percent / 100) * ' . $price . ', 4)',
186+
'NULL'
187+
],
188+
["bo.type = 'select' OR bo.type = 'radio'", '0', '1']
189+
);
190+
191+
$select = $this->createMock(\Magento\Framework\DB\Select::class);
192+
$select->expects($this->once())->method('from')->willReturn($select);
193+
$select->expects($this->exactly(5))->method('join')->willReturn($select);
194+
$select->expects($this->exactly(2))->method('where')->willReturn($select);
195+
$select->expects($this->once())->method('columns')->willReturn($select);
196+
$select->expects($this->any())->method('__toString')->willReturn($selectQuery);
197+
198+
$this->connectionMock->expects($this->once())->method('getIfNullSql');
199+
$this->connectionMock->expects($this->once())->method('getLeastSql');
200+
$this->connectionMock->expects($this->any())
201+
->method('select')
202+
->willReturn($select);
203+
$this->connectionMock->expects($this->exactly(9))->method('quoteIdentifier');
204+
$this->connectionMock->expects($this->once())->method('query')->with($processedQuery);
205+
206+
$pool = $this->createMock(EntityMetadataInterface::class);
207+
$pool->expects($this->once())->method('getLinkField')->willReturn($entity);
208+
$this->metadataPool->expects($this->once())
209+
->method('getMetadata')
210+
->willReturn($pool);
211+
212+
$this->invokeMethodViaReflection('calculateDynamicBundleSelectionPrice', []);
213+
}
214+
92215
/**
93216
* Tests create Bundle Price temporary table
94217
*/
@@ -147,16 +270,18 @@ public function testGetBundleOptionTable(): void
147270
* Invoke private method via reflection
148271
*
149272
* @param string $methodName
273+
* @param array $args
150274
* @return string
275+
* @throws \ReflectionException
151276
*/
152-
private function invokeMethodViaReflection(string $methodName): string
277+
private function invokeMethodViaReflection(string $methodName, array $args = []): string
153278
{
154279
$method = new \ReflectionMethod(
155280
Price::class,
156281
$methodName
157282
);
158283
$method->setAccessible(true);
159284

160-
return (string)$method->invoke($this->priceModel);
285+
return (string)$method->invoke($this->priceModel, $args);
161286
}
162287
}

0 commit comments

Comments
 (0)