Skip to content

Commit 4784fc2

Browse files
committed
api-functional test coverage
1 parent 169d751 commit 4784fc2

File tree

3 files changed

+354
-9
lines changed

3 files changed

+354
-9
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
namespace Magento\Catalog\Test\Fixture;
3+
4+
use Magento\CatalogInventory\Api\StockRegistryInterface;
5+
use Magento\Framework\DataObject;
6+
use Magento\Framework\DataObjectFactory;
7+
use Magento\TestFramework\Fixture\DataFixtureInterface;
8+
9+
class ProductStock implements DataFixtureInterface
10+
{
11+
private const DEFAULT_DATA = [
12+
'prod_id' => null,
13+
'prod_qty' => 1
14+
];
15+
16+
/**
17+
* @var DataObjectFactory
18+
*/
19+
protected DataObjectFactory $dataObjectFactory;
20+
21+
/**
22+
* @var StockRegistryInterface
23+
*/
24+
protected StockRegistryInterface $stockRegistry;
25+
26+
/**
27+
* @param DataObjectFactory $dataObjectFactory
28+
* @param StockRegistryInterface $stockRegistry
29+
*/
30+
public function __construct(
31+
DataObjectFactory $dataObjectFactory,
32+
StockRegistryInterface $stockRegistry
33+
) {
34+
$this->dataObjectFactory = $dataObjectFactory;
35+
$this->stockRegistry = $stockRegistry;
36+
}
37+
38+
/**
39+
* {@inheritdoc}
40+
* @param array $data Parameters. Same format as ReduceProductStock::DEFAULT_DATA
41+
*/
42+
public function apply(array $data = []): ?DataObject
43+
{
44+
$stockItem = $this->stockRegistry->getStockItem($data['prod_id']);
45+
$stockItem->setData('is_in_stock', 1);
46+
$stockItem->setData('qty', 90);
47+
$stockItem->setData('manage_stock', 1);
48+
$stockItem->save();
49+
50+
return $this->dataObjectFactory->create(['data' => [$data]]);
51+
}
52+
}

app/code/Magento/QuoteGraphQl/Model/Resolver/CheckAvailability.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class CheckAvailability implements ResolverInterface
3434
/**
3535
* Product type code
3636
*/
37-
private const PRODUCT_TYPE = "bundle";
37+
private const PRODUCT_TYPE_BUNDLE = "bundle";
3838

