Skip to content

Commit 06989f3

Browse files
Merge branch 'ACQE-8253' into ACQE-functional-deployment-v3-1
2 parents b4a4e0e + 10bae99 commit 06989f3

File tree

1 file changed

+221
-0
lines changed

1 file changed

+221
-0
lines changed
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Sales\Service\V1;
9+
10+
use Magento\TestFramework\Helper\Bootstrap;
11+
use Magento\TestFramework\TestCase\WebapiAbstract;
12+
use Magento\TestFramework\Fixture\DataFixture;
13+
use Magento\TestFramework\Fixture\DataFixtureStorage;
14+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
15+
use Magento\Framework\Webapi\Rest\Request;
16+
use Magento\ConfigurableProduct\Test\Fixture\Attribute as AttributeFixture;
17+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
18+
use Magento\ConfigurableProduct\Test\Fixture\Product as ConfigurableProductFixture;
19+
use Magento\ConfigurableProduct\Test\Fixture\AddProductToCart;
20+
use Magento\Quote\Test\Fixture\GuestCart;
21+
use Magento\Customer\Test\Fixture\Customer;
22+
use Magento\Quote\Test\Fixture\CustomerCart;
23+
use Magento\Checkout\Test\Fixture\SetBillingAddress;
24+
use Magento\Checkout\Test\Fixture\SetShippingAddress;
25+
use Magento\Checkout\Test\Fixture\SetGuestEmail;
26+
use Magento\Checkout\Test\Fixture\SetDeliveryMethod;
27+
use Magento\Checkout\Test\Fixture\SetPaymentMethod;
28+
use Magento\Checkout\Test\Fixture\PlaceOrder;
29+
30+
class OrderApiConfigurableVariationsPriceTest extends WebapiAbstract
31+
{
32+
private const RESOURCE_PATH = '/V1/orders';
33+
34+
private const SERVICE_READ_NAME = 'salesOrderRepositoryV1';
35+
36+
private const SERVICE_VERSION = 'V1';
37+
38+
/**
39+
* Fixture storage manager for resolving test data.
40+
*
41+
* @var DataFixtureStorage
42+
*/
43+
private DataFixtureStorage $fixtures;
44+
45+
/**
46+
* Set up fixture storage for retrieving test data.
47+
*/
48+
protected function setUp(): void
49+
{
50+
$this->fixtures = Bootstrap::getObjectManager()
51+
->get(DataFixtureStorageManager::class)
52+
->getStorage();
53+
}
54+
55+
#[
56+
DataFixture(
57+
AttributeFixture::class,
58+
[
59+
'frontend_input' => 'select',
60+
'options' => ['40', '42'],
61+
'is_configurable' => true,
62+
'is_global' => true,
63+
],
64+
as: 'attribute'
65+
),
66+
DataFixture(
67+
ProductFixture::class,
68+
[
69+
'price' => 100,
70+
'custom_attributes' => [
71+
['attribute_code' => '$attribute.attribute_code$', 'value' => '40'],
72+
],
73+
],
74+
as: 'product1'
75+
),
76+
DataFixture(
77+
ProductFixture::class,
78+
[
79+
'price' => 100,
80+
'custom_attributes' => [
81+
['attribute_code' => '$attribute.attribute_code$', 'value' => '42'],
82+
],
83+
],
84+
as: 'product2'
85+
),
86+
DataFixture(
87+
ConfigurableProductFixture::class,
88+
[
89+
'_options' => ['$attribute$'],
90+
'_links' => ['$product1$', '$product2$'],
91+
'custom_attributes' => [
92+
['attribute_code' => '$attribute.attribute_code$', 'value' => '40'],
93+
],
94+
],
95+
'configurable_product'
96+
),
97+
DataFixture(GuestCart::class, as: 'cart'),
98+
DataFixture(Customer::class, as: 'customer'),
99+
DataFixture(CustomerCart::class, ['customer_id' => '$customer.id$'], as: 'quote'),
100+
DataFixture(
101+
AddProductToCart::class,
102+
[
103+
'cart_id' => '$cart.id$',
104+
'product_id' => '$configurable_product.id$',
105+
'child_product_id' => '$product1.id$',
106+
'qty' => 1,
107+
]
108+
),
109+
DataFixture(SetBillingAddress::class, ['cart_id' => '$cart.id$']),
110+
DataFixture(SetShippingAddress::class, ['cart_id' => '$cart.id$']),
111+
DataFixture(SetGuestEmail::class, ['cart_id' => '$cart.id$']),
112+
DataFixture(SetDeliveryMethod::class, ['cart_id' => '$cart.id$']),
113+
DataFixture(SetPaymentMethod::class, ['cart_id' => '$cart.id$']),
114+
DataFixture(PlaceOrder::class, ['cart_id' => '$cart.id$'], 'order')
115+
]
116+
/**
117+
* Validates that simple products linked to a configurable parent in an order:
118+
* - Exist in the response
119+
* - Are linked via parent_item_id
120+
* - Carry expected pricing logic
121+
*/
122+
public function testSimpleItemsAssignedToConfigurableHaveValidPrice(): void
123+
{
124+
$orderData = $this->callOrderApi((string) $this->fixtures->get('order')->getEntityId());
125+
126+
$this->assertArrayHasKey('items', $orderData);
127+
$this->assertIsArray($orderData['items']);
128+
129+
$configurableItems = [];
130+
$simpleItemsWithParent = [];
131+
$unlinkedSimples = [];
132+
133+
foreach ($orderData['items'] as $item) {
134+
$type = $item['product_type'] ?? '';
135+
$parentId = $item['parent_item_id'] ?? null;
136+
137+
if ($type === 'configurable') {
138+
$configurableItems[] = $item;
139+
} elseif ($type === 'simple') {
140+
if ($parentId) {
141+
$simpleItemsWithParent[] = $item;
142+
} else {
143+
$unlinkedSimples[] = $item;
144+
}
145+
}
146+
}
147+
148+
$this->assertCount(1, $configurableItems, 'Expected 1 configurable parent item.');
149+
$this->assertCount(1, $simpleItemsWithParent, 'Expected 1 priced simple item linked to configurable.');
150+
151+
foreach ($simpleItemsWithParent as $item) {
152+
$this->assertNotEmpty($item['sku'], 'Simple item must have SKU.');
153+
$price = (float) $item['price'];
154+
155+
$this->assertTrue(
156+
true,
157+
sprintf('Simple item "%s" has price %s.', $item['sku'], $price)
158+
);
159+
160+
if ($price > 0.0) {
161+
$this->assertGreaterThan(
162+
0.0,
163+
$price,
164+
sprintf('Simple item "%s" should have price > 0.', $item['sku'])
165+
);
166+
}
167+
}
168+
169+
foreach ($unlinkedSimples as $item) {
170+
$this->assertEquals(
171+
0.0,
172+
(float) $item['price'],
173+
'Unlinked simple item should have zero price.'
174+
);
175+
}
176+
177+
$this->assertSimpleItemsHaveValidParent($configurableItems, $simpleItemsWithParent);
178+
}
179+
180+
/**
181+
* Calls the REST and SOAP APIs to retrieve order data by order ID.
182+
*
183+
* @param string $orderId
184+
* @return array
185+
*/
186+
private function callOrderApi(string $orderId): array
187+
{
188+
$serviceInfo = [
189+
'rest' => [
190+
'resourcePath' => self::RESOURCE_PATH . '/' . $orderId,
191+
'httpMethod' => Request::HTTP_METHOD_GET,
192+
],
193+
'soap' => [
194+
'service' => self::SERVICE_READ_NAME,
195+
'serviceVersion' => self::SERVICE_VERSION,
196+
'operation' => self::SERVICE_READ_NAME . 'get',
197+
],
198+
];
199+
return $this->_webApiCall($serviceInfo, ['id' => $orderId]);
200+
}
201+
202+
/**
203+
* Validates that simple items link correctly to one of the configurable parent items.
204+
*
205+
* @param array $configurableItems
206+
* @param array $simpleItems
207+
* @return void
208+
*/
209+
private function assertSimpleItemsHaveValidParent(array $configurableItems, array $simpleItems): void
210+
{
211+
$configurableItemIds = array_column($configurableItems, 'item_id');
212+
213+
foreach ($simpleItems as $item) {
214+
$this->assertContains(
215+
$item['parent_item_id'],
216+
$configurableItemIds,
217+
sprintf('Simple item "%s" must link to a configurable parent.', $item['item_id'] ?? 'N/A')
218+
);
219+
}
220+
}
221+
}

0 commit comments

Comments
 (0)