From b14e5d602165b3abddc0e5a90937d99b3cd88127 Mon Sep 17 00:00:00 2001 From: KrasnoshchokBohdan <krasnoshchok.bohdan@gmail.com> Date: Fri, 6 Jun 2025 11:06:54 +0300 Subject: [PATCH 1/4] magento/magento2#39948: Magento_Persistent performance overhead on non-cart, non-checkout pages - new QuoteResourceWrapper class that uses direct SQL queries instead of loading the entire quote object, which should improve performance --- .../Persistent/Model/QuoteResourceWrapper.php | 60 +++++++++++++++++++ .../CheckExpirePersistentQuoteObserver.php | 38 ++++++++---- 2 files changed, 87 insertions(+), 11 deletions(-) create mode 100644 app/code/Magento/Persistent/Model/QuoteResourceWrapper.php diff --git a/app/code/Magento/Persistent/Model/QuoteResourceWrapper.php b/app/code/Magento/Persistent/Model/QuoteResourceWrapper.php new file mode 100644 index 0000000000000..adc4e216c2b34 --- /dev/null +++ b/app/code/Magento/Persistent/Model/QuoteResourceWrapper.php @@ -0,0 +1,60 @@ +<?php +declare(strict_types=1); + +namespace Magento\Persistent\Model; + +use Magento\Framework\App\ResourceConnection; + +class QuoteResourceWrapper +{ + /** + * @var ResourceConnection + */ + private ResourceConnection $resourceConnection; + + public function __construct( + ResourceConnection $resourceConnection + ) { + $this->resourceConnection = $resourceConnection; + } + + /** + * Check if quote is active. + * + * @param int|null $quoteId + * @return bool + */ + public function isActive(?int $quoteId): bool + { + if (empty($quoteId)) { + return false; + } + $table = $this->resourceConnection->getTableName('quote'); + $connection = $this->resourceConnection->getConnection(); + $select = $connection->select() + ->from($table, 'is_active') + ->where('entity_id = ?', $quoteId); + + return (bool)$connection->fetchOne($select); + } + + /** + * Check if quote is persistent. + * + * @param int|null $quoteId + * @return bool + */ + public function isPersistent(?int $quoteId): bool + { + if (empty($quoteId)) { + return false; + } + $table = $this->resourceConnection->getTableName('quote'); + $connection = $this->resourceConnection->getConnection(); + $select = $connection->select() + ->from($table, 'is_persistent') + ->where('entity_id = ?', $quoteId); + + return (bool)$connection->fetchOne($select); + } +} diff --git a/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php b/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php index cf3d92fe985fc..1a0700d88bf7c 100644 --- a/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php +++ b/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php @@ -1,15 +1,22 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2015 Adobe + * All Rights Reserved. */ namespace Magento\Persistent\Observer; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Event\ManagerInterface; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Persistent\Helper\Data; +use Magento\Persistent\Helper\Session; +use Magento\Persistent\Model\QuoteManager; +use Magento\Persistent\Model\QuoteResourceWrapper; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\CartInterface; use Magento\Quote\Model\Quote; +use Magento\Framework\App\ObjectManager; /** * Observer of expired session @@ -83,14 +90,20 @@ class CheckExpirePersistentQuoteObserver implements ObserverInterface private $quoteRepository; /** - * @param \Magento\Persistent\Helper\Session $persistentSession - * @param \Magento\Persistent\Helper\Data $persistentData - * @param \Magento\Persistent\Model\QuoteManager $quoteManager - * @param \Magento\Framework\Event\ManagerInterface $eventManager + * @var QuoteResourceWrapper|null + */ + private ?QuoteResourceWrapper $quoteResourceWrapper; + + /** + * @param Session $persistentSession + * @param Data $persistentData + * @param QuoteManager $quoteManager + * @param ManagerInterface $eventManager * @param \Magento\Customer\Model\Session $customerSession * @param \Magento\Checkout\Model\Session $checkoutSession - * @param \Magento\Framework\App\RequestInterface $request + * @param RequestInterface $request * @param CartRepositoryInterface $quoteRepository + * @param QuoteResourceWrapper|null $quoteResourceWrapper */ public function __construct( \Magento\Persistent\Helper\Session $persistentSession, @@ -100,7 +113,8 @@ public function __construct( \Magento\Customer\Model\Session $customerSession, \Magento\Checkout\Model\Session $checkoutSession, \Magento\Framework\App\RequestInterface $request, - CartRepositoryInterface $quoteRepository + CartRepositoryInterface $quoteRepository, + ?QuoteResourceWrapper $quoteResourceWrapper = null ) { $this->_persistentSession = $persistentSession; $this->quoteManager = $quoteManager; @@ -110,6 +124,8 @@ public function __construct( $this->_persistentData = $persistentData; $this->request = $request; $this->quoteRepository = $quoteRepository; + $this->quoteResourceWrapper = $quoteResourceWrapper ?: ObjectManager::getInstance() + ->get(QuoteResourceWrapper::class); } /** @@ -141,7 +157,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) $this->_checkoutSession->getQuoteId() && // persistent session does not expire on onepage checkout page !$this->isRequestFromCheckoutPage($this->request) && - $this->getQuote()->getIsPersistent() + (bool)$this->quoteResourceWrapper->isPersistent($this->_checkoutSession->getQuoteId()) ) { $this->_eventManager->dispatch('persistent_session_expired'); $this->quoteManager->expire(); @@ -161,9 +177,9 @@ private function isPersistentQuoteOutdated(): bool if (!($this->_persistentData->isEnabled() && $this->_persistentData->isShoppingCartPersist()) && !$this->_customerSession->isLoggedIn() && $this->_checkoutSession->getQuoteId() - && $this->isActiveQuote() + && $this->quoteResourceWrapper->isActive($this->_checkoutSession->getQuoteId()) ) { - return (bool)$this->getQuote()->getIsPersistent(); + return (bool)$this->quoteResourceWrapper->isPersistent($this->_checkoutSession->getQuoteId()); } return false; } From b2de6869b1a5b9822e1cc3d35c605b68c8206285 Mon Sep 17 00:00:00 2001 From: KrasnoshchokBohdan <krasnoshchok.bohdan@gmail.com> Date: Fri, 6 Jun 2025 16:28:57 +0300 Subject: [PATCH 2/4] magento/magento2#39948: Magento_Persistent performance overhead on non-cart, non-checkout pages - static tests fix --- .../Persistent/Model/QuoteResourceWrapper.php | 9 ++ .../CheckExpirePersistentQuoteObserver.php | 84 +++++-------------- 2 files changed, 28 insertions(+), 65 deletions(-) diff --git a/app/code/Magento/Persistent/Model/QuoteResourceWrapper.php b/app/code/Magento/Persistent/Model/QuoteResourceWrapper.php index adc4e216c2b34..7cb51bc2db068 100644 --- a/app/code/Magento/Persistent/Model/QuoteResourceWrapper.php +++ b/app/code/Magento/Persistent/Model/QuoteResourceWrapper.php @@ -1,4 +1,8 @@ <?php +/** + * Copyright 2025 Adobe + * All Rights Reserved. + */ declare(strict_types=1); namespace Magento\Persistent\Model; @@ -12,6 +16,11 @@ class QuoteResourceWrapper */ private ResourceConnection $resourceConnection; + /** + * Constructor + * + * @param ResourceConnection $resourceConnection + */ public function __construct( ResourceConnection $resourceConnection ) { diff --git a/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php b/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php index 1a0700d88bf7c..8ce6e7299ab91 100644 --- a/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php +++ b/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php @@ -7,15 +7,12 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Persistent\Helper\Data; use Magento\Persistent\Helper\Session; use Magento\Persistent\Model\QuoteManager; use Magento\Persistent\Model\QuoteResourceWrapper; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Api\Data\CartInterface; -use Magento\Quote\Model\Quote; use Magento\Framework\App\ObjectManager; /** @@ -26,14 +23,14 @@ class CheckExpirePersistentQuoteObserver implements ObserverInterface { /** - * Customer session + * Customer session instance for managing customer authentication state * * @var \Magento\Customer\Model\Session */ protected $_customerSession; /** - * Checkout session + * Checkout session instance for managing quote data during checkout * * @var \Magento\Checkout\Model\Session */ @@ -47,7 +44,7 @@ class CheckExpirePersistentQuoteObserver implements ObserverInterface protected $_eventManager = null; /** - * Persistent session + * Helper that provides persistent session functionality * * @var \Magento\Persistent\Helper\Session */ @@ -59,14 +56,14 @@ class CheckExpirePersistentQuoteObserver implements ObserverInterface protected $quoteManager; /** - * Persistent data + * Helper that provides configuration and utility methods for persistent functionality * * @var \Magento\Persistent\Helper\Data */ protected $_persistentData = null; /** - * Request + * Current HTTP request object * * @var \Magento\Framework\App\RequestInterface */ @@ -80,21 +77,15 @@ class CheckExpirePersistentQuoteObserver implements ObserverInterface private $checkoutPagePath = 'checkout'; /** - * @var Quote - */ - private $quote; - - /** - * @var CartRepositoryInterface - */ - private $quoteRepository; - - /** + * Resource wrapper for efficient quote operations + * * @var QuoteResourceWrapper|null */ private ?QuoteResourceWrapper $quoteResourceWrapper; /** + * Constructor + * * @param Session $persistentSession * @param Data $persistentData * @param QuoteManager $quoteManager @@ -102,19 +93,17 @@ class CheckExpirePersistentQuoteObserver implements ObserverInterface * @param \Magento\Customer\Model\Session $customerSession * @param \Magento\Checkout\Model\Session $checkoutSession * @param RequestInterface $request - * @param CartRepositoryInterface $quoteRepository * @param QuoteResourceWrapper|null $quoteResourceWrapper */ public function __construct( - \Magento\Persistent\Helper\Session $persistentSession, + Session $persistentSession, \Magento\Persistent\Helper\Data $persistentData, - \Magento\Persistent\Model\QuoteManager $quoteManager, - \Magento\Framework\Event\ManagerInterface $eventManager, + QuoteManager $quoteManager, + ManagerInterface $eventManager, \Magento\Customer\Model\Session $customerSession, \Magento\Checkout\Model\Session $checkoutSession, - \Magento\Framework\App\RequestInterface $request, - CartRepositoryInterface $quoteRepository, - ?QuoteResourceWrapper $quoteResourceWrapper = null + RequestInterface $request, + ?QuoteResourceWrapper $quoteResourceWrapper = null ) { $this->_persistentSession = $persistentSession; $this->quoteManager = $quoteManager; @@ -123,7 +112,6 @@ public function __construct( $this->_eventManager = $eventManager; $this->_persistentData = $persistentData; $this->request = $request; - $this->quoteRepository = $quoteRepository; $this->quoteResourceWrapper = $quoteResourceWrapper ?: ObjectManager::getInstance() ->get(QuoteResourceWrapper::class); } @@ -131,12 +119,10 @@ public function __construct( /** * Check and clear session data if persistent session expired * - * @param \Magento\Framework\Event\Observer $observer + * @param Observer $observer * @return void - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function execute(\Magento\Framework\Event\Observer $observer) + public function execute(Observer $observer) { if (!$this->_persistentData->canProcess($observer)) { return; @@ -169,8 +155,6 @@ public function execute(\Magento\Framework\Event\Observer $observer) * Checks if current quote marked as persistent and Persistence Functionality is disabled. * * @return bool - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException */ private function isPersistentQuoteOutdated(): bool { @@ -184,43 +168,13 @@ private function isPersistentQuoteOutdated(): bool return false; } - /** - * Getter for Quote with micro optimization - * - * @return Quote - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - private function getQuote(): Quote - { - if ($this->quote === null) { - $this->quote = $this->_checkoutSession->getQuote(); - } - return $this->quote; - } - - /** - * Check if quote is active. - * - * @return bool - */ - private function isActiveQuote(): bool - { - try { - $this->quoteRepository->getActive($this->_checkoutSession->getQuoteId()); - return true; - } catch (NoSuchEntityException $e) { - return false; - } - } - /** * Check current request is coming from onepage checkout page. * - * @param \Magento\Framework\App\RequestInterface $request + * @param RequestInterface $request * @return bool */ - private function isRequestFromCheckoutPage(\Magento\Framework\App\RequestInterface $request): bool + private function isRequestFromCheckoutPage(RequestInterface $request): bool { $requestUri = (string)$request->getRequestUri(); $refererUri = (string)$request->getServer('HTTP_REFERER'); From fdff3712f838331a21c1374ac2e203cf519a36cf Mon Sep 17 00:00:00 2001 From: KrasnoshchokBohdan <krasnoshchok.bohdan@gmail.com> Date: Fri, 6 Jun 2025 16:57:16 +0300 Subject: [PATCH 3/4] magento/magento2#39948: Magento_Persistent performance overhead on non-cart, non-checkout pages - unit tests --- .../Unit/Model/QuoteResourceWrapperTest.php | 222 ++++++++++++++++++ ...CheckExpirePersistentQuoteObserverTest.php | 68 +++--- 2 files changed, 257 insertions(+), 33 deletions(-) create mode 100644 app/code/Magento/Persistent/Test/Unit/Model/QuoteResourceWrapperTest.php diff --git a/app/code/Magento/Persistent/Test/Unit/Model/QuoteResourceWrapperTest.php b/app/code/Magento/Persistent/Test/Unit/Model/QuoteResourceWrapperTest.php new file mode 100644 index 0000000000000..9ee1f86615b9a --- /dev/null +++ b/app/code/Magento/Persistent/Test/Unit/Model/QuoteResourceWrapperTest.php @@ -0,0 +1,222 @@ +<?php +/** + * Copyright 2025 Adobe + * All Rights Reserved. + */ +declare(strict_types=1); + +namespace Magento\Persistent\Test\Unit\Model; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Select; +use Magento\Persistent\Model\QuoteResourceWrapper; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class QuoteResourceWrapperTest extends TestCase +{ + /** + * @var ResourceConnection|MockObject + */ + private $resourceConnectionMock; + + /** + * @var AdapterInterface|MockObject + */ + private $connectionMock; + + /** + * @var QuoteResourceWrapper + */ + private $model; + + /** + * @var Select|MockObject + */ + private $selectMock; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->resourceConnectionMock = $this->createMock(ResourceConnection::class); + $this->connectionMock = $this->getMockForAbstractClass(AdapterInterface::class); + $this->selectMock = $this->createMock(Select::class); + + $this->model = new QuoteResourceWrapper($this->resourceConnectionMock); + + $this->resourceConnectionMock->method('getConnection') + ->willReturn($this->connectionMock); + $this->connectionMock->method('select') + ->willReturn($this->selectMock); + } + + /** + * Test isActive with null quote ID + */ + public function testIsActiveWithNullQuoteId(): void + { + $this->assertFalse($this->model->isActive(null)); + } + + /** + * Test isActive with active quote + */ + public function testIsActiveWithActiveQuote(): void + { + $quoteId = 123; + $tableName = 'quote'; + + $this->resourceConnectionMock->expects($this->once()) + ->method('getTableName') + ->with('quote') + ->willReturn($tableName); + + $this->selectMock->expects($this->once()) + ->method('from') + ->with($tableName, 'is_active') + ->willReturnSelf(); + + $this->selectMock->expects($this->once()) + ->method('where') + ->with('entity_id = ?', $quoteId) + ->willReturnSelf(); + + $this->connectionMock->expects($this->once()) + ->method('fetchOne') + ->with($this->selectMock) + ->willReturn('1'); + + $this->assertTrue($this->model->isActive($quoteId)); + } + + /** + * Test isActive with inactive quote + */ + public function testIsActiveWithInactiveQuote(): void + { + $quoteId = 123; + $tableName = 'quote'; + + $this->resourceConnectionMock->expects($this->once()) + ->method('getTableName') + ->with('quote') + ->willReturn($tableName); + + $this->selectMock->expects($this->once()) + ->method('from') + ->with($tableName, 'is_active') + ->willReturnSelf(); + + $this->selectMock->expects($this->once()) + ->method('where') + ->with('entity_id = ?', $quoteId) + ->willReturnSelf(); + + $this->connectionMock->expects($this->once()) + ->method('fetchOne') + ->with($this->selectMock) + ->willReturn('0'); + + $this->assertFalse($this->model->isActive($quoteId)); + } + + /** + * Test isPersistent with null quote ID + */ + public function testIsPersistentWithNullQuoteId(): void + { + $this->assertFalse($this->model->isPersistent(null)); + } + + /** + * Test isPersistent with persistent quote + */ + public function testIsPersistentWithPersistentQuote(): void + { + $quoteId = 123; + $tableName = 'quote'; + + $this->resourceConnectionMock->expects($this->once()) + ->method('getTableName') + ->with('quote') + ->willReturn($tableName); + + $this->selectMock->expects($this->once()) + ->method('from') + ->with($tableName, 'is_persistent') + ->willReturnSelf(); + + $this->selectMock->expects($this->once()) + ->method('where') + ->with('entity_id = ?', $quoteId) + ->willReturnSelf(); + + $this->connectionMock->expects($this->once()) + ->method('fetchOne') + ->with($this->selectMock) + ->willReturn('1'); + + $this->assertTrue($this->model->isPersistent($quoteId)); + } + + /** + * Test isPersistent with non-persistent quote + */ + public function testIsPersistentWithNonPersistentQuote(): void + { + $quoteId = 123; + $tableName = 'quote'; + + $this->resourceConnectionMock->expects($this->once()) + ->method('getTableName') + ->with('quote') + ->willReturn($tableName); + + $this->selectMock->expects($this->once()) + ->method('from') + ->with($tableName, 'is_persistent') + ->willReturnSelf(); + + $this->selectMock->expects($this->once()) + ->method('where') + ->with('entity_id = ?', $quoteId) + ->willReturnSelf(); + + $this->connectionMock->expects($this->once()) + ->method('fetchOne') + ->with($this->selectMock) + ->willReturn('0'); + + $this->assertFalse($this->model->isPersistent($quoteId)); + } + + /** + * Test type casting from database on isPersistent + */ + public function testIsPersistentTypeCasting(): void + { + $quoteId = 123; + $tableName = 'quote'; + + $this->resourceConnectionMock->expects($this->once()) + ->method('getTableName') + ->willReturn($tableName); + + $this->selectMock->expects($this->once()) + ->method('from') + ->willReturnSelf(); + + $this->selectMock->expects($this->once()) + ->method('where') + ->willReturnSelf(); + + $this->connectionMock->expects($this->once()) + ->method('fetchOne') + ->willReturn(''); + + $this->assertFalse($this->model->isPersistent($quoteId)); + } +} diff --git a/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php b/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php index c26dde2b03ce9..c66dfbb920b70 100644 --- a/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php +++ b/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2015 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -10,13 +10,11 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\Event\ManagerInterface; use Magento\Framework\Event\Observer; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Persistent\Helper\Data; use Magento\Persistent\Helper\Session; use Magento\Persistent\Model\QuoteManager; +use Magento\Persistent\Model\QuoteResourceWrapper; use Magento\Persistent\Observer\CheckExpirePersistentQuoteObserver; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Model\Quote; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\MockObject\Rule\InvokedCount; use PHPUnit\Framework\TestCase; @@ -72,14 +70,9 @@ class CheckExpirePersistentQuoteObserverTest extends TestCase private $requestMock; /** - * @var MockObject|Quote + * @var MockObject|QuoteResourceWrapper */ - private $quoteMock; - - /** - * @var MockObject|CartRepositoryInterface - */ - private $quoteRepositoryMock; + private $quoteResourceWrapperMock; /** * @inheritdoc @@ -100,7 +93,7 @@ protected function setUp(): void ->disableOriginalConstructor() ->addMethods(['getRequestUri', 'getServer']) ->getMockForAbstractClass(); - $this->quoteRepositoryMock = $this->getMockForAbstractClass(CartRepositoryInterface::class); + $this->quoteResourceWrapperMock = $this->createMock(QuoteResourceWrapper::class); $this->model = new CheckExpirePersistentQuoteObserver( $this->sessionMock, @@ -110,13 +103,8 @@ protected function setUp(): void $this->customerSessionMock, $this->checkoutSessionMock, $this->requestMock, - $this->quoteRepositoryMock + $this->quoteResourceWrapperMock ); - $this->quoteMock = $this->getMockBuilder(Quote::class) - ->addMethods(['getIsPersistent']) - ->onlyMethods(['getCustomerIsGuest']) - ->disableOriginalConstructor() - ->getMock(); } public function testExecuteWhenCanNotApplyPersistentData() @@ -132,20 +120,30 @@ public function testExecuteWhenCanNotApplyPersistentData() public function testExecuteWhenPersistentIsNotEnabled() { - $quoteId = 'quote_id_1'; + $quoteId = 10; $this->persistentHelperMock ->expects($this->once()) ->method('canProcess') ->with($this->observerMock) ->willReturn(true); - $this->persistentHelperMock->expects($this->exactly(2))->method('isEnabled')->willReturn(false); - $this->checkoutSessionMock->expects($this->exactly(2))->method('getQuoteId')->willReturn($quoteId); - $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive') + $this->persistentHelperMock->expects($this->once())->method('isEnabled')->willReturn(false); + $this->checkoutSessionMock->expects($this->any())->method('getQuoteId')->willReturn($quoteId); + + $this->quoteResourceWrapperMock->expects($this->once()) + ->method('isActive') + ->with($quoteId) + ->willReturn(true); + $this->quoteResourceWrapperMock->expects($this->once()) + ->method('isPersistent') ->with($quoteId) - ->willThrowException(new NoSuchEntityException()); - $this->eventManagerMock->expects($this->never())->method('dispatch'); + ->willReturn(true); + + $this->eventManagerMock->expects($this->once())->method('dispatch'); + $this->quoteManagerMock->expects($this->once())->method('expire'); + $this->checkoutSessionMock->expects($this->once())->method('clearQuote'); + $this->customerSessionMock->expects($this->once())->method('setCustomerId')->with(null)->willReturnSelf(); + $this->model->execute($this->observerMock); } @@ -167,6 +165,8 @@ public function testExecuteWhenPersistentIsEnabled( InvokedCount $dispatchCounter, InvokedCount $setCustomerIdCounter ): void { + $quoteId = 10; + $this->persistentHelperMock ->expects($this->once()) ->method('canProcess') @@ -179,19 +179,21 @@ public function testExecuteWhenPersistentIsEnabled( ->method('isShoppingCartPersist') ->willReturn(true); $this->sessionMock->expects($this->atLeastOnce())->method('isPersistent')->willReturn(false); - $this->checkoutSessionMock - ->method('getQuote') - ->willReturn($this->quoteMock); - $this->quoteMock->method('getCustomerIsGuest')->willReturn(true); - $this->quoteMock->method('getIsPersistent')->willReturn(true); $this->customerSessionMock ->expects($this->atLeastOnce()) ->method('isLoggedIn') ->willReturn(false); $this->checkoutSessionMock - ->expects($this->atLeastOnce()) + ->expects($this->any()) ->method('getQuoteId') - ->willReturn(10); + ->willReturn($quoteId); + + // Mock the QuoteResourceWrapper calls + $this->quoteResourceWrapperMock->expects($this->any()) + ->method('isPersistent') + ->with($quoteId) + ->willReturn(true); + $this->eventManagerMock->expects($dispatchCounter)->method('dispatch'); $this->quoteManagerMock->expects($expireCounter)->method('expire'); $this->customerSessionMock From 94eb707b7a00c11d9c177d4d730edf7ba3314227 Mon Sep 17 00:00:00 2001 From: KrasnoshchokBohdan <krasnoshchok.bohdan@gmail.com> Date: Sat, 7 Jun 2025 10:23:24 +0300 Subject: [PATCH 4/4] magento/magento2#39948: Magento_Persistent performance overhead on non-cart, non-checkout pages - static tests --- .../Persistent/Observer/CheckExpirePersistentQuoteObserver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php b/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php index 8ce6e7299ab91..894f372b29781 100644 --- a/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php +++ b/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php @@ -70,7 +70,7 @@ class CheckExpirePersistentQuoteObserver implements ObserverInterface private $request; /** - * Checkout Page path + * Path identifier for checkout pages * * @var string */