Skip to content

Commit 0869361

Browse files
Merge branch 'ACQE-8867' into ACQE-functional-deployment-version20
2 parents 602861c + 55ca490 commit 0869361

File tree

1 file changed

+399
-0
lines changed

1 file changed

+399
-0
lines changed
Lines changed: 399 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,399 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Sales\Model;
9+
10+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
11+
use Magento\Checkout\Test\Fixture\PlaceOrder as PlaceOrderFixture;
12+
use Magento\Checkout\Test\Fixture\SetBillingAddress as SetBillingAddressFixture;
13+
use Magento\Checkout\Test\Fixture\SetDeliveryMethod as SetDeliveryMethodFixture;
14+
use Magento\Checkout\Test\Fixture\SetGuestEmail as SetGuestEmailFixture;
15+
use Magento\Checkout\Test\Fixture\SetPaymentMethod as SetPaymentMethodFixture;
16+
use Magento\Checkout\Test\Fixture\SetShippingAddress as SetShippingAddressFixture;
17+
use Magento\Framework\App\ResourceConnection;
18+
use Magento\Framework\DB\Adapter\AdapterInterface;
19+
use Magento\Framework\Exception\LocalizedException;
20+
use Magento\Quote\Test\Fixture\AddProductToCart as AddProductToCartFixture;
21+
use Magento\Quote\Test\Fixture\GuestCart as GuestCartFixture;
22+
use Magento\Sales\Test\Fixture\Creditmemo as CreditmemoFixture;
23+
use Magento\Sales\Test\Fixture\Invoice as InvoiceFixture;
24+
use Magento\Sales\Test\Fixture\Shipment as ShipmentFixture;
25+
use Magento\TestFramework\Fixture\Config;
26+
use Magento\TestFramework\Fixture\DataFixture;
27+
use Magento\TestFramework\Fixture\DataFixtureStorage;
28+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
29+
use Magento\TestFramework\Helper\Bootstrap;
30+
use PHPUnit\Framework\TestCase;
31+
32+
/**
33+
* Test asynchronous grid indexing for OMS entities
34+
*
35+
* Verifies that entities are synced from main tables to grid tables
36+
*
37+
* @magentoDbIsolation enabled
38+
* @magentoAppIsolation enabled
39+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
40+
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
41+
*/
42+
class OmsGridAsyncInsertTest extends TestCase
43+
{
44+
/**
45+
* @var \Magento\Framework\ObjectManagerInterface
46+
*/
47+
private $objectManager;
48+
49+
/**
50+
* @var ResourceConnection
51+
*/
52+
private ResourceConnection $resourceConnection;
53+
54+
/**
55+
* @var AdapterInterface
56+
*/
57+
private AdapterInterface $connection;
58+
59+
/**
60+
* @var DataFixtureStorage
61+
*/
62+
private DataFixtureStorage $fixtures;
63+
64+
/**
65+
* Set up test dependencies
66+
*
67+
* @return void
68+
* @throws LocalizedException
69+
*/
70+
protected function setUp(): void
71+
{
72+
$this->objectManager = Bootstrap::getObjectManager();
73+
$this->resourceConnection = $this->objectManager->get(ResourceConnection::class);
74+
$this->connection = $this->resourceConnection->getConnection();
75+
$this->fixtures = $this->objectManager->get(DataFixtureStorageManager::class)->getStorage();
76+
}
77+
78+
/**
79+
* Test Order async grid insert
80+
*
81+
* Verifies that:
82+
* - Order exists in sales_order table after creation
83+
* - Order does NOT exist in sales_order_grid table before async insert
84+
* - Order exists in sales_order_grid table after async insert
85+
* - Key fields match between main table and grid table
86+
*
87+
* @return void
88+
*/
89+
#[
90+
Config('dev/grid/async_indexing', '1'),
91+
Config('payment/checkmo/active', '1'),
92+
Config('carriers/flatrate/active', '1'),
93+
DataFixture(ProductFixture::class, as: 'product'),
94+
DataFixture(GuestCartFixture::class, as: 'cart'),
95+
DataFixture(SetGuestEmailFixture::class, ['cart_id' => '$cart.id$', 'email' => 'guest@example.com']),
96+
DataFixture(AddProductToCartFixture::class, [
97+
'cart_id' => '$cart.id$', 'product_id' => '$product.id$', 'qty' => 1
98+
]),
99+
DataFixture(SetBillingAddressFixture::class, ['cart_id' => '$cart.id$']),
100+
DataFixture(SetShippingAddressFixture::class, ['cart_id' => '$cart.id$']),
101+
DataFixture(SetDeliveryMethodFixture::class, [
102+
'cart_id' => '$cart.id$', 'carrier_code' => 'flatrate', 'method_code' => 'flatrate'
103+
]),
104+
DataFixture(SetPaymentMethodFixture::class, ['cart_id' => '$cart.id$', 'method' => 'checkmo']),
105+
DataFixture(PlaceOrderFixture::class, ['cart_id' => '$cart.id$'], 'order'),
106+
]
107+
public function testOrderAsyncGridInsert(): void
108+
{
109+
$orderId = (int)$this->fixtures->get('order')->getEntityId();
110+
111+
// Verify order exists in main table but NOT in grid
112+
$this->assertEntityInMainTableButNotInGrid(
113+
'sales_order',
114+
'sales_order_grid',
115+
$orderId,
116+
'Order'
117+
);
118+
119+
// Execute async grid insert directly
120+
$this->executeAsyncGridInsert('SalesOrderIndexGridAsyncInsert');
121+
122+
// Verify order exists in both tables
123+
$this->assertEntityInBothMainTableAndGrid(
124+
'sales_order',
125+
'sales_order_grid',
126+
$orderId,
127+
['increment_id', 'status', 'grand_total'],
128+
'Order'
129+
);
130+
}
131+
132+
/**
133+
* Test Invoice async grid insert
134+
*
135+
* Verifies that:
136+
* - Invoice exists in sales_invoice table after creation
137+
* - Invoice does NOT exist in sales_invoice_grid table before async insert
138+
* - Invoice exists in sales_invoice_grid table after async insert
139+
* - Key fields match between main table and grid table
140+
*
141+
* @return void
142+
*/
143+
#[
144+
Config('dev/grid/async_indexing', '1'),
145+
Config('payment/checkmo/active', '1'),
146+
Config('carriers/flatrate/active', '1'),
147+
DataFixture(ProductFixture::class, as: 'product'),
148+
DataFixture(GuestCartFixture::class, as: 'cart'),
149+
DataFixture(SetGuestEmailFixture::class, ['cart_id' => '$cart.id$', 'email' => 'guest@example.com']),
150+
DataFixture(AddProductToCartFixture::class, [
151+
'cart_id' => '$cart.id$', 'product_id' => '$product.id$', 'qty' => 1
152+
]),
153+
DataFixture(SetBillingAddressFixture::class, ['cart_id' => '$cart.id$']),
154+
DataFixture(SetShippingAddressFixture::class, ['cart_id' => '$cart.id$']),
155+
DataFixture(SetDeliveryMethodFixture::class, [
156+
'cart_id' => '$cart.id$', 'carrier_code' => 'flatrate', 'method_code' => 'flatrate'
157+
]),
158+
DataFixture(SetPaymentMethodFixture::class, ['cart_id' => '$cart.id$', 'method' => 'checkmo']),
159+
DataFixture(PlaceOrderFixture::class, ['cart_id' => '$cart.id$'], 'order'),
160+
DataFixture(InvoiceFixture::class, ['order_id' => '$order.id$'], 'invoice'),
161+
]
162+
public function testInvoiceAsyncGridInsert(): void
163+
{
164+
$invoiceId = (int)$this->fixtures->get('invoice')->getEntityId();
165+
166+
// Verify invoice exists in main table but NOT in grid
167+
$this->assertEntityInMainTableButNotInGrid(
168+
'sales_invoice',
169+
'sales_invoice_grid',
170+
$invoiceId,
171+
'Invoice'
172+
);
173+
174+
// Execute async grid insert directly
175+
$this->executeAsyncGridInsert('SalesInvoiceIndexGridAsyncInsert');
176+
177+
// Verify invoice exists in both tables
178+
$this->assertEntityInBothMainTableAndGrid(
179+
'sales_invoice',
180+
'sales_invoice_grid',
181+
$invoiceId,
182+
['increment_id', 'state', 'grand_total'],
183+
'Invoice'
184+
);
185+
}
186+
187+
/**
188+
* Test Shipment async grid insert
189+
*
190+
* Verifies that:
191+
* - Shipment exists in sales_shipment table after creation
192+
* - Shipment does NOT exist in sales_shipment_grid table before async insert
193+
* - Shipment exists in sales_shipment_grid table after async insert
194+
* - Key fields match between main table and grid table
195+
*
196+
* @return void
197+
*/
198+
#[
199+
Config('dev/grid/async_indexing', '1'),
200+
Config('payment/checkmo/active', '1'),
201+
Config('carriers/flatrate/active', '1'),
202+
DataFixture(ProductFixture::class, as: 'product'),
203+
DataFixture(GuestCartFixture::class, as: 'cart'),
204+
DataFixture(SetGuestEmailFixture::class, ['cart_id' => '$cart.id$', 'email' => 'guest@example.com']),
205+
DataFixture(AddProductToCartFixture::class, [
206+
'cart_id' => '$cart.id$', 'product_id' => '$product.id$', 'qty' => 1
207+
]),
208+
DataFixture(SetBillingAddressFixture::class, ['cart_id' => '$cart.id$']),
209+
DataFixture(SetShippingAddressFixture::class, ['cart_id' => '$cart.id$']),
210+
DataFixture(SetDeliveryMethodFixture::class, [
211+
'cart_id' => '$cart.id$', 'carrier_code' => 'flatrate', 'method_code' => 'flatrate'
212+
]),
213+
DataFixture(SetPaymentMethodFixture::class, ['cart_id' => '$cart.id$', 'method' => 'checkmo']),
214+
DataFixture(PlaceOrderFixture::class, ['cart_id' => '$cart.id$'], 'order'),
215+
DataFixture(ShipmentFixture::class, ['order_id' => '$order.id$'], 'shipment'),
216+
]
217+
public function testShipmentAsyncGridInsert(): void
218+
{
219+
$shipmentId = (int)$this->fixtures->get('shipment')->getEntityId();
220+
221+
// Verify shipment exists in main table but NOT in grid
222+
$this->assertEntityInMainTableButNotInGrid(
223+
'sales_shipment',
224+
'sales_shipment_grid',
225+
$shipmentId,
226+
'Shipment'
227+
);
228+
229+
// Execute async grid insert directly
230+
$this->executeAsyncGridInsert('SalesShipmentIndexGridAsyncInsert');
231+
232+
// Verify shipment exists in both tables
233+
$this->assertEntityInBothMainTableAndGrid(
234+
'sales_shipment',
235+
'sales_shipment_grid',
236+
$shipmentId,
237+
['increment_id', 'total_qty'],
238+
'Shipment'
239+
);
240+
}
241+
242+
/**
243+
* Test Creditmemo async grid insert
244+
*
245+
* Verifies that:
246+
* - Creditmemo exists in sales_creditmemo table after creation
247+
* - Creditmemo does NOT exist in sales_creditmemo_grid table before async insert
248+
* - Creditmemo exists in sales_creditmemo_grid table after async insert
249+
* - Key fields match between main table and grid table
250+
*
251+
* @return void
252+
*/
253+
#[
254+
Config('dev/grid/async_indexing', '1'),
255+
Config('payment/checkmo/active', '1'),
256+
Config('carriers/flatrate/active', '1'),
257+
DataFixture(ProductFixture::class, as: 'product'),
258+
DataFixture(GuestCartFixture::class, as: 'cart'),
259+
DataFixture(SetGuestEmailFixture::class, ['cart_id' => '$cart.id$', 'email' => 'guest@example.com']),
260+
DataFixture(AddProductToCartFixture::class, [
261+
'cart_id' => '$cart.id$', 'product_id' => '$product.id$', 'qty' => 1
262+
]),
263+
DataFixture(SetBillingAddressFixture::class, ['cart_id' => '$cart.id$']),
264+
DataFixture(SetShippingAddressFixture::class, ['cart_id' => '$cart.id$']),
265+
DataFixture(SetDeliveryMethodFixture::class, [
266+
'cart_id' => '$cart.id$', 'carrier_code' => 'flatrate', 'method_code' => 'flatrate'
267+
]),
268+
DataFixture(SetPaymentMethodFixture::class, ['cart_id' => '$cart.id$', 'method' => 'checkmo']),
269+
DataFixture(PlaceOrderFixture::class, ['cart_id' => '$cart.id$'], 'order'),
270+
DataFixture(InvoiceFixture::class, ['order_id' => '$order.id$'], 'invoice'),
271+
DataFixture(CreditmemoFixture::class, ['order_id' => '$order.id$'], 'creditmemo'),
272+
]
273+
public function testCreditmemoAsyncGridInsert(): void
274+
{
275+
$creditmemoId = (int)$this->fixtures->get('creditmemo')->getEntityId();
276+
277+
// Verify creditmemo exists in main table but NOT in grid
278+
$this->assertEntityInMainTableButNotInGrid(
279+
'sales_creditmemo',
280+
'sales_creditmemo_grid',
281+
$creditmemoId,
282+
'Creditmemo'
283+
);
284+
285+
// Execute async grid insert directly
286+
$this->executeAsyncGridInsert('SalesCreditmemoIndexGridAsyncInsert');
287+
288+
// Verify creditmemo exists in both tables
289+
$this->assertEntityInBothMainTableAndGrid(
290+
'sales_creditmemo',
291+
'sales_creditmemo_grid',
292+
$creditmemoId,
293+
['increment_id', 'state', 'grand_total'],
294+
'Creditmemo'
295+
);
296+
}
297+
298+
/**
299+
* Execute async grid insert directly (bypasses cron scheduling)
300+
*
301+
* This method calls the GridAsyncInsert service directly without going through
302+
* the cron scheduler. This is faster and more reliable for testing.
303+
*
304+
* @param string $virtualTypeName
305+
* @return void
306+
*/
307+
private function executeAsyncGridInsert(string $virtualTypeName): void
308+
{
309+
$this->objectManager->get($virtualTypeName)->asyncInsert();
310+
}
311+
312+
/**
313+
* Assert entity exists in main table but NOT in grid table
314+
*
315+
* Verifies the state before async grid insert has been executed.
316+
*
317+
* @param string $mainTable
318+
* @param string $gridTable
319+
* @param int $entityId
320+
* @param string $entityType
321+
* @return void
322+
*/
323+
private function assertEntityInMainTableButNotInGrid(
324+
string $mainTable,
325+
string $gridTable,
326+
int $entityId,
327+
string $entityType
328+
): void {
329+
$this->assertNotEmpty(
330+
$this->getEntityFromTable($mainTable, $entityId),
331+
"{$entityType} {$entityId} should exist in {$mainTable} table"
332+
);
333+
334+
$this->assertEmpty(
335+
$this->getEntityFromTable($gridTable, $entityId),
336+
"{$entityType} {$entityId} should NOT be in {$gridTable} yet (before async insert)"
337+
);
338+
}
339+
340+
/**
341+
* Assert entity exists in both main table and grid table with matching data
342+
*
343+
* Verifies the state after async grid insert has been executed.
344+
*
345+
* @param string $mainTable
346+
* @param string $gridTable
347+
* @param int $entityId
348+
* @param array $fieldsToCompare
349+
* @param string $entityType
350+
* @return void
351+
*/
352+
private function assertEntityInBothMainTableAndGrid(
353+
string $mainTable,
354+
string $gridTable,
355+
int $entityId,
356+
array $fieldsToCompare,
357+
string $entityType
358+
): void {
359+
$entityInMainTable = $this->getEntityFromTable($mainTable, $entityId);
360+
$this->assertNotEmpty(
361+
$entityInMainTable,
362+
"{$entityType} {$entityId} should exist in {$mainTable} table"
363+
);
364+
365+
$entityInGrid = $this->getEntityFromTable($gridTable, $entityId);
366+
$this->assertNotEmpty(
367+
$entityInGrid,
368+
"{$entityType} {$entityId} should be in {$gridTable} after async insert"
369+
);
370+
371+
// Verify specified fields match between main table and grid
372+
foreach ($fieldsToCompare as $field) {
373+
if (isset($entityInMainTable[$field]) && isset($entityInGrid[$field])) {
374+
$this->assertEquals(
375+
$entityInMainTable[$field],
376+
$entityInGrid[$field],
377+
"{$entityType} field '{$field}' should match between {$mainTable} and {$gridTable}"
378+
);
379+
}
380+
}
381+
}
382+
383+
/**
384+
* Get entity data from specified table by entity ID
385+
*
386+
* @param string $tableName
387+
* @param int $entityId
388+
* @return array|null
389+
*/
390+
private function getEntityFromTable(string $tableName, int $entityId): ?array
391+
{
392+
$select = $this->connection->select()
393+
->from($this->resourceConnection->getTableName($tableName))
394+
->where('entity_id = ?', $entityId);
395+
396+
$row = $this->connection->fetchRow($select);
397+
return $row ?: null;
398+
}
399+
}

0 commit comments

Comments
 (0)