Skip to content

Commit 09c2a3f

Browse files
committed
ACP2E-4294: Restricted Category Products Still Counted in Wishlist After Customer Group Update
1 parent a1aa2b6 commit 09c2a3f

File tree

5 files changed

+469
-19
lines changed

5 files changed

+469
-19
lines changed

app/code/Magento/Wishlist/Block/Customer/Wishlist.php

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,9 @@
33
* Copyright 2013 Adobe
44
* All Rights Reserved.
55
*/
6-
declare(strict_types=1);
76

87
namespace Magento\Wishlist\Block\Customer;
98

10-
use Magento\Catalog\Block\Product\Context;
11-
use Magento\Catalog\Helper\Product\ConfigurationPool;
12-
use Magento\Customer\Helper\Session\CurrentCustomer;
13-
use Magento\Framework\Data\Helper\PostHelper;
14-
159
/**
1610
* Wishlist block customer items.
1711
*
@@ -49,20 +43,20 @@ class Wishlist extends \Magento\Wishlist\Block\AbstractBlock
4943
protected $postDataHelper;
5044

5145
/**
52-
* @param Context $context
46+
* @param \Magento\Catalog\Block\Product\Context $context
5347
* @param \Magento\Framework\App\Http\Context $httpContext
54-
* @param ConfigurationPool $helperPool
55-
* @param CurrentCustomer $currentCustomer
56-
* @param PostHelper $postDataHelper
48+
* @param \Magento\Catalog\Helper\Product\ConfigurationPool $helperPool
49+
* @param \Magento\Customer\Helper\Session\CurrentCustomer $currentCustomer
50+
* @param \Magento\Framework\Data\Helper\PostHelper $postDataHelper
5751
* @param array $data
5852
*/
5953
public function __construct(
60-
\Magento\Catalog\Block\Product\Context $context,
61-
\Magento\Framework\App\Http\Context $httpContext,
54+
\Magento\Catalog\Block\Product\Context $context,
55+
\Magento\Framework\App\Http\Context $httpContext,
6256
\Magento\Catalog\Helper\Product\ConfigurationPool $helperPool,
63-
\Magento\Customer\Helper\Session\CurrentCustomer $currentCustomer,
64-
\Magento\Framework\Data\Helper\PostHelper $postDataHelper,
65-
array $data = []
57+
\Magento\Customer\Helper\Session\CurrentCustomer $currentCustomer,
58+
\Magento\Framework\Data\Helper\PostHelper $postDataHelper,
59+
array $data = []
6660
) {
6761
parent::__construct(
6862
$context,
@@ -79,12 +73,10 @@ public function __construct(
7973
*
8074
* @param \Magento\Wishlist\Model\ResourceModel\Item\Collection $collection
8175
* @return $this
82-
* @throws \Exception
8376
*/
8477
protected function _prepareCollection($collection)
8578
{
86-
$collection->setInStockFilter()->setOrder('added_at', 'ASC');
87-
79+
$collection->setInStockFilter(true)->setOrder('added_at', 'ASC');
8880
return $this;
8981
}
9082

app/code/Magento/Wishlist/Model/WishlistItemPermissionsCollectionProcessor.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public function execute(Collection $collection): Collection
5656
);
5757

5858
$validItems = [];
59+
/** @var Item $item */
5960
foreach ($items as $item) {
6061
if (!isset($products[$item->getProductId()]) || $products[$item->getProductId()]->getIsHidden()) {
6162
continue;
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Wishlist\Test\Unit\Model;
9+
10+
use Magento\Catalog\Api\Data\ProductInterface;
11+
use Magento\Catalog\Api\ProductRepositoryInterface;
12+
use Magento\Framework\Api\SearchCriteria;
13+
use Magento\Framework\Api\SearchCriteriaBuilder;
14+
use Magento\Framework\Api\SearchResultsInterface;
15+
use Magento\Wishlist\Model\ResourceModel\Item\Collection;
16+
use Magento\Wishlist\Model\WishlistItemPermissionsCollectionProcessor;
17+
use PHPUnit\Framework\MockObject\MockObject;
18+
use PHPUnit\Framework\TestCase;
19+
20+
class WishlistItemPermissionsCollectionProcessorTest extends TestCase
21+
{
22+
/** @var ProductRepositoryInterface|MockObject */
23+
private $productRepository;
24+
25+
/** @var SearchCriteriaBuilder|MockObject */
26+
private $searchCriteriaBuilder;
27+
28+
/**
29+
* @var WishlistItemPermissionsCollectionProcessor
30+
*/
31+
private WishlistItemPermissionsCollectionProcessor $processor;
32+
33+
/**
34+
* @inheritDoc
35+
*/
36+
protected function setUp(): void
37+
{
38+
$this->productRepository = $this->createMock(ProductRepositoryInterface::class);
39+
$this->searchCriteriaBuilder = $this->createMock(SearchCriteriaBuilder::class);
40+
41+
$this->processor = new WishlistItemPermissionsCollectionProcessor(
42+
$this->productRepository,
43+
$this->searchCriteriaBuilder
44+
);
45+
parent::setUp();
46+
}
47+
48+
/**
49+
* @return void
50+
* @throws \Exception
51+
*/
52+
public function testExecuteReturnsCollectionWhenEmpty(): void
53+
{
54+
$collection = $this->getMockBuilder(Collection::class)
55+
->disableOriginalConstructor()
56+
->onlyMethods(['getItems'])
57+
->getMock();
58+
59+
$collection->expects($this->once())
60+
->method('getItems')
61+
->willReturn([]);
62+
63+
$this->productRepository->expects($this->never())->method('getList');
64+
$this->searchCriteriaBuilder->expects($this->never())->method('addFilter');
65+
66+
$result = $this->processor->execute($collection);
67+
68+
$this->assertSame($collection, $result);
69+
}
70+
71+
/**
72+
* @return void
73+
* @throws \PHPUnit\Framework\MockObject\Exception
74+
*/
75+
public function testExecuteFiltersCollectionWithValidProducts(): void
76+
{
77+
$wishlistItem1 = $this->getMockBuilder(\Magento\Wishlist\Model\Item::class)
78+
->disableOriginalConstructor()
79+
->addMethods(['getProductId'])
80+
->getMock();
81+
$wishlistItem1->method('getProductId')->willReturn(10);
82+
83+
$wishlistItem2 = $this->getMockBuilder(\Magento\Wishlist\Model\Item::class)
84+
->disableOriginalConstructor()
85+
->addMethods(['getProductId'])
86+
->getMock();
87+
$wishlistItem2->method('getProductId')->willReturn(11);
88+
89+
$collection = $this->getMockBuilder(Collection::class)
90+
->disableOriginalConstructor()
91+
->onlyMethods(['getItems', 'getColumnValues', 'addFieldToFilter'])
92+
->getMock();
93+
94+
/** @var Collection|MockObject $clonedCollection */
95+
$clonedCollection = clone $collection;
96+
97+
$collection->expects($this->once())
98+
->method('getItems')
99+
->willReturn([$wishlistItem1, $wishlistItem2]);
100+
101+
$collection->expects($this->once())
102+
->method('getColumnValues')
103+
->with('product_id')
104+
->willReturn([10, 11]);
105+
106+
$clonedCollection->expects($this->once())
107+
->method('addFieldToFilter')
108+
->with(
109+
'main_table.product_id',
110+
['in' => [10]]
111+
)
112+
->willReturnSelf();
113+
114+
$searchCriteria = $this->createMock(SearchCriteria::class);
115+
116+
$this->searchCriteriaBuilder->expects($this->once())
117+
->method('addFilter')
118+
->with('entity_id', [10, 11], 'in')
119+
->willReturnSelf();
120+
121+
$this->searchCriteriaBuilder->expects($this->once())
122+
->method('create')
123+
->willReturn($searchCriteria);
124+
125+
$product10 = $this->getMockBuilder(ProductInterface::class)
126+
->disableOriginalConstructor()
127+
->addMethods(['getIsHidden'])
128+
->getMockForAbstractClass();
129+
$product10->method('getId')->willReturn(10);
130+
$product10->method('getIsHidden')->willReturn(false);
131+
132+
$product11 = $this->getMockBuilder(ProductInterface::class)
133+
->disableOriginalConstructor()
134+
->addMethods(['getIsHidden'])
135+
->getMockForAbstractClass();
136+
$product11->method('getId')->willReturn(11);
137+
$product11->method('getIsHidden')->willReturn(true);
138+
139+
$searchResults = $this->createMock(SearchResultsInterface::class);
140+
$searchResults->method('getItems')->willReturn([$product10, $product11]);
141+
142+
$this->productRepository->expects($this->once())
143+
->method('getList')
144+
->with($searchCriteria)
145+
->willReturn($searchResults);
146+
147+
$result = $this->processor->execute($collection);
148+
149+
$this->assertNotSame($clonedCollection, $result);
150+
}
151+
152+
/**
153+
* @return void
154+
* @throws \PHPUnit\Framework\MockObject\Exception
155+
*/
156+
public function testExecuteUsesCacheOnSecondCall(): void
157+
{
158+
$wishlistItem1 = $this->getMockBuilder(\Magento\Wishlist\Model\Item::class)
159+
->disableOriginalConstructor()
160+
->addMethods(['getProductId'])
161+
->getMock();
162+
$wishlistItem1->method('getProductId')->willReturn(10);
163+
164+
$wishlistItem2 = $this->getMockBuilder(\Magento\Wishlist\Model\Item::class)
165+
->disableOriginalConstructor()
166+
->addMethods(['getProductId'])
167+
->getMock();
168+
$wishlistItem2->method('getProductId')->willReturn(11);
169+
170+
/** @var Collection|MockObject $collection1 */
171+
$collection1 = $this->getMockBuilder(Collection::class)
172+
->disableOriginalConstructor()
173+
->onlyMethods(['getItems', 'getColumnValues', 'addFieldToFilter'])
174+
->getMock();
175+
176+
/** @var Collection|MockObject $cloned1 */
177+
$cloned1 = clone $collection1;
178+
179+
$collection1->expects($this->once())
180+
->method('getItems')
181+
->willReturn([$wishlistItem1, $wishlistItem2]);
182+
183+
$collection1->expects($this->once())
184+
->method('getColumnValues')
185+
->with('product_id')
186+
->willReturn([21, 22]);
187+
188+
$cloned1->expects($this->once())
189+
->method('addFieldToFilter')
190+
->with('main_table.product_id', ['in' => [21, 22]])
191+
->willReturnSelf();
192+
193+
$searchCriteria = $this->createMock(SearchCriteria::class);
194+
195+
$this->searchCriteriaBuilder->expects($this->once())
196+
->method('addFilter')
197+
->with('entity_id', [21, 22], 'in')
198+
->willReturnSelf();
199+
200+
$this->searchCriteriaBuilder->expects($this->once())
201+
->method('create')
202+
->willReturn($searchCriteria);
203+
204+
$product21 = $this->getMockBuilder(ProductInterface::class)
205+
->disableOriginalConstructor()
206+
->addMethods(['getIsHidden'])
207+
->getMockForAbstractClass();
208+
$product21->method('getId')->willReturn(21);
209+
$product21->method('getIsHidden')->willReturn(false);
210+
211+
$product22 = $this->getMockBuilder(ProductInterface::class)
212+
->disableOriginalConstructor()
213+
->addMethods(['getIsHidden'])
214+
->getMockForAbstractClass();
215+
$product22->method('getId')->willReturn(22);
216+
$product22->method('getIsHidden')->willReturn(false);
217+
218+
$searchResults = $this->createMock(SearchResultsInterface::class);
219+
$searchResults->method('getItems')->willReturn([$product21, $product22]);
220+
221+
$this->productRepository->expects($this->once())
222+
->method('getList')
223+
->with($searchCriteria)
224+
->willReturn($searchResults);
225+
226+
$this->processor->execute($collection1);
227+
228+
$collection2 = $this->getMockBuilder(Collection::class)
229+
->disableOriginalConstructor()
230+
->onlyMethods(['getItems', 'getColumnValues', 'addFieldToFilter'])
231+
->getMock();
232+
233+
/** @var Collection|MockObject $cloned2 */
234+
$cloned2 = clone $collection2;
235+
236+
$collection2->expects($this->once())
237+
->method('getItems')
238+
->willReturn([$wishlistItem1, $wishlistItem2]);
239+
240+
$collection2->expects($this->once())
241+
->method('getColumnValues')
242+
->with('product_id')
243+
->willReturn([21, 22]);
244+
245+
$cloned2->expects($this->once())
246+
->method('addFieldToFilter')
247+
->with('main_table.product_id', ['in' => [21, 22]])
248+
->willReturnSelf();
249+
250+
$this->processor->execute($collection2);
251+
}
252+
}

app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Magento\Framework\Registry;
2727
use Magento\Framework\Serialize\Serializer\Json;
2828
use Magento\Framework\Stdlib\DateTime;
29+
use Magento\Store\Api\Data\StoreInterface;
2930
use Magento\Store\Model\StoreManagerInterface;
3031
use Magento\Wishlist\Helper\Data;
3132
use Magento\Wishlist\Model\Item;
@@ -233,7 +234,31 @@ protected function setUp(): void
233234
);
234235
}
235236

236-
public function testLoadByCustomerId()
237+
/**
238+
* @return void
239+
* @throws \Magento\Framework\Exception\NoSuchEntityException
240+
* @throws \PHPUnit\Framework\MockObject\Exception
241+
*/
242+
public function testGetItemCollection(): void
243+
{
244+
$wishlistItemCollection = $this->createMock(Collection::class);
245+
$wishlistItemCollection->expects($this->once())->method('addWishlistFilter')->willReturnSelf();
246+
$wishlistItemCollection->expects($this->once())->method('addStoreFilter')->willReturnSelf();
247+
$wishlistItemCollection->expects($this->once())->method('setVisibilityFilter')->willReturnSelf();
248+
$this->permissionCollectionProcessor->expects($this->once())
249+
->method('execute')
250+
->with($wishlistItemCollection);
251+
$this->itemsFactory->expects($this->once())->method('create')->willReturn($wishlistItemCollection);
252+
$store = $this->createMock(StoreInterface::class);
253+
$this->storeManager->expects($this->any())->method('getStores')->willReturn([$store]);
254+
$this->wishlist->getItemCollection();
255+
}
256+
257+
/**
258+
* @return void
259+
* @throws LocalizedException
260+
*/
261+
public function testLoadByCustomerId(): void
237262
{
238263
$customerId = 1;
239264
$customerIdFieldName = 'customer_id';
@@ -350,6 +375,7 @@ public function testUpdateItem($itemId, $buyRequest, $param): void
350375
$this->itemsFactory->expects($this->any())
351376
->method('create')
352377
->willReturn($items);
378+
$this->permissionCollectionProcessor->expects($this->once())->method('execute')->willReturn($items);
353379

354380
$this->productRepository->expects($this->once())
355381
->method('getById')

0 commit comments

Comments
 (0)