Skip to content

Commit facd914

Browse files
Merge branch 'ACQE-8324' into ACQE-functional-deployment-v4-1
2 parents 1243b1b + 6bf3d68 commit facd914

File tree

1 file changed

+315
-0
lines changed

1 file changed

+315
-0
lines changed
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Quote\Api;
10+
11+
use Magento\Catalog\Api\Data\ProductInterface;
12+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
13+
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
14+
use Magento\ConfigurableProduct\Test\Fixture\Attribute as AttributeFixture;
15+
use Magento\ConfigurableProduct\Test\Fixture\Product as ConfigurableProductFixture;
16+
use Magento\Framework\Exception\NoSuchEntityException;
17+
use Magento\Framework\Webapi\Rest\Request;
18+
use Magento\TestFramework\Fixture\DataFixture;
19+
use Magento\TestFramework\Fixture\DataFixtureStorage;
20+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
21+
use Magento\TestFramework\Helper\Bootstrap;
22+
use Magento\TestFramework\TestCase\WebapiAbstract;
23+
24+
/**
25+
* Test for Magento\Quote\Api\GuestCartConfigurableItemRepositoryTest.
26+
*/
27+
class GuestCartConfigurableItemRepositoryTest extends WebapiAbstract
28+
{
29+
30+
private const RESOURCE_PATH_GUEST_CART = '/V1/guest-carts/';
31+
32+
private const SERVICE_VERSION_GUEST_CART = 'V1';
33+
34+
private const SERVICE_NAME_GUEST_CART = 'quoteGuestCartManagementV1';
35+
36+
private const SERVICE_NAME_GUEST_CART_ITEM = 'quoteGuestCartItemRepositoryV1';
37+
38+
/**
39+
* @var array
40+
*/
41+
private $simpleProductSkus = [];
42+
43+
/**
44+
* @var DataFixtureStorage
45+
*/
46+
private $fixtures;
47+
48+
/**
49+
* @inheritdoc
50+
*/
51+
protected function setUp(): void
52+
{
53+
parent::setUp();
54+
$this->fixtures = Bootstrap::getObjectManager()->get(DataFixtureStorageManager::class)->getStorage();
55+
}
56+
57+
/**
58+
* Test guest cart update with configurable item
59+
*
60+
* @return void
61+
* @throws NoSuchEntityException
62+
*/
63+
#[
64+
DataFixture(ProductFixture::class, ['price' => 10, 'sku' => 'simple-10'], as: 'p1'),
65+
DataFixture(ProductFixture::class, ['price' => 20, 'sku' => 'simple-20'], as: 'p2'),
66+
DataFixture(AttributeFixture::class, ['attribute_code' => 'test_configurable'], as: 'attr'),
67+
DataFixture(
68+
ConfigurableProductFixture::class,
69+
[
70+
'sku' => 'configurable',
71+
'name' => 'Configurable Product',
72+
'_options' => ['$attr$'],
73+
'_links' => ['$p1$', '$p2$']
74+
],
75+
'configurableProduct'
76+
)
77+
]
78+
public function testGuestCartUpdateConfigurableItem(): void
79+
{
80+
$guestCartId = $this->createGuestCart();
81+
$response = $this->addConfigurableProductToCart($guestCartId);
82+
$this->updateConfigurableProductInCart($guestCartId, $response['item_id']);
83+
$this->verifyCartItems($guestCartId, $response['item_id']);
84+
}
85+
86+
/**
87+
* Create a guest cart and return its ID.
88+
*
89+
* @return string
90+
*/
91+
private function createGuestCart(): string
92+
{
93+
$quoteId = $this->_webApiCall([
94+
'rest' => [
95+
'resourcePath' => self::RESOURCE_PATH_GUEST_CART,
96+
'httpMethod' => Request::HTTP_METHOD_POST
97+
],
98+
'soap' => [
99+
'service' => self::SERVICE_NAME_GUEST_CART,
100+
'serviceVersion' => self::SERVICE_VERSION_GUEST_CART,
101+
'operation' => self::SERVICE_NAME_GUEST_CART . 'CreateEmptyCart',
102+
],
103+
], ['storeId' => 1]);
104+
$this->assertTrue(strlen($quoteId) >= 32);
105+
106+
return $quoteId;
107+
}
108+
109+
/**
110+
* Add a configurable product to the guest cart.
111+
*
112+
* @param string $guestCartId
113+
* @return array
114+
* @throws NoSuchEntityException
115+
*/
116+
private function addConfigurableProductToCart(string $guestCartId): array
117+
{
118+
$configurableProduct = $this->getConfigurableProduct();
119+
$optionData = $this->getConfigurableOptionData($configurableProduct);
120+
$requestData = $this->buildCartItemRequestData(
121+
$guestCartId,
122+
$configurableProduct->getSku(),
123+
$optionData['attribute_id'],
124+
$optionData['option_id']
125+
);
126+
$serviceInfo = $this->getCartServiceInfo($guestCartId, 'add');
127+
$response = $this->_webApiCall($serviceInfo, $requestData);
128+
$this->assertNotNull($response['item_id']);
129+
$this->assertEquals(Configurable::TYPE_CODE, $response['product_type']);
130+
$this->assertEquals($requestData['cartItem']['qty'], $response['qty']);
131+
$this->assertContains($response['sku'], $this->simpleProductSkus);
132+
$this->assertEquals(
133+
$response['product_option']['extension_attributes']['configurable_item_options'][0],
134+
$requestData['cartItem']['product_option']['extension_attributes']['configurable_item_options'][0]
135+
);
136+
137+
return $response;
138+
}
139+
140+
/**
141+
* Verify that the cart contains the expected items.
142+
*
143+
* @param string $guestCartId
144+
* @param int $expectedItemId
145+
* @return void
146+
*/
147+
private function verifyCartItems(string $guestCartId, int $expectedItemId): void
148+
{
149+
$serviceInfo = $this->getCartServiceInfo($guestCartId, 'get');
150+
$response = $this->_webApiCall($serviceInfo, ['cartId' => $guestCartId]);
151+
$this->assertIsArray($response);
152+
$this->assertGreaterThan(0, count($response), 'Cart should contain at least one item');
153+
154+
$foundItem = false;
155+
foreach ($response as $item) {
156+
if ($item['item_id'] == $expectedItemId) {
157+
$foundItem = true;
158+
$this->assertEquals(Configurable::TYPE_CODE, $item['product_type']);
159+
$this->assertEquals(1, $item['qty']);
160+
$this->assertArrayHasKey('product_option', $item);
161+
$this->assertContains($item['sku'], $this->simpleProductSkus);
162+
$this->assertArrayHasKey('extension_attributes', $item['product_option']);
163+
$this->assertArrayHasKey(
164+
'configurable_item_options',
165+
$item['product_option']['extension_attributes']
166+
);
167+
break;
168+
}
169+
}
170+
171+
$this->assertTrue($foundItem, 'Expected cart item not found in cart items list');
172+
}
173+
174+
/**
175+
* Update a configurable product in the guest cart.
176+
*
177+
* @param string $guestCartId
178+
* @param int $itemId
179+
* @return void
180+
* @throws NoSuchEntityException
181+
*/
182+
private function updateConfigurableProductInCart(string $guestCartId, int $itemId): void
183+
{
184+
$configurableProduct = $this->getConfigurableProduct();
185+
$optionData = $this->getConfigurableOptionData($configurableProduct);
186+
$requestData = $this->buildCartItemRequestData(
187+
$guestCartId,
188+
$configurableProduct->getSku(),
189+
$optionData['attribute_id'],
190+
$optionData['option_id']
191+
);
192+
$requestData['cartItem']['item_id'] = $itemId;
193+
$serviceInfo = $this->getCartServiceInfo($guestCartId, 'update', $itemId);
194+
$response = $this->_webApiCall($serviceInfo, $requestData);
195+
$this->assertNotNull($response['item_id']);
196+
$this->assertEquals(Configurable::TYPE_CODE, $response['product_type']);
197+
$this->assertEquals($requestData['cartItem']['qty'], $response['qty']);
198+
$this->assertContains($response['sku'], $this->simpleProductSkus);
199+
$this->assertEquals(
200+
$response['product_option']['extension_attributes']['configurable_item_options'][0],
201+
$requestData['cartItem']['product_option']['extension_attributes']['configurable_item_options'][0]
202+
);
203+
}
204+
205+
/**
206+
* Get configurable product from fixtures
207+
*
208+
* @return ProductInterface
209+
* @throws NoSuchEntityException
210+
*/
211+
private function getConfigurableProduct(): ProductInterface
212+
{
213+
$configurableProduct = $this->fixtures->get('configurableProduct');
214+
$simpleProducts = $configurableProduct->getTypeInstance()->getUsedProducts($configurableProduct);
215+
foreach ($simpleProducts as $simpleProduct) {
216+
$this->simpleProductSkus[] = $simpleProduct->getSku();
217+
}
218+
219+
return $configurableProduct;
220+
}
221+
222+
/**
223+
* Get configurable option data for a configurable product.
224+
*
225+
* @param ProductInterface $configurableProduct
226+
* @return array
227+
*/
228+
private function getConfigurableOptionData(ProductInterface $configurableProduct): array
229+
{
230+
$configOptions = $configurableProduct->getExtensionAttributes()->getConfigurableProductOptions();
231+
$options = $configOptions[0]->getOptions();
232+
$optionKey = isset($options[null]) ? null : 0;
233+
234+
return [
235+
'attribute_id' => $configOptions[0]->getAttributeId(),
236+
'option_id' => $options[$optionKey]['value_index']
237+
];
238+
}
239+
240+
/**
241+
* Build the request data for adding or updating a cart item.
242+
*
243+
* @param string $cartId
244+
* @param string $sku
245+
* @param string $attributeId
246+
* @param string $optionId
247+
* @return array[]
248+
*/
249+
private function buildCartItemRequestData(
250+
string $cartId,
251+
string $sku,
252+
string $attributeId,
253+
string $optionId
254+
): array {
255+
return [
256+
'cartItem' => [
257+
'sku' => $sku,
258+
'qty' => 1,
259+
'quote_id' => $cartId,
260+
'product_option' => [
261+
'extension_attributes' => [
262+
'configurable_item_options' => [
263+
[
264+
'option_id' => $attributeId,
265+
'option_value' => $optionId,
266+
]
267+
]
268+
]
269+
]
270+
],
271+
];
272+
}
273+
274+
/**
275+
* Get the service information for the cart operations.
276+
*
277+
* @param string $cartId
278+
* @param string $action
279+
* @param int|null $itemId
280+
* @return array[]
281+
*/
282+
private function getCartServiceInfo(
283+
string $cartId,
284+
string $action = 'add',
285+
?int $itemId = null
286+
): array {
287+
$resourcePath = self::RESOURCE_PATH_GUEST_CART . $cartId . '/items';
288+
if ($action === 'update' && $itemId !== null) {
289+
$resourcePath .= '/' . $itemId;
290+
}
291+
$httpMethod = Request::HTTP_METHOD_POST;
292+
if ($action === 'update') {
293+
$httpMethod = Request::HTTP_METHOD_PUT;
294+
} elseif ($action === 'get') {
295+
$httpMethod = Request::HTTP_METHOD_GET;
296+
}
297+
$soapOperation = match ($action) {
298+
'get' => self::SERVICE_NAME_GUEST_CART_ITEM . 'GetList',
299+
'add', 'update' => self::SERVICE_NAME_GUEST_CART_ITEM . 'Save',
300+
default => self::SERVICE_NAME_GUEST_CART_ITEM . 'Save'
301+
};
302+
303+
return [
304+
'rest' => [
305+
'resourcePath' => $resourcePath,
306+
'httpMethod' => $httpMethod
307+
],
308+
'soap' => [
309+
'service' => self::SERVICE_NAME_GUEST_CART_ITEM,
310+
'serviceVersion' => self::SERVICE_VERSION_GUEST_CART,
311+
'operation' => $soapOperation,
312+
],
313+
];
314+
}
315+
}

0 commit comments

Comments
 (0)