3939
/**
4040
* @var StockStatusRepositoryInterface
@@ -76,13 +76,13 @@ private function checkProductQtyStatus($cartItem):bool
7676
$requestedQty = 0;
7777
$previousQty = 0;
7878

79-
if ($cartItem->getProductType() == self::PRODUCT_TYPE) {
79+
if ($cartItem->getProductType() == self::PRODUCT_TYPE_BUNDLE) {
8080
$qtyOptions = $cartItem->getQtyOptions();
81-
$requestedQty = $cartItem->getQtyToAdd() ? $cartItem->getQtyToAdd() : $cartItem->getQty();
82-
$previousQty = $cartItem->getPreviousQty() ? $cartItem->getPreviousQty() : 0;
81+
$requestedQty = $cartItem->getQtyToAdd() ?? $cartItem->getQty();
82+
$previousQty = $cartItem->getPreviousQty() ?? 0;
8383
$totalReqQty = $previousQty + $requestedQty;
84-
foreach($qtyOptions as $qtyOption)
85-
{
84+
85+
foreach($qtyOptions as $qtyOption) {
8686
$productId = (int) $qtyOption->getProductId();
8787
$requiredItemQty = (float) $qtyOption->getValue();
8888
if ($totalReqQty) {
@@ -96,9 +96,9 @@ private function checkProductQtyStatus($cartItem):bool
9696
} else {
9797
foreach ($cartItem->getQuote()->getItems() as $item) {
9898

99-
if($item->getItemId() == $cartItem->getItemId() && $item->getQtyToAdd()) {
100-
$requestedQty = (float)$item->getQtyToAdd();
101-
$previousQty = $item->getPreviousQty() ? (float)$item->getPreviousQty() : 0;
99+
if ($item->getItemId() == $cartItem->getItemId()) {
100+
$requestedQty = $item->getQtyToAdd() ?? $item->getQty();
101+
$previousQty = $item->getPreviousQty() ?? 0;
102102
}
103103
}
104104
$requiredItemQty = $requestedQty + $previousQty;
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
<?php
2+
/**
3+
* Copyright 2023 Adobe
4+
* All Rights Reserved.
5+
*
6+
* NOTICE: All information contained herein is, and remains
7+
* the property of Adobe and its suppliers, if any. The intellectual
8+
* and technical concepts contained herein are proprietary to Adobe
9+
* and its suppliers and are protected by all applicable intellectual
10+
* property laws, including trade secret and copyright laws.
11+
* Dissemination of this information or reproduction of this material
12+
* is strictly forbidden unless prior written permission is obtained from
13+
* Adobe.
14+
*/
15+
declare(strict_types=1);
16+
17+
namespace Magento\GraphQl\Quote;
18+
19+
use Magento\Bundle\Test\Fixture\AddProductToCart as AddBundleProductToCart;
20+
use Magento\Bundle\Test\Fixture\Link as BundleSelectionFixture;
21+
use Magento\Bundle\Test\Fixture\Option as BundleOptionFixture;
22+
use Magento\Bundle\Test\Fixture\Product as BundleProductFixture;
23+
use Magento\Catalog\Api\ProductRepositoryInterface;
24+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
25+
use Magento\Catalog\Test\Fixture\ProductStock as ProductStockFixture;
26+
use Magento\ConfigurableProduct\Test\Fixture\AddProductToCart as AddConfigurableProductToCartFixture;
27+
use Magento\ConfigurableProduct\Test\Fixture\Attribute as AttributeFixture;
28+
use Magento\ConfigurableProduct\Test\Fixture\Product as ConfigurableProductFixture;
29+
use Magento\Customer\Test\Fixture\Customer;
30+
use Magento\Framework\DataObject;
31+
use Magento\Framework\ObjectManagerInterface;
32+
use Magento\Quote\Test\Fixture\AddProductToCart;
33+
use Magento\Quote\Test\Fixture\CustomerCart;
34+
use Magento\Quote\Test\Fixture\GuestCart as GuestCartFixture;
35+
use Magento\Quote\Test\Fixture\QuoteIdMask as QuoteMaskFixture;
36+
use Magento\SalesRule\Test\Fixture\ProductCondition as ProductConditionFixture;
37+
use Magento\TestFramework\Fixture\DataFixture;
38+
use Magento\TestFramework\Fixture\DataFixtureStorage;
39+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
40+
use Magento\TestFramework\Helper\Bootstrap;
41+
use Magento\TestFramework\TestCase\GraphQlAbstract;
42+
43+
/**
44+
* Test discount totals calculation model
45+
*/
46+
class StockStatusTest extends GraphQlAbstract
47+
{
48+
/**
49+
* @var ObjectManagerInterface
50+
*/
51+
private $objectManager;
52+
53+
/**
54+
* @var DataFixtureStorage
55+
*/
56+
private $fixtures;
57+
58+
/**
59+
* @var ProductRepositoryInterface|mixed
60+
*/
61+
private $productRepository;
62+
63+
/**
64+
* @inheritDoc
65+
*/
66+
protected function setUp(): void
67+
{
68+
parent::setUp();
69+
$this->objectManager = Bootstrap::getObjectManager();
70+
$this->fixtures = DataFixtureStorageManager::getStorage();
71+
$this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
72+
}
73+
74+
#[
75+
DataFixture(ProductFixture::class, ['price' => 100.00], as: 'product'),
76+
DataFixture(GuestCartFixture::class, as: 'cart'),
77+
DataFixture(AddProductToCart::class, ['cart_id' => '$cart.id$', 'product_id' => '$product.id$', 'qty' => 100]),
78+
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$cart.id$'], 'quoteIdMask'),
79+
DataFixture(ProductStockFixture::class, ['prod_id' => '$product.id$', 'prod_qty' => 90], 'prodStock')
80+
]
81+
public function testStockStatusUnavailableSimpleProduct(): void
82+
{
83+
$maskedQuoteId = $this->fixtures->get('quoteIdMask')->getMaskedId();
84+
$query = $this->getQuery($maskedQuoteId);
85+
$response = $this->graphQlMutation($query);
86+
$responseDataObject = new DataObject($response);
87+
88+
self::assertEquals('unavailable', $responseDataObject->getData('cart/items/0/status'));
89+
}
90+
91+
#[
92+
DataFixture(ProductFixture::class, ['sku' => 'spl-prod', 'price' => 100.00], as: 'product'),
93+
DataFixture(GuestCartFixture::class, as: 'cart'),
94+
DataFixture(AddProductToCart::class, ['cart_id' => '$cart.id$', 'product_id' => '$product.id$', 'qty' => 100]),
95+
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$cart.id$'], 'quoteIdMask')
96+
]
97+
public function testStockStatusUnavailableAddSimpleProduct(): void
98+
{
99+
$sku = 'spl-prod';
100+
$maskedQuoteId = $this->fixtures->get('quoteIdMask')->getMaskedId();
101+
$query = $this->mutationAddSimpleProduct($maskedQuoteId, $sku, 100);
102+
$response = $this->graphQlMutation($query);
103+
$responseDataObject = new DataObject($response);
104+
105+
self::assertEquals('unavailable', $responseDataObject->getData('addProductsToCart/cart/items/0/status'));
106+
}
107+
108+
#[
109+
DataFixture(ProductFixture::class, ['price' => 100.00], as: 'product'),
110+
DataFixture(BundleSelectionFixture::class, ['sku' => '$product.sku$', 'price' => 100, 'price_type' => 0], as:'link'),
111+
DataFixture(BundleOptionFixture::class, ['title' => 'Checkbox Options', 'type' => 'checkbox',
112+
'required' => 1,'product_links' => ['$link$']], 'option'),
113+
DataFixture(
114+
BundleProductFixture::class,
115+
['price' => 90, '_options' => ['$option$']],
116+
as:'bundleProduct'
117+
),
118+
DataFixture(GuestCartFixture::class, as: 'cart'),
119+
DataFixture(
120+
AddBundleProductToCart::class,
121+
[
122+
'cart_id' => '$cart.id$',
123+
'product_id' => '$bundleProduct.id$',
124+
'selections' => [['$product.id$']],
125+
'qty' => 100
126+
],
127+
),
128+
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$cart.id$'], 'quoteIdMask'),
129+
DataFixture(ProductStockFixture::class, ['prod_id' => '$product.id$', 'prod_qty' => 90], 'prodStock')
130+
]
131+
public function testStockStatusUnavailableBundleProduct(): void
132+
{
133+
$maskedQuoteId = $this->fixtures->get('quoteIdMask')->getMaskedId();
134+
$query = $this->getQuery($maskedQuoteId);
135+
$response = $this->graphQlMutation($query);
136+
$responseDataObject = new DataObject($response);
137+
138+
self::assertEquals('unavailable', $responseDataObject->getData('cart/items/0/status'));
139+
}
140+
141+
#[
142+
DataFixture(ProductFixture::class, ['price' => 100.00], as: 'product'),
143+
DataFixture(BundleSelectionFixture::class, ['sku' => '$product.sku$', 'price' => 100, 'price_type' => 0], as:'link'),
144+
DataFixture(BundleOptionFixture::class, ['title' => 'Checkbox Options', 'type' => 'checkbox',
145+
'required' => 1,'product_links' => ['$link$']], 'option'),
146+
DataFixture(
147+
BundleProductFixture::class,
148+
['sku' => 'bundle-prod1', 'price' => 90, '_options' => ['$option$']],
149+
as:'bundleProduct'
150+
),
151+
DataFixture(GuestCartFixture::class, as: 'cart'),
152+
DataFixture(
153+
AddBundleProductToCart::class,
154+
[
155+
'cart_id' => '$cart.id$',
156+
'product_id' => '$bundleProduct.id$',
157+
'selections' => [['$product.id$']],
158+
'qty' => 100
159+
],
160+
),
161+
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$cart.id$'], 'quoteIdMask')
162+
]
163+
public function testStockStatusUnavailableAddBundleProduct(): void
164+
{
165+
$sku = 'bundle-prod1';
166+
$product = $this->productRepository->get($sku);
167+
168+
/** @var $typeInstance \Magento\Bundle\Model\Product\Type */
169+
$typeInstance = $product->getTypeInstance();
170+
$typeInstance->setStoreFilter($product->getStoreId(), $product);
171+
/** @var $option \Magento\Bundle\Model\Option */
172+
$option = $typeInstance->getOptionsCollection($product)->getFirstItem();
173+
/** @var \Magento\Catalog\Model\Product $selection */
174+
$selection = $typeInstance->getSelectionsCollection([$option->getId()], $product)->getFirstItem();
175+
$optionId = $option->getId();
176+
$selectionId = $selection->getSelectionId();
177+
178+
$bundleOptionIdV2 = $this->generateBundleOptionIdV2((int) $optionId, (int) $selectionId, 1);
179+
$maskedQuoteId = $this->fixtures->get('quoteIdMask')->getMaskedId();
180+
181+
$query = $this->mutationAddBundleProduct($maskedQuoteId, $sku, $bundleOptionIdV2, 100);
182+
$response = $this->graphQlMutation($query);
183+
$responseDataObject = new DataObject($response);
184+
185+
self::assertEquals('unavailable', $responseDataObject->getData('addProductsToCart/cart/items/0/status'));
186+
}
187+
188+
#[
189+
DataFixture(ProductFixture::class, as: 'product'),
190+
DataFixture(AttributeFixture::class, as: 'attribute'),
191+
DataFixture(
192+
ConfigurableProductFixture::class,
193+
['_options' => ['$attribute$'], '_links' => ['$product$']],
194+
'configurable_product'
195+
),
196+
DataFixture(GuestCartFixture::class, as: 'cart'),
197+
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$cart.id$'], 'quoteIdMask'),
198+
DataFixture(
199+
AddConfigurableProductToCartFixture::class,
200+
[
201+
'cart_id' => '$cart.id$',
202+
'product_id' => '$configurable_product.id$',
203+
'child_product_id' => '$product.id$',
204+
'qty' => 100
205+
],
206+
),
207+
DataFixture(ProductStockFixture::class, ['prod_id' => '$product.id$', 'prod_qty' => 90], 'prodStock')
208+
]
209+
public function testStockStatusUnavailableConfigurableProduct(): void
210+
{
211+
$maskedQuoteId = $this->fixtures->get('quoteIdMask')->getMaskedId();
212+
$query = $this->getQuery($maskedQuoteId);
213+
$response = $this->graphQlMutation($query);
214+
$responseDataObject = new DataObject($response);
215+
216+
self::assertEquals('unavailable', $responseDataObject->getData('cart/items/0/status'));
217+
}
218+
219+
/**
220+
* @param string $cartId
221+
* @return string
222+
*/
223+
private function getQuery(string $cartId): string
224+
{
225+
return <<<QUERY
226+
{
227+
cart(cart_id:"{$cartId}"){
228+
items{
229+
status
230+
}
231+
}
232+
}
233+
234+
QUERY;
235+
}
236+
237+
private function mutationAddSimpleProduct(string $cartId, string $sku, int $qty): string
238+
{
239+
return <<<QUERY
240+
mutation {
241+
addProductsToCart(
242+
cartId: "{$cartId}",
243+
cartItems: [
244+
{
245+
sku: "{$sku}"
246+
quantity: $qty
247+
}]
248+
) {
249+
cart {
250+
items {
251+
status
252+
}
253+
}
254+
}
255+
}
256+
QUERY;
257+
}
258+
259+
private function mutationAddBundleProduct(string $cartId, string $sku, string $bundleOptionIdV2, int $qty): string
260+
{
261+
return <<<QUERY
262+
mutation {
263+
addProductsToCart(
264+
cartId: "{$cartId}",
265+
cartItems: [
266+
{
267+
sku: "{$sku}"
268+
quantity: $qty
269+
selected_options: [
270+
"{$bundleOptionIdV2}"
271+
]
272+
}]
273+
) {
274+
cart {
275+
items {
276+
status
277+
product {
278+
sku
279+
}
280+
}
281+
}
282+
}
283+
}
284+
QUERY;
285+
}
286+
287+
private function generateBundleOptionIdV2(int $optionId, int $selectionId, int $quantity): string
288+
{
289+
return base64_encode("bundle/$optionId/$selectionId/$quantity");
290+
}
291+
}
292+
293+

0 commit comments

Comments
 (0)