From b16430296a8297e4f3e7ec708865279053a5935b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Oct 2025 19:41:03 +0000 Subject: [PATCH 1/5] Initial plan From 91453f4f3b52671990eebec7052ce74d887021d4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Oct 2025 19:51:52 +0000 Subject: [PATCH 2/5] Add state management to optimize salesrule attribute loading Co-authored-by: ihor-sviziev <1873745+ihor-sviziev@users.noreply.github.com> --- .../Plugin/QuoteConfigProductAttributes.php | 22 +++++- .../Model/Plugin/TotalsCollectorPlugin.php | 59 +++++++++++++++ .../Model/Quote/TotalsCollectionState.php | 42 +++++++++++ .../QuoteConfigProductAttributesTest.php | 29 +++++++- .../Plugin/TotalsCollectorPluginTest.php | 72 +++++++++++++++++++ .../Model/Quote/TotalsCollectionStateTest.php | 56 +++++++++++++++ app/code/Magento/SalesRule/etc/di.xml | 3 + 7 files changed, 280 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php create mode 100644 app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php create mode 100644 app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php create mode 100644 app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php diff --git a/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php b/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php index 764658b3ac00a..790fe51ec582d 100644 --- a/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php +++ b/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php @@ -8,6 +8,7 @@ use Magento\Quote\Model\Quote\Config; use Magento\SalesRule\Model\ResourceModel\Rule as RuleResource; +use Magento\SalesRule\Model\Quote\TotalsCollectionState; class QuoteConfigProductAttributes { @@ -21,17 +22,29 @@ class QuoteConfigProductAttributes */ private $activeAttributeCodes; + /** + * @var TotalsCollectionState + */ + private $totalsCollectionState; + /** * @param RuleResource $ruleResource + * @param TotalsCollectionState $totalsCollectionState */ - public function __construct(RuleResource $ruleResource) - { + public function __construct( + RuleResource $ruleResource, + TotalsCollectionState $totalsCollectionState + ) { $this->ruleResource = $ruleResource; + $this->totalsCollectionState = $totalsCollectionState; } /** * Append sales rule product attribute keys to select by quote item collection * + * Only loads attributes when totals collection is in progress to avoid unnecessary + * database queries and EAV attribute loading on every cart view. + * * @param Config $subject * @param array $attributeKeys * @@ -40,6 +53,11 @@ public function __construct(RuleResource $ruleResource) */ public function afterGetProductAttributes(Config $subject, array $attributeKeys): array { + // Only load salesrule attributes when actually collecting totals + if (!$this->totalsCollectionState->isCollecting()) { + return $attributeKeys; + } + if ($this->activeAttributeCodes === null) { $this->activeAttributeCodes = array_column($this->ruleResource->getActiveAttributes(), 'attribute_code'); } diff --git a/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php b/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php new file mode 100644 index 0000000000000..d5e0754ee57c9 --- /dev/null +++ b/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php @@ -0,0 +1,59 @@ +totalsCollectionState = $totalsCollectionState; + } + + /** + * Set flag before totals collection starts + * + * @param TotalsCollector $subject + * @param Quote $quote + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeCollect(TotalsCollector $subject, Quote $quote): array + { + $this->totalsCollectionState->setIsCollecting(true); + return [$quote]; + } + + /** + * Unset flag after totals collection completes + * + * @param TotalsCollector $subject + * @param Total $result + * @return Total + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterCollect(TotalsCollector $subject, Total $result): Total + { + $this->totalsCollectionState->setIsCollecting(false); + return $result; + } +} diff --git a/app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php b/app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php new file mode 100644 index 0000000000000..9bc80a4a47de2 --- /dev/null +++ b/app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php @@ -0,0 +1,42 @@ +isCollecting = $state; + } + + /** + * Check if totals collection is in progress + * + * @return bool + */ + public function isCollecting(): bool + { + return $this->isCollecting; + } +} diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php index a06584cec2273..92e32281727ee 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php @@ -10,6 +10,7 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Quote\Model\Quote\Config; use Magento\SalesRule\Model\Plugin\QuoteConfigProductAttributes; +use Magento\SalesRule\Model\Quote\TotalsCollectionState; use Magento\SalesRule\Model\ResourceModel\Rule; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -26,15 +27,22 @@ class QuoteConfigProductAttributesTest extends TestCase */ protected $ruleResource; + /** + * @var TotalsCollectionState|MockObject + */ + protected $totalsCollectionState; + protected function setUp(): void { $objectManager = new ObjectManager($this); $this->ruleResource = $this->createMock(Rule::class); + $this->totalsCollectionState = $this->createMock(TotalsCollectionState::class); $this->plugin = $objectManager->getObject( QuoteConfigProductAttributes::class, [ - 'ruleResource' => $this->ruleResource + 'ruleResource' => $this->ruleResource, + 'totalsCollectionState' => $this->totalsCollectionState ] ); } @@ -45,6 +53,10 @@ public function testAfterGetProductAttributes() $attributeCode = 'code of the attribute'; $expected = [0 => $attributeCode]; + $this->totalsCollectionState->expects($this->once()) + ->method('isCollecting') + ->willReturn(true); + $this->ruleResource->expects($this->once()) ->method('getActiveAttributes') ->willReturn( @@ -55,4 +67,19 @@ public function testAfterGetProductAttributes() $this->assertEquals($expected, $this->plugin->afterGetProductAttributes($subject, [])); } + + public function testAfterGetProductAttributesNotCollecting() + { + $subject = $this->createMock(Config::class); + $inputAttributes = ['existing_attribute']; + + $this->totalsCollectionState->expects($this->once()) + ->method('isCollecting') + ->willReturn(false); + + $this->ruleResource->expects($this->never()) + ->method('getActiveAttributes'); + + $this->assertEquals($inputAttributes, $this->plugin->afterGetProductAttributes($subject, $inputAttributes)); + } } diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php new file mode 100644 index 0000000000000..f626ed5f53417 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php @@ -0,0 +1,72 @@ +totalsCollectionState = $this->createMock(TotalsCollectionState::class); + $this->totalsCollector = $this->createMock(TotalsCollector::class); + $this->quote = $this->createMock(Quote::class); + + $this->plugin = new TotalsCollectorPlugin($this->totalsCollectionState); + } + + public function testBeforeCollectSetsState() + { + $this->totalsCollectionState->expects($this->once()) + ->method('setIsCollecting') + ->with(true); + + $result = $this->plugin->beforeCollect($this->totalsCollector, $this->quote); + + $this->assertEquals([$this->quote], $result); + } + + public function testAfterCollectUnsetsState() + { + $total = $this->createMock(Total::class); + + $this->totalsCollectionState->expects($this->once()) + ->method('setIsCollecting') + ->with(false); + + $result = $this->plugin->afterCollect($this->totalsCollector, $total); + + $this->assertSame($total, $result); + } +} diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php new file mode 100644 index 0000000000000..7da89194e6abe --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php @@ -0,0 +1,56 @@ +state = new TotalsCollectionState(); + } + + public function testDefaultStateIsFalse() + { + $this->assertFalse($this->state->isCollecting()); + } + + public function testSetIsCollectingTrue() + { + $this->state->setIsCollecting(true); + $this->assertTrue($this->state->isCollecting()); + } + + public function testSetIsCollectingFalse() + { + $this->state->setIsCollecting(true); + $this->state->setIsCollecting(false); + $this->assertFalse($this->state->isCollecting()); + } + + public function testMultipleToggle() + { + $this->assertFalse($this->state->isCollecting()); + + $this->state->setIsCollecting(true); + $this->assertTrue($this->state->isCollecting()); + + $this->state->setIsCollecting(false); + $this->assertFalse($this->state->isCollecting()); + + $this->state->setIsCollecting(true); + $this->assertTrue($this->state->isCollecting()); + } +} diff --git a/app/code/Magento/SalesRule/etc/di.xml b/app/code/Magento/SalesRule/etc/di.xml index cd841b36338e4..2be44da6494ac 100644 --- a/app/code/Magento/SalesRule/etc/di.xml +++ b/app/code/Magento/SalesRule/etc/di.xml @@ -54,6 +54,9 @@ + + + From a219c3fb66af632f6ace702f00fdcbc573ca46d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Oct 2025 19:54:39 +0000 Subject: [PATCH 3/5] Force reload of items collection during totals collection to ensure salesrule attributes are loaded Co-authored-by: ihor-sviziev <1873745+ihor-sviziev@users.noreply.github.com> --- .../Model/Plugin/TotalsCollectorPlugin.php | 11 +++++++- .../Plugin/TotalsCollectorPluginTest.php | 28 ++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php b/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php index d5e0754ee57c9..0b8c0447cdec8 100644 --- a/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php +++ b/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php @@ -30,7 +30,10 @@ public function __construct(TotalsCollectionState $totalsCollectionState) } /** - * Set flag before totals collection starts + * Set flag before totals collection starts and ensure items are reloaded + * + * Forces quote items collection to be reloaded to ensure salesrule attributes + * are included when totals collection requires them. * * @param TotalsCollector $subject * @param Quote $quote @@ -40,6 +43,12 @@ public function __construct(TotalsCollectionState $totalsCollectionState) public function beforeCollect(TotalsCollector $subject, Quote $quote): array { $this->totalsCollectionState->setIsCollecting(true); + + // Force reload of items collection to ensure salesrule attributes are loaded + if ($quote->hasItemsCollection()) { + $quote->unsetData('items_collection'); + } + return [$quote]; } diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php index f626ed5f53417..d20aa6cfe4dbd 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php @@ -46,8 +46,34 @@ protected function setUp(): void $this->plugin = new TotalsCollectorPlugin($this->totalsCollectionState); } - public function testBeforeCollectSetsState() + public function testBeforeCollectSetsStateAndClearsCache() { + $this->quote->expects($this->once()) + ->method('hasItemsCollection') + ->willReturn(true); + + $this->quote->expects($this->once()) + ->method('unsetData') + ->with('items_collection'); + + $this->totalsCollectionState->expects($this->once()) + ->method('setIsCollecting') + ->with(true); + + $result = $this->plugin->beforeCollect($this->totalsCollector, $this->quote); + + $this->assertEquals([$this->quote], $result); + } + + public function testBeforeCollectSetsStateWithoutClearingCacheIfNotSet() + { + $this->quote->expects($this->once()) + ->method('hasItemsCollection') + ->willReturn(false); + + $this->quote->expects($this->never()) + ->method('unsetData'); + $this->totalsCollectionState->expects($this->once()) ->method('setIsCollecting') ->with(true); From 681e56c5fe107a2f4838372a813745eee90978a7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Oct 2025 19:57:47 +0000 Subject: [PATCH 4/5] Fix code formatting in TotalsCollectorPlugin Co-authored-by: ihor-sviziev <1873745+ihor-sviziev@users.noreply.github.com> --- .../Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php b/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php index 0b8c0447cdec8..f3e2465f04654 100644 --- a/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php +++ b/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php @@ -48,7 +48,7 @@ public function beforeCollect(TotalsCollector $subject, Quote $quote): array if ($quote->hasItemsCollection()) { $quote->unsetData('items_collection'); } - + return [$quote]; } From bf149f585cc19afeddae40d54e7bd9add5f2f5b2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 08:53:41 +0000 Subject: [PATCH 5/5] Refactor to use cache-based approach instead of state management Co-authored-by: ihor-sviziev <1873745+ihor-sviziev@users.noreply.github.com> --- .../Plugin/QuoteConfigProductAttributes.php | 55 ++++++++--- .../Model/Plugin/TotalsCollectorPlugin.php | 68 ------------- .../Model/Quote/TotalsCollectionState.php | 42 -------- .../ClearProductAttributesCacheObserver.php | 47 +++++++++ .../QuoteConfigProductAttributesTest.php | 75 +++++++++----- .../Plugin/TotalsCollectorPluginTest.php | 98 ------------------- .../Model/Quote/TotalsCollectionStateTest.php | 56 ----------- ...learProductAttributesCacheObserverTest.php | 44 +++++++++ app/code/Magento/SalesRule/etc/di.xml | 3 - app/code/Magento/SalesRule/etc/events.xml | 2 + 10 files changed, 185 insertions(+), 305 deletions(-) delete mode 100644 app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php delete mode 100644 app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php create mode 100644 app/code/Magento/SalesRule/Observer/ClearProductAttributesCacheObserver.php delete mode 100644 app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php delete mode 100644 app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php create mode 100644 app/code/Magento/SalesRule/Test/Unit/Observer/ClearProductAttributesCacheObserverTest.php diff --git a/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php b/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php index 790fe51ec582d..f7ee3e6c27914 100644 --- a/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php +++ b/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php @@ -6,12 +6,23 @@ namespace Magento\SalesRule\Model\Plugin; +use Magento\Framework\App\CacheInterface; +use Magento\Framework\Serialize\SerializerInterface; use Magento\Quote\Model\Quote\Config; use Magento\SalesRule\Model\ResourceModel\Rule as RuleResource; -use Magento\SalesRule\Model\Quote\TotalsCollectionState; class QuoteConfigProductAttributes { + /** + * Cache key for active salesrule attributes + */ + private const CACHE_KEY = 'salesrule_active_product_attributes'; + + /** + * Cache tag for salesrule attributes + */ + private const CACHE_TAG = 'salesrule'; + /** * @var RuleResource */ @@ -23,27 +34,34 @@ class QuoteConfigProductAttributes private $activeAttributeCodes; /** - * @var TotalsCollectionState + * @var CacheInterface */ - private $totalsCollectionState; + private $cache; + + /** + * @var SerializerInterface + */ + private $serializer; /** * @param RuleResource $ruleResource - * @param TotalsCollectionState $totalsCollectionState + * @param CacheInterface $cache + * @param SerializerInterface $serializer */ public function __construct( RuleResource $ruleResource, - TotalsCollectionState $totalsCollectionState + CacheInterface $cache, + SerializerInterface $serializer ) { $this->ruleResource = $ruleResource; - $this->totalsCollectionState = $totalsCollectionState; + $this->cache = $cache; + $this->serializer = $serializer; } /** * Append sales rule product attribute keys to select by quote item collection * - * Only loads attributes when totals collection is in progress to avoid unnecessary - * database queries and EAV attribute loading on every cart view. + * Uses cache to avoid database queries on every request. * * @param Config $subject * @param array $attributeKeys @@ -53,13 +71,22 @@ public function __construct( */ public function afterGetProductAttributes(Config $subject, array $attributeKeys): array { - // Only load salesrule attributes when actually collecting totals - if (!$this->totalsCollectionState->isCollecting()) { - return $attributeKeys; - } - if ($this->activeAttributeCodes === null) { - $this->activeAttributeCodes = array_column($this->ruleResource->getActiveAttributes(), 'attribute_code'); + $cachedData = $this->cache->load(self::CACHE_KEY); + + if ($cachedData !== false) { + $this->activeAttributeCodes = $this->serializer->unserialize($cachedData); + } else { + $this->activeAttributeCodes = array_column( + $this->ruleResource->getActiveAttributes(), + 'attribute_code' + ); + $this->cache->save( + $this->serializer->serialize($this->activeAttributeCodes), + self::CACHE_KEY, + [self::CACHE_TAG] + ); + } } return array_merge($attributeKeys, $this->activeAttributeCodes); diff --git a/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php b/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php deleted file mode 100644 index f3e2465f04654..0000000000000 --- a/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php +++ /dev/null @@ -1,68 +0,0 @@ -totalsCollectionState = $totalsCollectionState; - } - - /** - * Set flag before totals collection starts and ensure items are reloaded - * - * Forces quote items collection to be reloaded to ensure salesrule attributes - * are included when totals collection requires them. - * - * @param TotalsCollector $subject - * @param Quote $quote - * @return array - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function beforeCollect(TotalsCollector $subject, Quote $quote): array - { - $this->totalsCollectionState->setIsCollecting(true); - - // Force reload of items collection to ensure salesrule attributes are loaded - if ($quote->hasItemsCollection()) { - $quote->unsetData('items_collection'); - } - - return [$quote]; - } - - /** - * Unset flag after totals collection completes - * - * @param TotalsCollector $subject - * @param Total $result - * @return Total - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function afterCollect(TotalsCollector $subject, Total $result): Total - { - $this->totalsCollectionState->setIsCollecting(false); - return $result; - } -} diff --git a/app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php b/app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php deleted file mode 100644 index 9bc80a4a47de2..0000000000000 --- a/app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php +++ /dev/null @@ -1,42 +0,0 @@ -isCollecting = $state; - } - - /** - * Check if totals collection is in progress - * - * @return bool - */ - public function isCollecting(): bool - { - return $this->isCollecting; - } -} diff --git a/app/code/Magento/SalesRule/Observer/ClearProductAttributesCacheObserver.php b/app/code/Magento/SalesRule/Observer/ClearProductAttributesCacheObserver.php new file mode 100644 index 0000000000000..68e14e2bef4c9 --- /dev/null +++ b/app/code/Magento/SalesRule/Observer/ClearProductAttributesCacheObserver.php @@ -0,0 +1,47 @@ +cache = $cache; + } + + /** + * Clear salesrule product attributes cache + * + * @param Observer $observer + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function execute(Observer $observer): void + { + $this->cache->remove(self::CACHE_KEY); + } +} diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php index 92e32281727ee..c9b2f211c5463 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php @@ -7,10 +7,11 @@ namespace Magento\SalesRule\Test\Unit\Model\Plugin; +use Magento\Framework\App\CacheInterface; +use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Quote\Model\Quote\Config; use Magento\SalesRule\Model\Plugin\QuoteConfigProductAttributes; -use Magento\SalesRule\Model\Quote\TotalsCollectionState; use Magento\SalesRule\Model\ResourceModel\Rule; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -18,7 +19,7 @@ class QuoteConfigProductAttributesTest extends TestCase { /** - * @var QuoteConfigProductAttributes|MockObject + * @var QuoteConfigProductAttributes */ protected $plugin; @@ -28,58 +29,84 @@ class QuoteConfigProductAttributesTest extends TestCase protected $ruleResource; /** - * @var TotalsCollectionState|MockObject + * @var CacheInterface|MockObject */ - protected $totalsCollectionState; + protected $cache; + + /** + * @var SerializerInterface|MockObject + */ + protected $serializer; protected function setUp(): void { $objectManager = new ObjectManager($this); $this->ruleResource = $this->createMock(Rule::class); - $this->totalsCollectionState = $this->createMock(TotalsCollectionState::class); + $this->cache = $this->createMock(CacheInterface::class); + $this->serializer = $this->createMock(SerializerInterface::class); $this->plugin = $objectManager->getObject( QuoteConfigProductAttributes::class, [ 'ruleResource' => $this->ruleResource, - 'totalsCollectionState' => $this->totalsCollectionState + 'cache' => $this->cache, + 'serializer' => $this->serializer ] ); } - public function testAfterGetProductAttributes() + public function testAfterGetProductAttributesWithCache() { $subject = $this->createMock(Config::class); $attributeCode = 'code of the attribute'; $expected = [0 => $attributeCode]; + $serializedData = '["' . $attributeCode . '"]'; - $this->totalsCollectionState->expects($this->once()) - ->method('isCollecting') - ->willReturn(true); + $this->cache->expects($this->once()) + ->method('load') + ->with('salesrule_active_product_attributes') + ->willReturn($serializedData); - $this->ruleResource->expects($this->once()) - ->method('getActiveAttributes') - ->willReturn( - [ - ['attribute_code' => $attributeCode, 'enabled' => true], - ] - ); + $this->serializer->expects($this->once()) + ->method('unserialize') + ->with($serializedData) + ->willReturn([$attributeCode]); + + $this->ruleResource->expects($this->never()) + ->method('getActiveAttributes'); $this->assertEquals($expected, $this->plugin->afterGetProductAttributes($subject, [])); } - public function testAfterGetProductAttributesNotCollecting() + public function testAfterGetProductAttributesWithoutCache() { $subject = $this->createMock(Config::class); - $inputAttributes = ['existing_attribute']; + $attributeCode = 'code of the attribute'; + $expected = [0 => $attributeCode]; + $serializedData = '["' . $attributeCode . '"]'; - $this->totalsCollectionState->expects($this->once()) - ->method('isCollecting') + $this->cache->expects($this->once()) + ->method('load') + ->with('salesrule_active_product_attributes') ->willReturn(false); - $this->ruleResource->expects($this->never()) - ->method('getActiveAttributes'); + $this->ruleResource->expects($this->once()) + ->method('getActiveAttributes') + ->willReturn( + [ + ['attribute_code' => $attributeCode, 'enabled' => true], + ] + ); - $this->assertEquals($inputAttributes, $this->plugin->afterGetProductAttributes($subject, $inputAttributes)); + $this->serializer->expects($this->once()) + ->method('serialize') + ->with([$attributeCode]) + ->willReturn($serializedData); + + $this->cache->expects($this->once()) + ->method('save') + ->with($serializedData, 'salesrule_active_product_attributes', ['salesrule']); + + $this->assertEquals($expected, $this->plugin->afterGetProductAttributes($subject, [])); } } diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php deleted file mode 100644 index d20aa6cfe4dbd..0000000000000 --- a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php +++ /dev/null @@ -1,98 +0,0 @@ -totalsCollectionState = $this->createMock(TotalsCollectionState::class); - $this->totalsCollector = $this->createMock(TotalsCollector::class); - $this->quote = $this->createMock(Quote::class); - - $this->plugin = new TotalsCollectorPlugin($this->totalsCollectionState); - } - - public function testBeforeCollectSetsStateAndClearsCache() - { - $this->quote->expects($this->once()) - ->method('hasItemsCollection') - ->willReturn(true); - - $this->quote->expects($this->once()) - ->method('unsetData') - ->with('items_collection'); - - $this->totalsCollectionState->expects($this->once()) - ->method('setIsCollecting') - ->with(true); - - $result = $this->plugin->beforeCollect($this->totalsCollector, $this->quote); - - $this->assertEquals([$this->quote], $result); - } - - public function testBeforeCollectSetsStateWithoutClearingCacheIfNotSet() - { - $this->quote->expects($this->once()) - ->method('hasItemsCollection') - ->willReturn(false); - - $this->quote->expects($this->never()) - ->method('unsetData'); - - $this->totalsCollectionState->expects($this->once()) - ->method('setIsCollecting') - ->with(true); - - $result = $this->plugin->beforeCollect($this->totalsCollector, $this->quote); - - $this->assertEquals([$this->quote], $result); - } - - public function testAfterCollectUnsetsState() - { - $total = $this->createMock(Total::class); - - $this->totalsCollectionState->expects($this->once()) - ->method('setIsCollecting') - ->with(false); - - $result = $this->plugin->afterCollect($this->totalsCollector, $total); - - $this->assertSame($total, $result); - } -} diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php deleted file mode 100644 index 7da89194e6abe..0000000000000 --- a/app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php +++ /dev/null @@ -1,56 +0,0 @@ -state = new TotalsCollectionState(); - } - - public function testDefaultStateIsFalse() - { - $this->assertFalse($this->state->isCollecting()); - } - - public function testSetIsCollectingTrue() - { - $this->state->setIsCollecting(true); - $this->assertTrue($this->state->isCollecting()); - } - - public function testSetIsCollectingFalse() - { - $this->state->setIsCollecting(true); - $this->state->setIsCollecting(false); - $this->assertFalse($this->state->isCollecting()); - } - - public function testMultipleToggle() - { - $this->assertFalse($this->state->isCollecting()); - - $this->state->setIsCollecting(true); - $this->assertTrue($this->state->isCollecting()); - - $this->state->setIsCollecting(false); - $this->assertFalse($this->state->isCollecting()); - - $this->state->setIsCollecting(true); - $this->assertTrue($this->state->isCollecting()); - } -} diff --git a/app/code/Magento/SalesRule/Test/Unit/Observer/ClearProductAttributesCacheObserverTest.php b/app/code/Magento/SalesRule/Test/Unit/Observer/ClearProductAttributesCacheObserverTest.php new file mode 100644 index 0000000000000..904b642b0731b --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Unit/Observer/ClearProductAttributesCacheObserverTest.php @@ -0,0 +1,44 @@ +cache = $this->createMock(CacheInterface::class); + $this->observer = new ClearProductAttributesCacheObserver($this->cache); + } + + public function testExecuteClearsCache() + { + $event = $this->createMock(Observer::class); + + $this->cache->expects($this->once()) + ->method('remove') + ->with('salesrule_active_product_attributes'); + + $this->observer->execute($event); + } +} diff --git a/app/code/Magento/SalesRule/etc/di.xml b/app/code/Magento/SalesRule/etc/di.xml index 2be44da6494ac..cd841b36338e4 100644 --- a/app/code/Magento/SalesRule/etc/di.xml +++ b/app/code/Magento/SalesRule/etc/di.xml @@ -54,9 +54,6 @@ - - - diff --git a/app/code/Magento/SalesRule/etc/events.xml b/app/code/Magento/SalesRule/etc/events.xml index f430ef2ee8758..20e448a5ef3a1 100644 --- a/app/code/Magento/SalesRule/etc/events.xml +++ b/app/code/Magento/SalesRule/etc/events.xml @@ -32,9 +32,11 @@ + +