Skip to content

Commit 4a996a4

Browse files
committed
ACP2E-1961: Cron job aggregate_sales_report_bestsellers_data is slow and affects on performance
- added unit tests for coverage
1 parent 1eaa7c8 commit 4a996a4

File tree

2 files changed

+243
-2
lines changed

2 files changed

+243
-2
lines changed

app/code/Magento/Sales/Model/ResourceModel/Report/Bestsellers.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ private function clearByDateRange($from = null, $to = null): void
181181
*/
182182
private function getRangeSubSelect($from = null, $to = null): ?Select
183183
{
184+
$subSelect = null;
184185
if ($from !== null || $to !== null) {
185186
$subSelect = $this->_getTableDateRangeSelect(
186187
$this->getTable('sales_order'),
@@ -189,8 +190,6 @@ private function getRangeSubSelect($from = null, $to = null): ?Select
189190
$from,
190191
$to
191192
);
192-
} else {
193-
$subSelect = null;
194193
}
195194

196195
return $subSelect;
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
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\Sales\Test\Unit\Model\ResourceModel\Report;
9+
10+
use Magento\Catalog\Model\ResourceModel\Product;
11+
use Magento\Framework\App\ResourceConnection;
12+
use Magento\Framework\DB\Adapter\AdapterInterface;
13+
use Magento\Framework\DB\Select;
14+
use Magento\Framework\Model\ResourceModel\Db\Context;
15+
use Magento\Framework\Stdlib\DateTime\DateTime;
16+
use Magento\Framework\Stdlib\DateTime\Timezone\Validator;
17+
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
18+
use Magento\Reports\Model\Flag;
19+
use Magento\Reports\Model\FlagFactory;
20+
use Magento\Sales\Model\ResourceModel\Helper;
21+
use Magento\Sales\Model\ResourceModel\Report\Bestsellers;
22+
use Magento\Store\Api\Data\StoreInterface;
23+
use Magento\Store\Model\StoreManagerInterface;
24+
use PHPUnit\Framework\MockObject\MockObject;
25+
use PHPUnit\Framework\TestCase;
26+
use Psr\Log\LoggerInterface;
27+
28+
class BestsellersTest extends TestCase
29+
{
30+
/**
31+
* @var Product|MockObject
32+
*/
33+
protected Product $_productResource;
34+
35+
/**
36+
* @var Helper|MockObject
37+
*/
38+
protected Helper $_salesResourceHelper;
39+
40+
/**
41+
* @var StoreManagerInterface|MockObject
42+
*/
43+
protected StoreManagerInterface $storeManager;
44+
45+
/**
46+
* @var Bestsellers
47+
*/
48+
protected Bestsellers $report;
49+
50+
/**
51+
* @var Context
52+
*/
53+
protected Context $context;
54+
55+
/**
56+
* @var LoggerInterface
57+
*/
58+
protected LoggerInterface $logger;
59+
60+
/**
61+
* @var TimezoneInterface
62+
*/
63+
protected TimezoneInterface $time;
64+
65+
/**
66+
* @var FlagFactory
67+
*/
68+
protected FlagFactory $flagFactory;
69+
70+
/**
71+
* @var Validator
72+
*/
73+
protected Validator $validator;
74+
75+
/**
76+
* @var DateTime
77+
*/
78+
protected DateTime $date;
79+
80+
/**
81+
* @var Product
82+
*/
83+
protected Product $product;
84+
85+
/**
86+
* @var Helper
87+
*/
88+
protected Helper $helper;
89+
90+
/**
91+
* @var string
92+
*/
93+
protected string $connectionName = 'connection_name';
94+
95+
/**
96+
* @inheritDoc
97+
*/
98+
protected function setUp(): void
99+
{
100+
$this->context = $this->createMock(Context::class);
101+
$this->logger = $this->createMock(LoggerInterface::class);
102+
$this->time = $this->createMock(TimezoneInterface::class);
103+
$this->flagFactory = $this->createMock(FlagFactory::class);
104+
$this->validator = $this->createMock(Validator::class);
105+
$this->date = $this->createMock(DateTime::class);
106+
$this->product = $this->createMock(Product::class);
107+
$this->helper = $this->createMock(Helper::class);
108+
$this->storeManager = $this->createMock(StoreManagerInterface::class);
109+
110+
parent::setUp();
111+
}
112+
113+
/**
114+
* @return void
115+
* @throws \Exception
116+
*/
117+
public function testAggregatePerStoreCalculationWithInterval(): void
118+
{
119+
$from = new \DateTime('yesterday');
120+
$to = new \DateTime();
121+
$periodExpr = 'DATE(DATE_ADD(`source_table`.`created_at`, INTERVAL -25200 SECOND))';
122+
$select = $this->getMockBuilder(Select::class)
123+
->disableOriginalConstructor()
124+
->getMock();
125+
$select->expects($this->exactly(2))->method('group');
126+
$select->expects($this->exactly(5))->method('from')->willReturn($select);
127+
$select->expects($this->exactly(3))->method('distinct')->willReturn($select);
128+
$select->expects($this->once())->method('joinInner')->willReturn($select);
129+
$select->expects($this->once())->method('joinLeft')->willReturn($select);
130+
$select->expects($this->any())->method('where')->willReturn($select);
131+
$select->expects($this->once())->method('useStraightJoin');
132+
$select->expects($this->exactly(2))->method('insertFromSelect');
133+
$connection = $this->createMock(AdapterInterface::class);
134+
$connection->expects($this->exactly(4))
135+
->method('getDatePartSql')
136+
->willReturn($periodExpr);
137+
$connection->expects($this->any())->method('select')->willReturn($select);
138+
$query = $this->createMock(\Zend_Db_Statement_Interface::class);
139+
$connection->expects($this->exactly(3))->method('query')->willReturn($query);
140+
$resource = $this->createMock(ResourceConnection::class);
141+
$resource->expects($this->any())
142+
->method('getConnection')
143+
->with($this->connectionName)
144+
->willReturn($connection);
145+
$this->context->expects($this->any())->method('getResources')->willReturn($resource);
146+
147+
$store = $this->createMock(StoreInterface::class);
148+
$store->expects($this->once())->method('getId')->willReturn(1);
149+
$this->storeManager->expects($this->once())->method('getStores')->with(true)->willReturn([$store]);
150+
151+
$this->helper->expects($this->exactly(3))->method('getBestsellersReportUpdateRatingPos');
152+
153+
$flag = $this->createMock(Flag::class);
154+
$flag->expects($this->once())->method('setReportFlagCode')->willReturn($flag);
155+
$flag->expects($this->once())->method('unsetData')->willReturn($flag);
156+
$flag->expects($this->once())->method('loadSelf');
157+
$this->flagFactory->expects($this->once())->method('create')->willReturn($flag);
158+
159+
$date = $this->createMock(\DateTime::class);
160+
$date->expects($this->exactly(4))->method('format')->with('e');
161+
$this->time->expects($this->exactly(4))->method('scopeDate')->willReturn($date);
162+
163+
$this->report = new Bestsellers(
164+
$this->context,
165+
$this->logger,
166+
$this->time,
167+
$this->flagFactory,
168+
$this->validator,
169+
$this->date,
170+
$this->product,
171+
$this->helper,
172+
$this->connectionName,
173+
[],
174+
$this->storeManager
175+
);
176+
177+
$this->report->aggregate($from, $to);
178+
}
179+
180+
/**
181+
* @return void
182+
* @throws \Exception
183+
*/
184+
public function testAggregatePerStoreCalculationNoInterval(): void
185+
{
186+
$periodExpr = 'DATE(DATE_ADD(`source_table`.`created_at`, INTERVAL -25200 SECOND))';
187+
$select = $this->getMockBuilder(Select::class)
188+
->disableOriginalConstructor()
189+
->getMock();
190+
$select->expects($this->exactly(2))->method('group');
191+
$select->expects($this->exactly(3))->method('from')->willReturn($select);
192+
$select->expects($this->once())->method('joinInner')->willReturn($select);
193+
$select->expects($this->once())->method('joinLeft')->willReturn($select);
194+
$select->expects($this->exactly(3))->method('where')->willReturn($select);
195+
$select->expects($this->once())->method('useStraightJoin');
196+
$select->expects($this->exactly(2))->method('insertFromSelect');
197+
$connection = $this->createMock(AdapterInterface::class);
198+
$connection->expects($this->once())
199+
->method('getDatePartSql')
200+
->willReturn($periodExpr);
201+
$connection->expects($this->any())->method('select')->willReturn($select);
202+
$connection->expects($this->exactly(2))->method('query');
203+
$resource = $this->createMock(ResourceConnection::class);
204+
$resource->expects($this->any())
205+
->method('getConnection')
206+
->with($this->connectionName)
207+
->willReturn($connection);
208+
$this->context->expects($this->any())->method('getResources')->willReturn($resource);
209+
210+
$store = $this->createMock(StoreInterface::class);
211+
$store->expects($this->once())->method('getId')->willReturn(1);
212+
$this->storeManager->expects($this->once())->method('getStores')->with(true)->willReturn([$store]);
213+
214+
$this->helper->expects($this->exactly(3))->method('getBestsellersReportUpdateRatingPos');
215+
216+
$flag = $this->createMock(Flag::class);
217+
$flag->expects($this->once())->method('setReportFlagCode')->willReturn($flag);
218+
$flag->expects($this->once())->method('unsetData')->willReturn($flag);
219+
$flag->expects($this->once())->method('loadSelf');
220+
$this->flagFactory->expects($this->once())->method('create')->willReturn($flag);
221+
222+
$date = $this->createMock(\DateTime::class);
223+
$date->expects($this->once())->method('format')->with('e');
224+
$this->time->expects($this->once())->method('scopeDate')->willReturn($date);
225+
226+
$this->report = new Bestsellers(
227+
$this->context,
228+
$this->logger,
229+
$this->time,
230+
$this->flagFactory,
231+
$this->validator,
232+
$this->date,
233+
$this->product,
234+
$this->helper,
235+
$this->connectionName,
236+
[],
237+
$this->storeManager
238+
);
239+
240+
$this->report->aggregate();
241+
}
242+
}

0 commit comments

Comments
 (0)