Skip to content

Commit 5e84f7b

Browse files
Merge branch '2.4-develop' into PR_2025_11_07_muntianu
2 parents 73a5b76 + a06a4a5 commit 5e84f7b

File tree

29 files changed

+4127
-28
lines changed

29 files changed

+4127
-28
lines changed

app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Edit.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,15 @@ public function __construct(
5555
public function execute()
5656
{
5757
$this->_setTypeId();
58-
$attributeSet = $this->attributeSetRepository->get($this->getRequest()->getParam('id'));
58+
59+
try {
60+
$attributeSetId = $this->getRequest()->getParam('id');
61+
$attributeSet = $this->attributeSetRepository->get($attributeSetId);
62+
} catch (NoSuchEntityException $e) {
63+
$this->messageManager->addErrorMessage(__('Attribute set %1 does not exist.', $attributeSetId));
64+
return $this->resultRedirectFactory->create()->setPath('catalog/*/index');
65+
}
66+
5967
if (!$attributeSet->getId()) {
6068
return $this->resultRedirectFactory->create()->setPath('catalog/*/index');
6169
}

app/code/Magento/Catalog/Model/Attribute/ScopeOverriddenValue.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ public function containsValue($entityType, $entity, $attributeCode, $storeId)
105105
* @return array
106106
*
107107
* @deprecated 101.0.0
108+
* @see MAGETWO-71174
108109
*/
109110
public function getDefaultValues($entityType, $entity)
110111
{
@@ -157,6 +158,10 @@ private function initAttributeValues($entityType, $entity, $storeId)
157158
$selects[] = $select;
158159
}
159160

161+
if (empty($selects)) {
162+
return;
163+
}
164+
160165
$unionSelect = new \Magento\Framework\DB\Sql\UnionExpression(
161166
$selects,
162167
\Magento\Framework\DB\Select::SQL_UNION_ALL
Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Test\Unit\Controller\Adminhtml\Product\Set;
9+
10+
use Magento\Backend\App\Action\Context;
11+
use Magento\Backend\Model\View\Result\Page as ResultPage;
12+
use Magento\Backend\Model\View\Result\Redirect as ResultRedirect;
13+
use Magento\Catalog\Controller\Adminhtml\Product\Set\Edit;
14+
use Magento\Catalog\Model\Product;
15+
use Magento\Catalog\Model\ResourceModel\Product as ProductResource;
16+
use Magento\Eav\Api\AttributeSetRepositoryInterface;
17+
use Magento\Eav\Api\Data\AttributeSetInterface;
18+
use Magento\Framework\App\RequestInterface;
19+
use Magento\Framework\Controller\Result\RedirectFactory;
20+
use Magento\Framework\Exception\NoSuchEntityException;
21+
use Magento\Framework\Message\ManagerInterface;
22+
use Magento\Framework\ObjectManagerInterface;
23+
use Magento\Framework\Registry;
24+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
25+
use Magento\Framework\View\Page\Config as PageConfig;
26+
use Magento\Framework\View\Page\Title;
27+
use Magento\Framework\View\Result\PageFactory;
28+
use PHPUnit\Framework\MockObject\MockObject;
29+
use PHPUnit\Framework\TestCase;
30+
31+
/**
32+
* Unit test for Edit controller
33+
*
34+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
35+
*/
36+
class EditTest extends TestCase
37+
{
38+
/**
39+
* @var Edit
40+
*/
41+
private $controller;
42+
43+
/**
44+
* @var Context|MockObject
45+
*/
46+
private $contextMock;
47+
48+
/**
49+
* @var Registry|MockObject
50+
*/
51+
private $registryMock;
52+
53+
/**
54+
* @var PageFactory|MockObject
55+
*/
56+
private $resultPageFactoryMock;
57+
58+
/**
59+
* @var AttributeSetRepositoryInterface|MockObject
60+
*/
61+
private $attributeSetRepositoryMock;
62+
63+
/**
64+
* @var RequestInterface|MockObject
65+
*/
66+
private $requestMock;
67+
68+
/**
69+
* @var ManagerInterface|MockObject
70+
*/
71+
private $messageManagerMock;
72+
73+
/**
74+
* @var RedirectFactory|MockObject
75+
*/
76+
private $resultRedirectFactoryMock;
77+
78+
/**
79+
* @var ObjectManagerInterface|MockObject
80+
*/
81+
private $objectManagerMock;
82+
83+
/**
84+
* @var ObjectManager
85+
*/
86+
private $objectManager;
87+
88+
/**
89+
* @inheritdoc
90+
*/
91+
protected function setUp(): void
92+
{
93+
$this->objectManager = new ObjectManager($this);
94+
95+
$this->contextMock = $this->createMock(Context::class);
96+
$this->registryMock = $this->createMock(Registry::class);
97+
$this->resultPageFactoryMock = $this->createMock(PageFactory::class);
98+
$this->attributeSetRepositoryMock = $this->getMockForAbstractClass(AttributeSetRepositoryInterface::class);
99+
$this->requestMock = $this->getMockForAbstractClass(RequestInterface::class);
100+
$this->messageManagerMock = $this->getMockForAbstractClass(ManagerInterface::class);
101+
$this->resultRedirectFactoryMock = $this->createMock(RedirectFactory::class);
102+
$this->objectManagerMock = $this->getMockForAbstractClass(ObjectManagerInterface::class);
103+
104+
// Setup Product and ProductResource mocks for _setTypeId()
105+
$productResourceMock = $this->createMock(ProductResource::class);
106+
$productResourceMock->expects($this->any())
107+
->method('getTypeId')
108+
->willReturn(4);
109+
110+
$productMock = $this->createMock(Product::class);
111+
$productMock->expects($this->any())
112+
->method('getResource')
113+
->willReturn($productResourceMock);
114+
115+
$this->objectManagerMock->expects($this->any())
116+
->method('create')
117+
->with(Product::class)
118+
->willReturn($productMock);
119+
120+
$this->contextMock->expects($this->any())
121+
->method('getRequest')
122+
->willReturn($this->requestMock);
123+
$this->contextMock->expects($this->any())
124+
->method('getMessageManager')
125+
->willReturn($this->messageManagerMock);
126+
$this->contextMock->expects($this->any())
127+
->method('getResultRedirectFactory')
128+
->willReturn($this->resultRedirectFactoryMock);
129+
$this->contextMock->expects($this->any())
130+
->method('getObjectManager')
131+
->willReturn($this->objectManagerMock);
132+
133+
$this->controller = $this->objectManager->getObject(
134+
Edit::class,
135+
[
136+
'context' => $this->contextMock,
137+
'coreRegistry' => $this->registryMock,
138+
'resultPageFactory' => $this->resultPageFactoryMock,
139+
'attributeSetRepository' => $this->attributeSetRepositoryMock
140+
]
141+
);
142+
}
143+
144+
/**
145+
* Test constructor with null attribute set repository (uses ObjectManager fallback)
146+
*
147+
* @return void
148+
*/
149+
public function testConstructorWithNullAttributeSetRepository(): void
150+
{
151+
// Create a mock for ObjectManager singleton
152+
$objectManagerMock = $this->getMockForAbstractClass(ObjectManagerInterface::class);
153+
$attributeSetRepositoryMock = $this->getMockForAbstractClass(AttributeSetRepositoryInterface::class);
154+
155+
$objectManagerMock->expects($this->once())
156+
->method('get')
157+
->with(AttributeSetRepositoryInterface::class)
158+
->willReturn($attributeSetRepositoryMock);
159+
160+
// Set ObjectManager singleton for the test
161+
\Magento\Framework\App\ObjectManager::setInstance($objectManagerMock);
162+
163+
// Create controller without attributeSetRepository parameter (null)
164+
// This will trigger the fallback: ObjectManager::getInstance()->get()
165+
$controller = new Edit(
166+
$this->contextMock,
167+
$this->registryMock,
168+
$this->resultPageFactoryMock,
169+
null // This triggers the ObjectManager fallback
170+
);
171+
172+
$this->assertInstanceOf(Edit::class, $controller);
173+
}
174+
175+
/**
176+
* Test execute method with valid attribute set ID
177+
*
178+
* @return void
179+
*/
180+
public function testExecuteWithValidAttributeSetId(): void
181+
{
182+
$attributeSetId = 18;
183+
$attributeSetName = 'Test Attribute Set';
184+
185+
// Mock attribute set
186+
$attributeSetMock = $this->getMockBuilder(AttributeSetInterface::class)
187+
->onlyMethods(['getAttributeSetName'])
188+
->addMethods(['getId'])
189+
->getMockForAbstractClass();
190+
$attributeSetMock->expects($this->any())
191+
->method('getId')
192+
->willReturn($attributeSetId);
193+
$attributeSetMock->expects($this->any())
194+
->method('getAttributeSetName')
195+
->willReturn($attributeSetName);
196+
197+
// Mock request
198+
$this->requestMock->expects($this->once())
199+
->method('getParam')
200+
->with('id')
201+
->willReturn($attributeSetId);
202+
203+
// Mock repository
204+
$this->attributeSetRepositoryMock->expects($this->once())
205+
->method('get')
206+
->with($attributeSetId)
207+
->willReturn($attributeSetMock);
208+
209+
// Mock registry - both for _setTypeId() and attribute set registration
210+
$this->registryMock->expects($this->exactly(2))
211+
->method('register')
212+
->willReturnCallback(function ($key, $value) use ($attributeSetMock) {
213+
if ($key === 'entityType') {
214+
return null;
215+
} elseif ($key === 'current_attribute_set' && $value === $attributeSetMock) {
216+
return null;
217+
}
218+
});
219+
220+
// Mock page result
221+
$resultPageMock = $this->createMock(ResultPage::class);
222+
$pageConfigMock = $this->createMock(PageConfig::class);
223+
$pageTitleMock = $this->createMock(Title::class);
224+
225+
$resultPageMock->expects($this->once())
226+
->method('setActiveMenu')
227+
->with('Magento_Catalog::catalog_attributes_sets')
228+
->willReturnSelf();
229+
$resultPageMock->expects($this->any())
230+
->method('getConfig')
231+
->willReturn($pageConfigMock);
232+
$pageConfigMock->expects($this->any())
233+
->method('getTitle')
234+
->willReturn($pageTitleMock);
235+
$pageTitleMock->expects($this->exactly(2))
236+
->method('prepend')
237+
->willReturnSelf();
238+
$resultPageMock->expects($this->exactly(2))
239+
->method('addBreadcrumb')
240+
->willReturnSelf();
241+
242+
$this->resultPageFactoryMock->expects($this->once())
243+
->method('create')
244+
->willReturn($resultPageMock);
245+
246+
$result = $this->controller->execute();
247+
$this->assertSame($resultPageMock, $result);
248+
}
249+
250+
/**
251+
* Test execute method with non-existing attribute set ID
252+
*
253+
* @return void
254+
*/
255+
public function testExecuteWithNonExistingAttributeSetId(): void
256+
{
257+
$attributeSetId = 999999;
258+
259+
// Mock request
260+
$this->requestMock->expects($this->once())
261+
->method('getParam')
262+
->with('id')
263+
->willReturn($attributeSetId);
264+
265+
// Mock repository to throw exception
266+
$this->attributeSetRepositoryMock->expects($this->once())
267+
->method('get')
268+
->with($attributeSetId)
269+
->willThrowException(new NoSuchEntityException(__('No such entity!')));
270+
271+
// Mock message manager
272+
$this->messageManagerMock->expects($this->once())
273+
->method('addErrorMessage')
274+
->with(__('Attribute set %1 does not exist.', $attributeSetId));
275+
276+
// Mock redirect result
277+
$resultRedirectMock = $this->createMock(ResultRedirect::class);
278+
$resultRedirectMock->expects($this->once())
279+
->method('setPath')
280+
->with('catalog/*/index')
281+
->willReturnSelf();
282+
283+
$this->resultRedirectFactoryMock->expects($this->once())
284+
->method('create')
285+
->willReturn($resultRedirectMock);
286+
287+
// Registry should only be called for entityType, not for current_attribute_set
288+
$this->registryMock->expects($this->once())
289+
->method('register')
290+
->with('entityType', $this->anything());
291+
292+
$result = $this->controller->execute();
293+
$this->assertSame($resultRedirectMock, $result);
294+
}
295+
296+
/**
297+
* Test execute method when attribute set has no ID after loading
298+
*
299+
* @return void
300+
*/
301+
public function testExecuteWithInvalidAttributeSet(): void
302+
{
303+
$attributeSetId = 18;
304+
305+
// Mock attribute set with no ID (invalid)
306+
$attributeSetMock = $this->getMockBuilder(AttributeSetInterface::class)
307+
->addMethods(['getId'])
308+
->getMockForAbstractClass();
309+
$attributeSetMock->expects($this->any())
310+
->method('getId')
311+
->willReturn(null);
312+
313+
// Mock request
314+
$this->requestMock->expects($this->once())
315+
->method('getParam')
316+
->with('id')
317+
->willReturn($attributeSetId);
318+
319+
// Mock repository
320+
$this->attributeSetRepositoryMock->expects($this->once())
321+
->method('get')
322+
->with($attributeSetId)
323+
->willReturn($attributeSetMock);
324+
325+
// Mock redirect result
326+
$resultRedirectMock = $this->createMock(ResultRedirect::class);
327+
$resultRedirectMock->expects($this->once())
328+
->method('setPath')
329+
->with('catalog/*/index')
330+
->willReturnSelf();
331+
332+
$this->resultRedirectFactoryMock->expects($this->once())
333+
->method('create')
334+
->willReturn($resultRedirectMock);
335+
336+
// Registry should only be called for entityType, not for current_attribute_set
337+
$this->registryMock->expects($this->once())
338+
->method('register')
339+
->with('entityType', $this->anything());
340+
341+
$result = $this->controller->execute();
342+
$this->assertSame($resultRedirectMock, $result);
343+
}
344+
}

0 commit comments

Comments
 (0)