Skip to content

Commit 8ad6913

Browse files
Merge branch 'ACQE-8152' into ACQE-functional-deployment-v3-2
2 parents 57fe87c + a12cd1b commit 8ad6913

File tree

1 file changed

+224
-0
lines changed

1 file changed

+224
-0
lines changed
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Downloadable\Api;
10+
11+
use Magento\Downloadable\Test\Fixture\DownloadableProduct;
12+
use Magento\Framework\Webapi\Rest\Request;
13+
use Magento\TestFramework\TestCase\WebapiAbstract;
14+
use Magento\TestFramework\Fixture\DataFixture;
15+
use Magento\TestFramework\Fixture\DataFixtureStorage;
16+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
17+
18+
/**
19+
* Test to verify REST-API updating product stock_item does not delete downloadable_product_links
20+
*
21+
* @magentoAppIsolation enabled
22+
*/
23+
class StockItemUpdatePreservesLinksTest extends WebapiAbstract
24+
{
25+
private const PRODUCT_RESOURCE_PATH = '/V1/products';
26+
27+
/**
28+
* @var DataFixtureStorage
29+
*/
30+
private $fixtures;
31+
32+
/**
33+
* @inheritDoc
34+
*/
35+
protected function setUp(): void
36+
{
37+
parent::setUp();
38+
$this->_markTestAsRestOnly();
39+
$this->fixtures = DataFixtureStorageManager::getStorage();
40+
}
41+
42+
/**
43+
* Test the complete workflow
44+
* Verify that REST-API updating product stock_item does not delete downloadable_product_links
45+
*
46+
* @return void
47+
*/
48+
#[
49+
DataFixture(DownloadableProduct::class, [
50+
'name' => 'Downloadable Product Test',
51+
'price' => 50.00,
52+
'type_id' => 'downloadable',
53+
'links_purchased_separately' => 1,
54+
'links_title' => 'Downloadable Links',
55+
'extension_attributes' => [
56+
'website_ids' => [1],
57+
'stock_item' => [
58+
'use_config_manage_stock' => true,
59+
'qty' => 100,
60+
'is_qty_decimal' => false,
61+
'is_in_stock' => true,
62+
],
63+
'downloadable_product_links' => [
64+
[
65+
'title' => 'Downloadable Product Link',
66+
'price' => 10.00,
67+
'link_type' => 'url',
68+
'is_shareable' => 0,
69+
'number_of_downloads' => 5,
70+
'sort_order' => 1
71+
],
72+
[
73+
'title' => 'Another Link',
74+
'price' => 15.00,
75+
'link_type' => 'file',
76+
'link_file' => 'test-file.txt',
77+
'is_shareable' => 1,
78+
'number_of_downloads' => 10,
79+
'sort_order' => 2
80+
]
81+
]
82+
]
83+
], 'downloadable_product')]
84+
public function testStockItemUpdatePreservesDownloadableLinks(): void
85+
{
86+
// Get the product SKU from the fixture
87+
$productSku = $this->fixtures->get('downloadable_product')->getSku();
88+
89+
// Get original product and verify it has downloadable links
90+
$originalProduct = $this->getProductBySku($productSku);
91+
$this->verifyProductHasDownloadableLinks($originalProduct, 'Original product should have downloadable links');
92+
$originalLinks = $originalProduct['extension_attributes']['downloadable_product_links'];
93+
94+
// Update product stock_item via catalogProductRepositoryV1 PUT endpoint
95+
$updatedProduct = $this->updateProductStockItem($productSku);
96+
97+
// Verify the API call was successful
98+
$this->assertNotEmpty($updatedProduct, 'API response should not be empty');
99+
$this->assertEquals($productSku, $updatedProduct['sku']);
100+
$this->assertEquals('99.99', $updatedProduct['price']);
101+
$this->assertEquals('1', $updatedProduct['status']);
102+
103+
// Verify downloadable product links are preserved
104+
$this->verifyDownloadableLinksPreserved($originalLinks, $productSku);
105+
}
106+
107+
/**
108+
* Update Product Stock Item
109+
*
110+
* @param string $productSku
111+
* @return array
112+
*/
113+
private function updateProductStockItem(string $productSku): array
114+
{
115+
$serviceInfo = [
116+
'rest' => [
117+
'resourcePath' => self::PRODUCT_RESOURCE_PATH . '/' . $productSku,
118+
'httpMethod' => Request::HTTP_METHOD_PUT,
119+
],
120+
'soap' => [
121+
'service' => 'catalogProductRepositoryV1',
122+
'serviceVersion' => 'V1',
123+
'operation' => 'catalogProductRepositoryV1Save',
124+
],
125+
];
126+
127+
$productData = [
128+
'product' => [
129+
'sku' => $productSku,
130+
'status' => '1',
131+
'price' => '99.99',
132+
'type_id' => 'downloadable',
133+
'extension_attributes' => [
134+
'stock_item' => [
135+
'qty' => 1
136+
]
137+
]
138+
]
139+
];
140+
141+
return $this->_webApiCall($serviceInfo, $productData);
142+
}
143+
144+
/**
145+
* Verify Downloadable Links are Preserved
146+
*
147+
* @param array $originalLinks
148+
* @param string $productSku
149+
* @return void
150+
*/
151+
private function verifyDownloadableLinksPreserved(array $originalLinks, string $productSku): void
152+
{
153+
$updatedProduct = $this->getProductBySku($productSku);
154+
$this->verifyProductHasDownloadableLinks($updatedProduct, 'Updated product should preserve downloadable links');
155+
156+
$preservedLinks = $updatedProduct['extension_attributes']['downloadable_product_links'];
157+
158+
$this->assertCount(
159+
count($originalLinks),
160+
$preservedLinks,
161+
'Number of downloadable links should be preserved after stock_item update'
162+
);
163+
164+
foreach ($preservedLinks as $link) {
165+
$this->assertArrayHasKey('id', $link, 'Link should have an ID');
166+
$this->assertArrayHasKey('title', $link, 'Link should have a title');
167+
$this->assertArrayHasKey('price', $link, 'Link should have a price');
168+
$this->assertArrayHasKey('sort_order', $link, 'Link should have a sort order');
169+
}
170+
171+
$this->assertGreaterThan(0, count($preservedLinks), 'Should have at least one downloadable link preserved');
172+
173+
$linkTitles = array_column($preservedLinks, 'title');
174+
$this->assertContains(
175+
'Downloadable Product Link',
176+
$linkTitles,
177+
'Downloadable product link should be preserved'
178+
);
179+
}
180+
181+
/**
182+
* Verify product has downloadable links
183+
*
184+
* @param array $product
185+
* @param string $message
186+
* @return void
187+
*/
188+
private function verifyProductHasDownloadableLinks(array $product, string $message): void
189+
{
190+
$this->assertArrayHasKey('extension_attributes', $product, $message . ' - missing extension_attributes');
191+
$this->assertArrayHasKey(
192+
'downloadable_product_links',
193+
$product['extension_attributes'],
194+
$message . ' - missing downloadable_product_links'
195+
);
196+
$this->assertNotEmpty(
197+
$product['extension_attributes']['downloadable_product_links'],
198+
$message . ' - downloadable_product_links should not be empty'
199+
);
200+
}
201+
202+
/**
203+
* Get product by SKU
204+
*
205+
* @param string $sku
206+
* @return array
207+
*/
208+
private function getProductBySku(string $sku): array
209+
{
210+
$serviceInfo = [
211+
'rest' => [
212+
'resourcePath' => self::PRODUCT_RESOURCE_PATH . '/' . $sku,
213+
'httpMethod' => Request::HTTP_METHOD_GET,
214+
],
215+
'soap' => [
216+
'service' => 'catalogProductRepositoryV1',
217+
'serviceVersion' => 'V1',
218+
'operation' => 'catalogProductRepositoryV1Get',
219+
],
220+
];
221+
222+
return $this->_webApiCall($serviceInfo, ['sku' => $sku]);
223+
}
224+
}

0 commit comments

Comments
 (0)