From 2256b0045d4bd77521e435119d643adc9533205a Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Wed, 11 Sep 2024 00:25:46 -0500 Subject: [PATCH 01/42] ACP2E-3334: [Internal] Fixture apply failure is not shown during execution or in logs --- .../TestFramework/Event/TestPreparedSubscriber.php | 4 ++++ .../Event/TestPreprationStartedSubscriber.php | 12 ++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php b/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php index 8a03af2d84ad..0c383933abcb 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php @@ -40,5 +40,9 @@ public function notify(Prepared $event): void $testObj->markTestSkipped($skipConfig['skipMessage']); } Magento::setTestPrepared(true); + Magento::setCurrentEventObject($event); + + $phpUnit = $objectManager->create(PhpUnit::class); + $phpUnit->startTest($testObj); } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreprationStartedSubscriber.php b/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreprationStartedSubscriber.php index 7e8dfcd7addc..cd23cd29e95e 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreprationStartedSubscriber.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreprationStartedSubscriber.php @@ -26,11 +26,11 @@ public function notify(PreparationStarted $event): void $className = $event->test()->className(); $methodName = $event->test()->methodName(); - $testObj = $objectManager->create($className, ['name' => $methodName]); - - Magento::setCurrentEventObject($event); - - $phpUnit = $objectManager->create(PhpUnit::class); - $phpUnit->startTest($testObj); +// $testObj = $objectManager->create($className, ['name' => $methodName]); +// +// Magento::setCurrentEventObject($event); +// +// $phpUnit = $objectManager->create(PhpUnit::class); +// $phpUnit->startTest($testObj); } } From fd43cdb8346fb689fa11dbee1721980eb2a3d990 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Wed, 11 Sep 2024 13:58:34 -0500 Subject: [PATCH 02/42] ACP2E-3334: [Internal] Fixture apply failure is not shown during execution or in logs --- .../Annotation/AbstractDataFixture.php | 6 ++++-- .../Annotation/ExceptionHandler.php | 16 ++++++++++++++-- .../Event/TestPreparedSubscriber.php | 4 ---- .../Event/TestPreprationStartedSubscriber.php | 12 ++++++------ 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php index dda31dd2cedd..35df3645a588 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php @@ -127,7 +127,8 @@ protected function _applyFixtures(array $fixtures, TestCase $test) 'Unable to apply fixture: ' . $this->getFixtureReference($fixture), $fixture['test']['class'], $fixture['test']['method'], - $exception + $exception, + $test ); } $this->_appliedFixtures[] = $fixture; @@ -157,7 +158,8 @@ protected function _revertFixtures(?TestCase $test = null) 'Unable to revert fixture: ' . $this->getFixtureReference($fixture), $fixture['test']['class'], $fixture['test']['method'], - $exception + $exception, + $test ); } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/ExceptionHandler.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/ExceptionHandler.php index 10d7132eb529..6bc60a7dfd00 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/ExceptionHandler.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/ExceptionHandler.php @@ -8,6 +8,7 @@ namespace Magento\TestFramework\Annotation; use PHPUnit\Framework\Exception; +use PHPUnit\Framework\TestCase; use ReflectionClass; use ReflectionException; @@ -20,13 +21,15 @@ class ExceptionHandler * @param string $testClass * @param string|null $testMethod * @param \Throwable|null $previous + * @param TestCase|null $test * @return void */ public static function handle( string $message, string $testClass, string $testMethod = null, - \Throwable $previous = null + \Throwable $previous = null, + TestCase $test = null ): void { try { $reflected = new ReflectionClass($testClass); @@ -72,7 +75,7 @@ public static function handle( . $exception->getTraceAsString(); } while ($exception = $exception->getPrevious()); } - throw new Exception( + $exception = new Exception( sprintf( "%s\n#0 %s%s", $message, @@ -82,5 +85,14 @@ public static function handle( 0, $previous ); + if ($test) { + \PHPUnit\Event\Facade::emitter()->testErrored( + $test->valueObjectForEvents(), + \PHPUnit\Event\Code\ThrowableBuilder::from($exception), + ); + $test::fail($exception->getMessage()); + } else { + throw $exception; + } } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php b/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php index 0c383933abcb..8a03af2d84ad 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php @@ -40,9 +40,5 @@ public function notify(Prepared $event): void $testObj->markTestSkipped($skipConfig['skipMessage']); } Magento::setTestPrepared(true); - Magento::setCurrentEventObject($event); - - $phpUnit = $objectManager->create(PhpUnit::class); - $phpUnit->startTest($testObj); } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreprationStartedSubscriber.php b/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreprationStartedSubscriber.php index cd23cd29e95e..7e8dfcd7addc 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreprationStartedSubscriber.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreprationStartedSubscriber.php @@ -26,11 +26,11 @@ public function notify(PreparationStarted $event): void $className = $event->test()->className(); $methodName = $event->test()->methodName(); -// $testObj = $objectManager->create($className, ['name' => $methodName]); -// -// Magento::setCurrentEventObject($event); -// -// $phpUnit = $objectManager->create(PhpUnit::class); -// $phpUnit->startTest($testObj); + $testObj = $objectManager->create($className, ['name' => $methodName]); + + Magento::setCurrentEventObject($event); + + $phpUnit = $objectManager->create(PhpUnit::class); + $phpUnit->startTest($testObj); } } From 32188b07b027dcdb5676519671391c59d1e9048c Mon Sep 17 00:00:00 2001 From: Alexandru Plapana Date: Thu, 5 Dec 2024 15:49:59 +0200 Subject: [PATCH 03/42] ACP2E-3472: [CLOUD] Shipping calculation is not considering the shopping cart rule --- .../Sales/Total/Quote/CommonTaxCollector.php | 28 ++++++++++++++----- .../Tax/Model/Sales/Total/Quote/Tax.php | 26 +++++++++++------ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php index 07c9aebb3e1d..0786e5644adf 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php @@ -1,7 +1,7 @@ quoteDetailsItemDataObjectFactory = $quoteDetailsItemDataObjectFactory; $this->customerAddressFactory = $customerAddressFactory; $this->customerAddressRegionFactory = $customerAddressRegionFactory; + $this->regionFactory = $regionInterfaceFactory; $this->taxHelper = $taxHelper ?: ObjectManager::getInstance()->get(TaxHelper::class); $this->quoteDetailsItemExtensionFactory = $quoteDetailsItemExtensionInterfaceFactory ?: ObjectManager::getInstance()->get(QuoteDetailsItemExtensionInterfaceFactory::class); @@ -215,6 +226,9 @@ public function mapAddress(QuoteAddress $address) $customerAddress->setRegion( $this->customerAddressRegionFactory->create()->setRegionId($address->getRegionId()) ); + $region = $this->regionFactory->create()->setRegionCode($address->getRegionCode()); + $region->setRegion($address->getRegion()); + $customerAddress->setRegion($region); $customerAddress->setPostcode($address->getPostcode()); $customerAddress->setCity($address->getCity()); $customerAddress->setStreet($address->getStreet()); diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php index 941367bc6017..4bc68d4dbf91 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php @@ -6,13 +6,20 @@ namespace Magento\Tax\Model\Sales\Total\Quote; use Magento\Customer\Api\Data\AddressInterfaceFactory as CustomerAddressFactory; +use Magento\Customer\Api\Data\RegionInterfaceFactory; use Magento\Customer\Api\Data\RegionInterfaceFactory as CustomerAddressRegionFactory; use Magento\Framework\App\ObjectManager; use Magento\Framework\Serialize\Serializer\Json; use Magento\Quote\Api\Data\ShippingAssignmentInterface; use Magento\Quote\Model\Quote\Address; +use Magento\Tax\Api\Data\QuoteDetailsInterfaceFactory; +use Magento\Tax\Api\Data\QuoteDetailsItemInterfaceFactory; use Magento\Tax\Api\Data\TaxClassKeyInterface; +use Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory; +use Magento\Tax\Api\TaxCalculationInterface; +use Magento\Tax\Helper\Data; use Magento\Tax\Model\Calculation; +use Magento\Tax\Model\Config; /** * Tax totals calculation model @@ -52,15 +59,16 @@ class Tax extends CommonTaxCollector /** * Class constructor * - * @param \Magento\Tax\Model\Config $taxConfig - * @param \Magento\Tax\Api\TaxCalculationInterface $taxCalculationService - * @param \Magento\Tax\Api\Data\QuoteDetailsInterfaceFactory $quoteDetailsDataObjectFactory - * @param \Magento\Tax\Api\Data\QuoteDetailsItemInterfaceFactory $quoteDetailsItemDataObjectFactory - * @param \Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory $taxClassKeyDataObjectFactory + * @param Config $taxConfig + * @param TaxCalculationInterface $taxCalculationService + * @param QuoteDetailsInterfaceFactory $quoteDetailsDataObjectFactory + * @param QuoteDetailsItemInterfaceFactory $quoteDetailsItemDataObjectFactory + * @param TaxClassKeyInterfaceFactory $taxClassKeyDataObjectFactory * @param CustomerAddressFactory $customerAddressFactory * @param CustomerAddressRegionFactory $customerAddressRegionFactory - * @param \Magento\Tax\Helper\Data $taxData - * @param Json $serializer + * @param CustomerAddressRegionFactory $regionInterfaceFactory + * @param Data $taxData + * @param Json|null $serializer */ public function __construct( \Magento\Tax\Model\Config $taxConfig, @@ -70,6 +78,7 @@ public function __construct( \Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory $taxClassKeyDataObjectFactory, CustomerAddressFactory $customerAddressFactory, CustomerAddressRegionFactory $customerAddressRegionFactory, + RegionInterfaceFactory $regionInterfaceFactory, \Magento\Tax\Helper\Data $taxData, Json $serializer = null ) { @@ -83,7 +92,8 @@ public function __construct( $quoteDetailsItemDataObjectFactory, $taxClassKeyDataObjectFactory, $customerAddressFactory, - $customerAddressRegionFactory + $customerAddressRegionFactory, + $regionInterfaceFactory ); } From 70f247df3264ff07e27bb29717a7f4bdd9eac221 Mon Sep 17 00:00:00 2001 From: Alexandru Plapana Date: Tue, 10 Dec 2024 09:57:09 +0200 Subject: [PATCH 04/42] ACP2E-3472: [CLOUD] Shipping calculation is not considering the shopping cart rule --- .../Sales/Total/Quote/CommonTaxCollector.php | 24 +++++++---------- .../Tax/Model/Sales/Total/Quote/Tax.php | 26 ++++++------------- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php index 0786e5644adf..4c8f735d02a1 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php @@ -3,13 +3,11 @@ * Copyright 2014 Adobe * All Rights Reserved. */ - namespace Magento\Tax\Model\Sales\Total\Quote; use Magento\Customer\Api\AccountManagementInterface as CustomerAccountManagement; use Magento\Customer\Api\Data\AddressInterfaceFactory as CustomerAddressFactory; use Magento\Customer\Api\Data\AddressInterface as CustomerAddress; -use Magento\Customer\Api\Data\RegionInterfaceFactory; use Magento\Customer\Api\Data\RegionInterfaceFactory as CustomerAddressRegionFactory; use Magento\Quote\Model\Quote\Address as QuoteAddress; use Magento\Quote\Model\Quote\Address\Total\AbstractTotal; @@ -27,7 +25,6 @@ use Magento\Tax\Api\TaxCalculationInterface; use Magento\Tax\Helper\Data as TaxHelper; use Magento\Framework\App\ObjectManager; -use Magento\Tax\Api\Data\QuoteDetailsItemExtensionInterface; use Magento\Tax\Api\Data\QuoteDetailsItemExtensionInterfaceFactory; use Magento\Tax\Model\Config; @@ -130,11 +127,6 @@ class CommonTaxCollector extends AbstractTotal */ protected $customerAddressRegionFactory; - /** - * @var RegionInterfaceFactory - */ - private RegionInterfaceFactory $regionFactory; - /** * @var \Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory */ @@ -170,7 +162,6 @@ class CommonTaxCollector extends AbstractTotal * @param TaxClassKeyInterfaceFactory $taxClassKeyDataObjectFactory * @param CustomerAddressFactory $customerAddressFactory * @param CustomerAddressRegionFactory $customerAddressRegionFactory - * @param RegionInterfaceFactory $regionInterfaceFactory * @param TaxHelper|null $taxHelper * @param QuoteDetailsItemExtensionInterfaceFactory|null $quoteDetailsItemExtensionInterfaceFactory * @param CustomerAccountManagement|null $customerAccountManagement @@ -184,7 +175,6 @@ public function __construct( \Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory $taxClassKeyDataObjectFactory, CustomerAddressFactory $customerAddressFactory, CustomerAddressRegionFactory $customerAddressRegionFactory, - RegionInterfaceFactory $regionInterfaceFactory, TaxHelper $taxHelper = null, QuoteDetailsItemExtensionInterfaceFactory $quoteDetailsItemExtensionInterfaceFactory = null, ?CustomerAccountManagement $customerAccountManagement = null @@ -196,7 +186,6 @@ public function __construct( $this->quoteDetailsItemDataObjectFactory = $quoteDetailsItemDataObjectFactory; $this->customerAddressFactory = $customerAddressFactory; $this->customerAddressRegionFactory = $customerAddressRegionFactory; - $this->regionFactory = $regionInterfaceFactory; $this->taxHelper = $taxHelper ?: ObjectManager::getInstance()->get(TaxHelper::class); $this->quoteDetailsItemExtensionFactory = $quoteDetailsItemExtensionInterfaceFactory ?: ObjectManager::getInstance()->get(QuoteDetailsItemExtensionInterfaceFactory::class); @@ -223,11 +212,16 @@ public function mapAddress(QuoteAddress $address) { $customerAddress = $this->customerAddressFactory->create(); $customerAddress->setCountryId($address->getCountryId()); - $customerAddress->setRegion( - $this->customerAddressRegionFactory->create()->setRegionId($address->getRegionId()) + $region = $this->customerAddressRegionFactory->create( + [ + 'data' => + [ + 'region_id' => $address->getRegionId(), + 'region_code' => $address->getRegionCode(), + 'region' => $address->getRegion() + ] + ] ); - $region = $this->regionFactory->create()->setRegionCode($address->getRegionCode()); - $region->setRegion($address->getRegion()); $customerAddress->setRegion($region); $customerAddress->setPostcode($address->getPostcode()); $customerAddress->setCity($address->getCity()); diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php index 4bc68d4dbf91..941367bc6017 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php @@ -6,20 +6,13 @@ namespace Magento\Tax\Model\Sales\Total\Quote; use Magento\Customer\Api\Data\AddressInterfaceFactory as CustomerAddressFactory; -use Magento\Customer\Api\Data\RegionInterfaceFactory; use Magento\Customer\Api\Data\RegionInterfaceFactory as CustomerAddressRegionFactory; use Magento\Framework\App\ObjectManager; use Magento\Framework\Serialize\Serializer\Json; use Magento\Quote\Api\Data\ShippingAssignmentInterface; use Magento\Quote\Model\Quote\Address; -use Magento\Tax\Api\Data\QuoteDetailsInterfaceFactory; -use Magento\Tax\Api\Data\QuoteDetailsItemInterfaceFactory; use Magento\Tax\Api\Data\TaxClassKeyInterface; -use Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory; -use Magento\Tax\Api\TaxCalculationInterface; -use Magento\Tax\Helper\Data; use Magento\Tax\Model\Calculation; -use Magento\Tax\Model\Config; /** * Tax totals calculation model @@ -59,16 +52,15 @@ class Tax extends CommonTaxCollector /** * Class constructor * - * @param Config $taxConfig - * @param TaxCalculationInterface $taxCalculationService - * @param QuoteDetailsInterfaceFactory $quoteDetailsDataObjectFactory - * @param QuoteDetailsItemInterfaceFactory $quoteDetailsItemDataObjectFactory - * @param TaxClassKeyInterfaceFactory $taxClassKeyDataObjectFactory + * @param \Magento\Tax\Model\Config $taxConfig + * @param \Magento\Tax\Api\TaxCalculationInterface $taxCalculationService + * @param \Magento\Tax\Api\Data\QuoteDetailsInterfaceFactory $quoteDetailsDataObjectFactory + * @param \Magento\Tax\Api\Data\QuoteDetailsItemInterfaceFactory $quoteDetailsItemDataObjectFactory + * @param \Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory $taxClassKeyDataObjectFactory * @param CustomerAddressFactory $customerAddressFactory * @param CustomerAddressRegionFactory $customerAddressRegionFactory - * @param CustomerAddressRegionFactory $regionInterfaceFactory - * @param Data $taxData - * @param Json|null $serializer + * @param \Magento\Tax\Helper\Data $taxData + * @param Json $serializer */ public function __construct( \Magento\Tax\Model\Config $taxConfig, @@ -78,7 +70,6 @@ public function __construct( \Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory $taxClassKeyDataObjectFactory, CustomerAddressFactory $customerAddressFactory, CustomerAddressRegionFactory $customerAddressRegionFactory, - RegionInterfaceFactory $regionInterfaceFactory, \Magento\Tax\Helper\Data $taxData, Json $serializer = null ) { @@ -92,8 +83,7 @@ public function __construct( $quoteDetailsItemDataObjectFactory, $taxClassKeyDataObjectFactory, $customerAddressFactory, - $customerAddressRegionFactory, - $regionInterfaceFactory + $customerAddressRegionFactory ); } From 8c4a0a7a39d726b9de20f908563e6921fa364990 Mon Sep 17 00:00:00 2001 From: Alexandru Plapana Date: Tue, 10 Dec 2024 14:07:19 +0200 Subject: [PATCH 05/42] ACP2E-3472: [CLOUD] Shipping calculation is not considering the shopping cart rule --- .../Sales/Total/Quote/CommonTaxCollector.php | 31 +-- .../Total/Quote/CommonTaxCollectorTest.php | 179 ++++++++++++++---- 2 files changed, 157 insertions(+), 53 deletions(-) diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php index 4c8f735d02a1..70d3fb686556 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php @@ -3,6 +3,8 @@ * Copyright 2014 Adobe * All Rights Reserved. */ +declare(strict_types=1); + namespace Magento\Tax\Model\Sales\Total\Quote; use Magento\Customer\Api\AccountManagementInterface as CustomerAccountManagement; @@ -210,24 +212,27 @@ public function _resetState(): void */ public function mapAddress(QuoteAddress $address) { - $customerAddress = $this->customerAddressFactory->create(); - $customerAddress->setCountryId($address->getCountryId()); $region = $this->customerAddressRegionFactory->create( [ - 'data' => - [ - 'region_id' => $address->getRegionId(), - 'region_code' => $address->getRegionCode(), - 'region' => $address->getRegion() - ] + 'data' => [ + 'region_id' => $address->getRegionId(), + 'region_code' => $address->getRegionCode(), + 'region' => $address->getRegion() + ] ] ); - $customerAddress->setRegion($region); - $customerAddress->setPostcode($address->getPostcode()); - $customerAddress->setCity($address->getCity()); - $customerAddress->setStreet($address->getStreet()); - return $customerAddress; + return $this->customerAddressFactory->create( + [ + 'data' => [ + 'country_id' => $address->getCountryId(), + 'region' => $region, + 'postcode' => $address->getPostcode(), + 'city' => $address->getCity(), + 'street' => $address->getStreet() + ] + ] + ); } /** diff --git a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php index 58c048fd771e..da0e4200411a 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php @@ -1,13 +1,17 @@ taxConfig = $this->getMockBuilder(Config::class) ->disableOriginalConstructor() ->onlyMethods(['getShippingTaxClass', 'shippingPriceIncludesTax', 'discountTax']) @@ -117,8 +153,13 @@ protected function setUp(): void ->method('getQuote') ->willReturn($this->quote); $methods = ['create']; - $this->quoteDetailsItemDataObject = $objectManager->getObject(ItemDetails::class); - $this->taxClassKeyDataObject = $objectManager->getObject(TaxClassKey::class); + $this->quoteDetailsItemDataObject = $this->createMock(ItemDetails::class); + $this->quoteDetailsItemDataObject->method('setType')->willReturnSelf(); + $this->quoteDetailsItemDataObject->method('setCode')->willReturnSelf(); + $this->quoteDetailsItemDataObject->method('setQuantity')->willReturnSelf(); + $this->taxClassKeyDataObject = $this->createMock(TaxClassKey::class); + $this->taxClassKeyDataObject->method('setType')->willReturnSelf(); + $this->taxClassKeyDataObject->method('setValue')->willReturnSelf(); $this->quoteDetailsItemDataObjectFactoryMock = $this->createPartialMock(QuoteDetailsItemInterfaceFactory::class, $methods); $this->quoteDetailsItemDataObjectFactoryMock @@ -132,15 +173,78 @@ protected function setUp(): void $this->taxHelper = $this->getMockBuilder(TaxHelper::class) ->disableOriginalConstructor() ->getMock(); - $this->commonTaxCollector = $objectManager->getObject( - CommonTaxCollector::class, - [ - 'taxConfig' => $this->taxConfig, - 'quoteDetailsItemDataObjectFactory' => $this->quoteDetailsItemDataObjectFactoryMock, - 'taxClassKeyDataObjectFactory' => $this->taxClassKeyDataObjectFactoryMock, - 'taxHelper' => $this->taxHelper, - ] + $this->taxCalculation = $this->createMock(TaxCalculationInterface::class); + $this->quoteDetailsFactory = $this->createMock(QuoteDetailsInterfaceFactory::class); + $this->addressFactory = $this->createMock(AddressInterfaceFactory::class); + $this->regionFactory = $this->createMock(RegionInterfaceFactory::class); + $this->quoteDetailsItemExtensionFactory = $this->createMock(QuoteDetailsItemExtensionInterfaceFactory::class); + $this->accountManagement = $this->createMock(AccountManagementInterface::class); + $this->commonTaxCollector = new CommonTaxCollector( + $this->taxConfig, + $this->taxCalculation, + $this->quoteDetailsFactory, + $this->quoteDetailsItemDataObjectFactoryMock, + $this->taxClassKeyDataObjectFactoryMock, + $this->addressFactory, + $this->regionFactory, + $this->taxHelper, + $this->quoteDetailsItemExtensionFactory, + $this->accountManagement ); + + parent::setUp(); + } + + /** + * @return void + * @throws Exception + */ + public function testMapAddress(): void + { + $countryId = 1; + $regionId = 2; + $regionCode = 'regionCode'; + $region = 'region'; + $postCode = 'postCode'; + $city = 'city'; + $street = ['street']; + + $address = $this->createMock(QuoteAddress::class); + $address->expects($this->once())->method('getCountryId')->willReturn($countryId); + $address->expects($this->once())->method('getRegionId')->willReturn($regionId); + $address->expects($this->once())->method('getRegionCode')->willReturn($regionCode); + $address->expects($this->once())->method('getRegion')->willReturn($region); + $address->expects($this->once())->method('getPostcode')->willReturn($postCode); + $address->expects($this->once())->method('getCity')->willReturn($city); + $address->expects($this->once())->method('getStreet')->willReturn($street); + + $regionData = [ + 'data' => [ + 'region_id' => $regionId, + 'region_code' => $regionCode, + 'region' => $region, + ] + ]; + $regionObject = $this->createMock(RegionInterface::class); + $this->regionFactory->expects($this->once())->method('create')->with($regionData)->willReturn($regionObject); + $customerAddress = $this->createMock(AddressInterface::class); + + $this->addressFactory->expects($this->once()) + ->method('create') + ->with( + [ + 'data' => [ + 'country_id' => $countryId, + 'region' => $regionObject, + 'postcode' => $postCode, + 'city' => $city, + 'street' => $street + ] + ] + ) + ->willReturn($customerAddress); + + $this->assertSame($customerAddress, $this->commonTaxCollector->mapAddress($address)); } /** @@ -153,12 +257,13 @@ protected function setUp(): void * * @return void * @dataProvider getShippingDataObjectDataProvider + * @throws Exception */ public function testGetShippingDataObject( array $addressData, - $useBaseCurrency, - $shippingTaxClass, - $shippingPriceInclTax + bool $useBaseCurrency, + string $shippingTaxClass, + bool $shippingPriceInclTax ): void { $shippingAssignmentMock = $this->getMockForAbstractClass(ShippingAssignmentInterface::class); /** @var MockObject|QuoteAddressTotal $totalsMock */ @@ -201,10 +306,8 @@ public function testGetShippingDataObject( ->expects($this->once()) ->method('getBaseShippingDiscountAmount') ->willReturn($baseShippingAmount); - $expectedDiscountAmount = $baseShippingAmount; } else { $totalsMock->expects($this->never())->method('getBaseShippingDiscountAmount'); - $expectedDiscountAmount = $shippingAmount; } } foreach ($addressData as $key => $value) { @@ -214,10 +317,6 @@ public function testGetShippingDataObject( $this->quoteDetailsItemDataObject, $this->commonTaxCollector->getShippingDataObject($shippingAssignmentMock, $totalsMock, $useBaseCurrency) ); - - if ($shippingAmount) { - $this->assertEquals($expectedDiscountAmount, $this->quoteDetailsItemDataObject->getDiscountAmount()); - } } /** From 66a0a3d8cc5947a39ca0e216c2143bccbb7c518c Mon Sep 17 00:00:00 2001 From: Alexandru Plapana Date: Tue, 10 Dec 2024 15:18:47 +0200 Subject: [PATCH 06/42] ACP2E-3472: [CLOUD] Shipping calculation is not considering the shopping cart rule --- .../Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php index da0e4200411a..892e535377f5 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php @@ -37,6 +37,7 @@ use PHPUnit\Framework\TestCase; /** + * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CommonTaxCollectorTest extends TestCase From 313a8cdbdf083d0dd32669a9e6f94556b9dc7ea4 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Tue, 10 Dec 2024 14:09:33 -0600 Subject: [PATCH 07/42] ACP2E-3505: Disabled product still appears in related, upsell, crosssell items in grpahQL query --- .../Model/Resolver/Batch/AbstractLikedProducts.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php index f35af6f4885d..ee0afe51cab0 100644 --- a/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php +++ b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php @@ -101,7 +101,9 @@ private function findRelations(array $products, array $loadAttributes, int $link $relatedProducts = []; /** @var \Magento\Catalog\Api\Data\ProductInterface $item */ foreach ($relatedSearchResult->getItems() as $item) { - $relatedProducts[$item->getId()] = $item; + if ($item->isAvailable()) { + $relatedProducts[$item->getId()] = $item; + } } //Matching products with related products. From 8ed2a9a81470a24f0a84271d037f80867ceee91f Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Tue, 10 Dec 2024 16:50:55 -0600 Subject: [PATCH 08/42] ACP2E-3505: Disabled product still appears in related, upsell, crosssell items in grpahQL query --- .../Model/Resolver/Batch/AbstractLikedProducts.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php index ee0afe51cab0..4999a7abb110 100644 --- a/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php +++ b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php @@ -1,7 +1,7 @@ Date: Wed, 11 Dec 2024 13:43:31 -0600 Subject: [PATCH 09/42] ACP2E-3505: Disabled product still appears in related, upsell, crosssell items in grpahQL query --- .../RelatedProduct/GetRelatedProductsTest.php | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php index 2bcee4e16851..068d02601091 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php @@ -83,6 +83,40 @@ public function testQueryDisableRelatedProduct() self::assertCount(0, $relatedProducts); } + /** + * @magentoApiDataFixture Magento/Catalog/_files/products_related_disabled.php + * @magentoConfigFixture default/cataloginventory/options/show_out_of_stock 1 + */ + public function testQueryDisableRelatedProductWithShowOutOfStock() + { + $productSku = 'simple_with_cross'; + + $query = <<graphQlQuery($query); + + self::assertArrayHasKey('products', $response); + self::assertArrayHasKey('items', $response['products']); + self::assertCount(1, $response['products']['items']); + self::assertArrayHasKey(0, $response['products']['items']); + self::assertArrayHasKey('related_products', $response['products']['items'][0]); + $relatedProducts = $response['products']['items'][0]['related_products']; + self::assertCount(0, $relatedProducts); + } + /** * @magentoApiDataFixture Magento/Catalog/_files/products_crosssell.php */ From 364318572c023be212c999a981da1925df2f5ccc Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Wed, 11 Dec 2024 13:46:00 -0600 Subject: [PATCH 10/42] ACP2E-3505: Disabled product still appears in related, upsell, crosssell items in grpahQL query --- .../Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php index 068d02601091..221c5a476012 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php @@ -1,7 +1,7 @@ Date: Thu, 12 Dec 2024 10:26:03 +0200 Subject: [PATCH 11/42] ACP2E-3472: [CLOUD] Shipping calculation is not considering the shopping cart rule --- .../view/frontend/web/js/model/shipping-rates-validator.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js index 6cc7849683a3..f2151590376a 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js @@ -84,6 +84,8 @@ define([ $.each(elements, function (index, field) { uiRegistry.async(formPath + '.' + field)(self.doElementBinding.bind(self)); }); + let regionId = uiRegistry.async(formPath + '.region_id'); + this.bindHandler(regionId(), self.validateDelay); }, /** @@ -199,7 +201,7 @@ define([ address; if (this.validateAddressData(addressFlat)) { - addressFlat = uiRegistry.get('checkoutProvider').shippingAddress; + addressFlat = uiRegistry.get('checkoutProvider').shippingAddress; // this is how the triggering is performed. Need to trigger this when selecting region address = addressConverter.formAddressDataToQuoteAddress(addressFlat); selectShippingAddress(address); } else { From 24272fa54d38063dbc4c44db080b295ff5679e10 Mon Sep 17 00:00:00 2001 From: Alexandru Plapana Date: Thu, 12 Dec 2024 10:34:54 +0200 Subject: [PATCH 12/42] ACP2E-3472: [CLOUD] Shipping calculation is not considering the shopping cart rule --- .../view/frontend/web/js/model/shipping-rates-validator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js index f2151590376a..8ce9fef27928 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js @@ -201,7 +201,7 @@ define([ address; if (this.validateAddressData(addressFlat)) { - addressFlat = uiRegistry.get('checkoutProvider').shippingAddress; // this is how the triggering is performed. Need to trigger this when selecting region + addressFlat = uiRegistry.get('checkoutProvider').shippingAddress; address = addressConverter.formAddressDataToQuoteAddress(addressFlat); selectShippingAddress(address); } else { From 208ec78e5b9c80e313371b38dcdab87d214aef3f Mon Sep 17 00:00:00 2001 From: Alexandru Plapana Date: Thu, 12 Dec 2024 11:01:47 +0200 Subject: [PATCH 13/42] ACP2E-3472: [CLOUD] Shipping calculation is not considering the shopping cart rule --- .../view/frontend/web/js/model/shipping-rates-validator.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js index 8ce9fef27928..2906dbb70b95 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2014 Adobe + * All Rights Reserved. */ /** From e854c25ba034e577e0395ba5c47208c4ce7b6b62 Mon Sep 17 00:00:00 2001 From: Alexandru Plapana Date: Thu, 12 Dec 2024 11:55:18 +0200 Subject: [PATCH 14/42] ACP2E-3472: [CLOUD] Shipping calculation is not considering the shopping cart rule --- .../view/frontend/web/js/model/shipping-rates-validator.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js index 2906dbb70b95..2970193ba1c9 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js @@ -85,6 +85,7 @@ define([ uiRegistry.async(formPath + '.' + field)(self.doElementBinding.bind(self)); }); let regionId = uiRegistry.async(formPath + '.region_id'); + this.bindHandler(regionId(), self.validateDelay); }, From cad3806a1a34dd64a87c396fe3b34090ffde03c2 Mon Sep 17 00:00:00 2001 From: Alexandru Plapana Date: Thu, 12 Dec 2024 16:57:06 +0200 Subject: [PATCH 15/42] ACP2E-3472: [CLOUD] Shipping calculation is not considering the shopping cart rule --- .../view/frontend/web/js/model/shipping-rates-validator.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js index 2970193ba1c9..767820b731e5 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js @@ -85,8 +85,9 @@ define([ uiRegistry.async(formPath + '.' + field)(self.doElementBinding.bind(self)); }); let regionId = uiRegistry.async(formPath + '.region_id'); - - this.bindHandler(regionId(), self.validateDelay); + if (regionId() !== undefined) { + this.bindHandler(regionId(), self.validateDelay); + } }, /** From 30589d7db282f7bf846d35766b96fea246d05d41 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Thu, 12 Dec 2024 09:54:06 -0600 Subject: [PATCH 16/42] ACP2E-3505: Disabled product still appears in related, upsell, crosssell items in grpahQL query --- .../RelatedProduct/GetRelatedProductsTest.php | 78 +------------------ 1 file changed, 2 insertions(+), 76 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php index 221c5a476012..65566f4eba72 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php @@ -1,7 +1,7 @@ graphQlQuery($query); - - self::assertArrayHasKey('products', $response); - self::assertArrayHasKey('items', $response['products']); - self::assertCount(1, $response['products']['items']); - self::assertArrayHasKey(0, $response['products']['items']); - self::assertArrayHasKey('related_products', $response['products']['items'][0]); - $relatedProducts = $response['products']['items'][0]['related_products']; - self::assertCount(0, $relatedProducts); - } - - /** - * @magentoApiDataFixture Magento/Catalog/_files/products_crosssell.php - */ - public function testQueryCrossSellProducts() - { - $productSku = 'simple_with_cross'; - - $query = <<graphQlQuery($query); - - self::assertArrayHasKey('products', $response); - self::assertArrayHasKey('items', $response['products']); - self::assertCount(1, $response['products']['items']); - self::assertArrayHasKey(0, $response['products']['items']); - self::assertArrayHasKey('crosssell_products', $response['products']['items'][0]); - $crossSellProducts = $response['products']['items'][0]['crosssell_products']; - self::assertCount(1, $crossSellProducts); - $crossSellProduct = $crossSellProducts[0]; - self::assertArrayHasKey('sku', $crossSellProduct); - self::assertArrayHasKey('name', $crossSellProduct); - self::assertArrayHasKey('url_key', $crossSellProduct); - self::assertEquals($crossSellProduct['sku'], 'simple'); - self::assertEquals($crossSellProduct['name'], 'Simple Cross Sell'); - self::assertEquals($crossSellProduct['url_key'], 'simple-cross-sell'); - } - /** * @magentoApiDataFixture Magento/Catalog/_files/products_upsell.php */ From e696967fda84c72a6d5d341b121828c3de3c777f Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Thu, 12 Dec 2024 09:55:30 -0600 Subject: [PATCH 17/42] ACP2E-3505: Disabled product still appears in related, upsell, crosssell items in grpahQL query --- .../RelatedProduct/GetRelatedProductsTest.php | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php index 65566f4eba72..2bcee4e16851 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php @@ -83,6 +83,46 @@ public function testQueryDisableRelatedProduct() self::assertCount(0, $relatedProducts); } + /** + * @magentoApiDataFixture Magento/Catalog/_files/products_crosssell.php + */ + public function testQueryCrossSellProducts() + { + $productSku = 'simple_with_cross'; + + $query = <<graphQlQuery($query); + + self::assertArrayHasKey('products', $response); + self::assertArrayHasKey('items', $response['products']); + self::assertCount(1, $response['products']['items']); + self::assertArrayHasKey(0, $response['products']['items']); + self::assertArrayHasKey('crosssell_products', $response['products']['items'][0]); + $crossSellProducts = $response['products']['items'][0]['crosssell_products']; + self::assertCount(1, $crossSellProducts); + $crossSellProduct = $crossSellProducts[0]; + self::assertArrayHasKey('sku', $crossSellProduct); + self::assertArrayHasKey('name', $crossSellProduct); + self::assertArrayHasKey('url_key', $crossSellProduct); + self::assertEquals($crossSellProduct['sku'], 'simple'); + self::assertEquals($crossSellProduct['name'], 'Simple Cross Sell'); + self::assertEquals($crossSellProduct['url_key'], 'simple-cross-sell'); + } + /** * @magentoApiDataFixture Magento/Catalog/_files/products_upsell.php */ From 0929769786563704ad729eb83b46d46d6efd44f0 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Thu, 12 Dec 2024 15:16:19 -0600 Subject: [PATCH 18/42] ACP2E-3505: Disabled product still appears in related, upsell, crosssell items in grpahQL query --- .../RelatedProduct/GetRelatedProductsTest.php | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php index 2bcee4e16851..d169002ab842 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php @@ -1,7 +1,7 @@ graphQlQuery($query); + + self::assertArrayHasKey('products', $response); + self::assertArrayHasKey('items', $response['products']); + self::assertCount(1, $response['products']['items']); + self::assertArrayHasKey(0, $response['products']['items']); + self::assertArrayHasKey('related_products', $response['products']['items'][0]); + $relatedProducts = $response['products']['items'][0]['related_products']; + self::assertCount(0, $relatedProducts); + } + /** * @magentoApiDataFixture Magento/Catalog/_files/products_related_disabled.php */ From cb75b81cf39d42cccb5fbc1520ff67ed9810c8d7 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Thu, 12 Dec 2024 17:21:52 -0600 Subject: [PATCH 19/42] ACP2E-3505: Disabled product still appears in related, upsell, crosssell items in grpahQL query --- .../GraphQl/RelatedProduct/GetRelatedProductsTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php index d169002ab842..63bbc70b13c9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php @@ -52,11 +52,11 @@ public function testQueryRelatedProducts() /** * @magentoApiDataFixture Magento/Catalog/_files/products_related_disabled.php - * @magentoConfigFixture current_store cataloginventory/options/show_out_of_stock 1 - * @magentoConfigFixture current_store catalog/magento_catalogpermissions/enabled 1 - * @magentoConfigFixture current_store catalog/magento_catalogpermissions/grant_catalog_category_view 1 - * @magentoConfigFixture current_store catalog/magento_catalogpermissions/grant_catalog_product_price 1 - * @magentoConfigFixture current_store catalog/magento_catalogpermissions/grant_checkout_items 1 + * @magentoConfigFixture default_store cataloginventory/options/show_out_of_stock 1 + * @magentoConfigFixture default_store catalog/magento_catalogpermissions/enabled 1 + * @magentoConfigFixture default_store catalog/magento_catalogpermissions/grant_catalog_category_view 1 + * @magentoConfigFixture default_store catalog/magento_catalogpermissions/grant_catalog_product_price 1 + * @magentoConfigFixture default_store catalog/magento_catalogpermissions/grant_checkout_items 1 */ public function testQueryDisableRelatedProductWithShowOutOfStock() { From b7bd82ff951c1e9a438e4d642c29d6f27a7b09e0 Mon Sep 17 00:00:00 2001 From: Alexandru Plapana Date: Fri, 13 Dec 2024 09:49:05 +0200 Subject: [PATCH 20/42] ACP2E-3472: [CLOUD] Shipping calculation is not considering the shopping cart rule --- .../view/frontend/web/js/model/shipping-rates-validator.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js index 767820b731e5..ead5e9fd32cf 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js @@ -85,6 +85,7 @@ define([ uiRegistry.async(formPath + '.' + field)(self.doElementBinding.bind(self)); }); let regionId = uiRegistry.async(formPath + '.region_id'); + if (regionId() !== undefined) { this.bindHandler(regionId(), self.validateDelay); } From 838a4481917497535101cb818e1a6c0a7399448d Mon Sep 17 00:00:00 2001 From: Olga Moyseyenko Date: Sat, 14 Dec 2024 21:58:57 -0600 Subject: [PATCH 21/42] ACP2E-3501: VAPT: Business Logic Error - future date as customer date of birth --- .../Magento/Customer/Model/Validator/Dob.php | 84 +++++++++++++++++++ app/code/Magento/Customer/etc/validation.xml | 10 ++- .../Customer/Api/AccountManagementTest.php | 48 ++++++++++- 3 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/Customer/Model/Validator/Dob.php diff --git a/app/code/Magento/Customer/Model/Validator/Dob.php b/app/code/Magento/Customer/Model/Validator/Dob.php new file mode 100644 index 000000000000..f6e2dec6ac4a --- /dev/null +++ b/app/code/Magento/Customer/Model/Validator/Dob.php @@ -0,0 +1,84 @@ +currentDate = new \DateTime(); + $this->storeManager = $storeManager; + } + + /** + * Validate name fields. + * + * @param Customer $customer + * @return bool + */ + public function isValid($customer) + { + if (!$this->isValidDob($customer->getDob(), $customer->getStoreId())) { + parent::_addMessages([['dob' => 'The Date of Birth should not be greater than today.']]); + } + + return count($this->_messages) == 0; + } + + /** + * Check if specified dob is not in the future + * + * @param string|null $dobValue + * @param int $storeId + * @return bool + */ + private function isValidDob($dobValue, $storeId) + { + if ($dobValue != null) { + + // Get the timezone of the store + $store = $this->storeManager->getStore($storeId); + $timezone = $store->getConfig('general/locale/timezone'); + + // Get the date of birth and set the time to 00:00:00 + $dobDate = new \DateTime($dobValue, new \DateTimeZone($timezone)); + $dobDate->setTime(0, 0, 0); + + // Get the timestamp of the date of birth and the current date + $dobTimestamp = $dobDate->getTimestamp(); + $currentTimestamp = $this->currentDate->getTimestamp(); + + // If the date's of birth first minute is in the future, return false - the day has not started yet + if ($dobTimestamp > $currentTimestamp) { + return false; + } + } + + return true; + } +} diff --git a/app/code/Magento/Customer/etc/validation.xml b/app/code/Magento/Customer/etc/validation.xml index 7fd6cfeb7947..5d6118170155 100644 --- a/app/code/Magento/Customer/etc/validation.xml +++ b/app/code/Magento/Customer/etc/validation.xml @@ -1,8 +1,8 @@ @@ -23,12 +23,18 @@ + + + + + + diff --git a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php index b7f3e5687a11..6612a6c2fc4f 100644 --- a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php @@ -1,7 +1,7 @@ [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'CreateAccount', + ], + ]; + + $customerDataArray = $this->dataObjectProcessor->buildOutputDataArray( + $this->customerHelper->createSampleCustomerDataObject(), + \Magento\Customer\Api\Data\CustomerInterface::class + ); + $futureDob = '14-12-2044'; + $customerDataArray['dob'] = $futureDob; + $requestData = ['customer' => $customerDataArray, 'password' => CustomerHelper::PASSWORD]; + try { + $this->_webApiCall($serviceInfo, $requestData); + $this->fail('Expected exception did not occur.'); + } catch (\Exception $e) { + if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) { + $expectedException = new InputException(); + $expectedException->addError(__('The Date of Birth should not be greater than today.')); + $this->assertInstanceOf('SoapFault', $e); + $this->checkSoapFault( + $e, + $expectedException->getRawMessage(), + 'env:Sender', + $expectedException->getParameters() // expected error parameters + ); + } else { + $this->assertEquals(HTTPExceptionCodes::HTTP_BAD_REQUEST, $e->getCode()); + $exceptionData = $this->processRestExceptionResult($e); + $expectedExceptionData = [ + 'message' => 'The Date of Birth should not be greater than today.', + ]; + $this->assertEquals($expectedExceptionData, $exceptionData); + } + } + } public function testCreateCustomerWithoutOptionalFields() { $serviceInfo = [ From 5f45aa07c433bd05330a5e98d4e6e1d2e96f43cf Mon Sep 17 00:00:00 2001 From: Olga Moyseyenko Date: Mon, 16 Dec 2024 16:18:48 -0600 Subject: [PATCH 22/42] ACP2E-3501: VAPT: Business Logic Error - future date as customer date of birth --- app/code/Magento/Customer/Model/Validator/Dob.php | 10 +++++----- .../Magento/Customer/Api/AccountManagementTest.php | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Customer/Model/Validator/Dob.php b/app/code/Magento/Customer/Model/Validator/Dob.php index f6e2dec6ac4a..1f3bbc3d9401 100644 --- a/app/code/Magento/Customer/Model/Validator/Dob.php +++ b/app/code/Magento/Customer/Model/Validator/Dob.php @@ -12,19 +12,19 @@ use Magento\Store\Model\StoreManagerInterface; /** - * Customer name fields validator. + * Customer dob field validator. */ class Dob extends AbstractValidator { /** * @var \DateTime */ - private $currentDate; + private \DateTime $currentDate; /** * @var StoreManagerInterface */ - private $storeManager; + private StoreManagerInterface $storeManager; /** * @param StoreManagerInterface $storeManager @@ -41,7 +41,7 @@ public function __construct(StoreManagerInterface $storeManager) * @param Customer $customer * @return bool */ - public function isValid($customer) + public function isValid($customer): bool { if (!$this->isValidDob($customer->getDob(), $customer->getStoreId())) { parent::_addMessages([['dob' => 'The Date of Birth should not be greater than today.']]); @@ -57,7 +57,7 @@ public function isValid($customer) * @param int $storeId * @return bool */ - private function isValidDob($dobValue, $storeId) + private function isValidDob(?string $dobValue, int $storeId): bool { if ($dobValue != null) { diff --git a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php index 6612a6c2fc4f..65a718f67b2d 100644 --- a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php @@ -240,7 +240,9 @@ public function testCreateCustomerWithDateOfBirthInFuture() $this->customerHelper->createSampleCustomerDataObject(), \Magento\Customer\Api\Data\CustomerInterface::class ); - $futureDob = '14-12-2044'; + $date = new \DateTime(); + $date->modify('+1 month'); + $futureDob = $date->format('Y-m-d'); $customerDataArray['dob'] = $futureDob; $requestData = ['customer' => $customerDataArray, 'password' => CustomerHelper::PASSWORD]; try { From 00956aff05b59bee877e95c46d92a2acdbf4a062 Mon Sep 17 00:00:00 2001 From: Olga Moyseyenko Date: Tue, 17 Dec 2024 15:02:25 -0600 Subject: [PATCH 23/42] ACP2E-3501: VAPT: Business Logic Error - future date as customer date of birth --- app/code/Magento/Customer/Model/Validator/Dob.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Model/Validator/Dob.php b/app/code/Magento/Customer/Model/Validator/Dob.php index 1f3bbc3d9401..222238e80290 100644 --- a/app/code/Magento/Customer/Model/Validator/Dob.php +++ b/app/code/Magento/Customer/Model/Validator/Dob.php @@ -41,7 +41,7 @@ public function __construct(StoreManagerInterface $storeManager) * @param Customer $customer * @return bool */ - public function isValid($customer): bool + public function isValid($customer) { if (!$this->isValidDob($customer->getDob(), $customer->getStoreId())) { parent::_addMessages([['dob' => 'The Date of Birth should not be greater than today.']]); From 00aaa80eaa38319bee2b5c43e78754413968b5f5 Mon Sep 17 00:00:00 2001 From: Olga Moyseyenko Date: Sun, 22 Dec 2024 10:28:21 -0600 Subject: [PATCH 24/42] ACP2E-3501: VAPT: Business Logic Error - future date as customer date of birth --- app/code/Magento/Customer/Model/Validator/Dob.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Customer/Model/Validator/Dob.php b/app/code/Magento/Customer/Model/Validator/Dob.php index 222238e80290..4dab713834aa 100644 --- a/app/code/Magento/Customer/Model/Validator/Dob.php +++ b/app/code/Magento/Customer/Model/Validator/Dob.php @@ -9,6 +9,7 @@ use Magento\Customer\Model\Customer; use Magento\Framework\Validator\AbstractValidator; +use Magento\Store\Api\Data\StoreInterface as StoreInterface; use Magento\Store\Model\StoreManagerInterface; /** @@ -41,7 +42,7 @@ public function __construct(StoreManagerInterface $storeManager) * @param Customer $customer * @return bool */ - public function isValid($customer) + public function isValid($customer): bool { if (!$this->isValidDob($customer->getDob(), $customer->getStoreId())) { parent::_addMessages([['dob' => 'The Date of Birth should not be greater than today.']]); @@ -54,13 +55,11 @@ public function isValid($customer) * Check if specified dob is not in the future * * @param string|null $dobValue - * @param int $storeId + * @param null|string|bool|int|StoreInterface $storeId * @return bool */ - private function isValidDob(?string $dobValue, int $storeId): bool + private function isValidDob(?string $dobValue, null|string|bool|int|StoreInterface $storeId): bool { - if ($dobValue != null) { - // Get the timezone of the store $store = $this->storeManager->getStore($storeId); $timezone = $store->getConfig('general/locale/timezone'); @@ -77,7 +76,6 @@ private function isValidDob(?string $dobValue, int $storeId): bool if ($dobTimestamp > $currentTimestamp) { return false; } - } return true; } From 713485ec12670b0798729325dfd92cb3dde8a3da Mon Sep 17 00:00:00 2001 From: Olga Moyseyenko Date: Sun, 22 Dec 2024 13:45:56 -0600 Subject: [PATCH 25/42] ACP2E-3501: VAPT: Business Logic Error - future date as customer date of birth --- app/code/Magento/Customer/Model/Validator/Dob.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Customer/Model/Validator/Dob.php b/app/code/Magento/Customer/Model/Validator/Dob.php index 4dab713834aa..1246c825e1cd 100644 --- a/app/code/Magento/Customer/Model/Validator/Dob.php +++ b/app/code/Magento/Customer/Model/Validator/Dob.php @@ -60,6 +60,8 @@ public function isValid($customer): bool */ private function isValidDob(?string $dobValue, null|string|bool|int|StoreInterface $storeId): bool { + if ($dobValue != null) { + // Get the timezone of the store $store = $this->storeManager->getStore($storeId); $timezone = $store->getConfig('general/locale/timezone'); @@ -76,6 +78,7 @@ private function isValidDob(?string $dobValue, null|string|bool|int|StoreInterfa if ($dobTimestamp > $currentTimestamp) { return false; } + } return true; } From 860169fbf43c0b3f87f1e681a4c5503a8ea0b2e0 Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk Date: Mon, 23 Dec 2024 11:27:01 -0600 Subject: [PATCH 26/42] ACP2E-3455: Duplicated URL key error message generated when importing a product is not correct when the URL Key already belongs to a category --- .../Model/Import/Product.php | 44 ++- .../Test/Unit/Model/Import/ProductTest.php | 319 ++++++++++++++---- 2 files changed, 290 insertions(+), 73 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 394bff167303..6d52d34eab88 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -1,7 +1,7 @@ 'Imported resource (image: %s) at row %s could not be downloaded from external resource due to timeout or access permissions', ValidatorInterface::ERROR_INVALID_WEIGHT => 'Product weight is invalid', ValidatorInterface::ERROR_DUPLICATE_URL_KEY => 'Url key: \'%s\' was already generated for an item with the SKU: \'%s\'. You need to specify the unique URL key manually', + self::ERROR_DUPLICATE_URL_KEY_BY_CATEGORY => 'Url key: \'%s\' was already generated for a %s with the ID: %s. You need to specify the unique URL key manually', ValidatorInterface::ERROR_DUPLICATE_MULTISELECT_VALUES => 'Value for multiselect attribute %s contains duplicated values', 'invalidNewToDateValue' => 'Make sure new_to_date is later than or the same as new_from_date', // Can't add new translated strings in patch release @@ -933,7 +936,7 @@ public function __construct( $this->stockProcessor = $stockProcessor ?: ObjectManager::getInstance() ->get(StockProcessor::class); $this->linkProcessor = $linkProcessor ?? ObjectManager::getInstance() - ->get(LinkProcessor::class); + ->get(LinkProcessor::class); $this->linkProcessor->addNameToIds($this->_linkNameToId); $this->hashAlgorithm = (version_compare(PHP_VERSION, '8.1.0') >= 0) ? 'xxh128' : 'crc32c'; parent::__construct( @@ -949,7 +952,7 @@ public function __construct( $this->_optionEntity = $data['option_entity'] ?? $optionFactory->create(['data' => ['product_entity' => $this]]); $this->skuStorage = $skuStorage ?? ObjectManager::getInstance() - ->get(SkuStorage::class); + ->get(SkuStorage::class); $this->_initAttributeSets() ->_initTypeModels() ->_initSkus() @@ -957,9 +960,9 @@ public function __construct( $this->validator->init($this); $this->dateTimeFactory = $dateTimeFactory ?? ObjectManager::getInstance()->get(DateTimeFactory::class); $this->productRepository = $productRepository ?? ObjectManager::getInstance() - ->get(ProductRepositoryInterface::class); + ->get(ProductRepositoryInterface::class); $this->stockItemProcessor = $stockItemProcessor ?? ObjectManager::getInstance() - ->get(StockItemProcessorInterface::class); + ->get(StockItemProcessorInterface::class); $this->fileDriver = $fileDriver ?? ObjectManager::getInstance() ->get(File::class); } @@ -3117,7 +3120,7 @@ protected function _saveValidatedBunches() } /** - * Check that url_keys are not assigned to other products in DB + * Check that url_keys are not already assigned to others entities in DB * * @return void * @since 100.0.3 @@ -3129,7 +3132,11 @@ protected function checkUrlKeyDuplicates() $urlKeyDuplicates = $this->_connection->fetchAssoc( $this->_connection->select()->from( ['url_rewrite' => $resource->getTable('url_rewrite')], - ['request_path', 'store_id'] + [ + 'request_path', + 'store_id', + 'entity_type' + ] )->joinLeft( ['cpe' => $resource->getTable('catalog_product_entity')], "cpe.entity_id = url_rewrite.entity_id" @@ -3137,13 +3144,24 @@ protected function checkUrlKeyDuplicates() ->where('store_id IN (?)', $storeId) ->where('cpe.sku not in (?)', array_values($urlKeys)) ); + foreach ($urlKeyDuplicates as $entityData) { $rowNum = $this->rowNumbers[$entityData['store_id']][$entityData['request_path']]; - $message = sprintf( - $this->retrieveMessageTemplate(ValidatorInterface::ERROR_DUPLICATE_URL_KEY), - $entityData['request_path'], - $entityData['sku'] - ); + if ($entityData['entity_type'] === 'category') { + $message = sprintf( + $this->retrieveMessageTemplate(self::ERROR_DUPLICATE_URL_KEY_BY_CATEGORY), + $entityData['request_path'], + $entityData['entity_type'], + $entityData['entity_id'], + ); + } else { + $message = sprintf( + $this->retrieveMessageTemplate(ValidatorInterface::ERROR_DUPLICATE_URL_KEY), + $entityData['request_path'], + $entityData['sku'] + ); + } + $this->addRowError(ValidatorInterface::ERROR_DUPLICATE_URL_KEY, $rowNum, 'url_key', $message); } } diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php index de7a4852ca58..a2e3ebcf29b9 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php @@ -1,7 +1,7 @@ _initSkus() ->_initImagesArrayKeys(); - $objectManager = new ObjectManager($this); + $this->objectManager = new ObjectManager($this); + $this->productPropertiesMap = [ + 'jsonHelper' => $this->jsonHelper, + 'importExportData' => $this->importExportData, + 'importData' => $this->_dataSourceModel, + 'config' => $this->config, + 'resource' => $this->resource, + 'resourceHelper' => $this->resourceHelper, + 'string' => $this->string, + 'errorAggregator' => $this->errorAggregator, + 'eventManager' => $this->_eventManager, + 'stockRegistry' => $this->stockRegistry, + 'stockConfiguration' => $this->stockConfiguration, + 'stockStateProvider' => $this->stockStateProvider, + 'catalogData' => $this->_catalogData, + 'importConfig' => $this->_importConfig, + 'resourceFactory' => $this->_resourceFactory, + 'optionFactory' => $this->optionFactory, + 'setColFactory' => $this->_setColFactory, + 'productTypeFactory' => $this->_productTypeFactory, + 'linkFactory' => $this->_linkFactory, + 'proxyProdFactory' => $this->_proxyProdFactory, + 'uploaderFactory' => $this->_uploaderFactory, + 'filesystem' => $this->_filesystem, + 'stockResItemFac' => $this->_stockResItemFac, + 'localeDate' => $this->_localeDate, + 'dateTime' => $this->dateTime, + 'logger' => $this->_logger, + 'indexerRegistry' => $this->indexerRegistry, + 'storeResolver' => $this->storeResolver, + 'skuProcessor' => $this->skuProcessor, + 'categoryProcessor' => $this->categoryProcessor, + 'validator' => $this->validator, + 'objectRelationProcessor' => $this->objectRelationProcessor, + 'transactionManager' => $this->transactionManager, + 'taxClassProcessor' => $this->taxClassProcessor, + 'scopeConfig' => $this->scopeConfig, + 'productUrl' => $this->productUrl, + 'data' => $this->data, + 'imageTypeProcessor' => $this->imageTypeProcessor, + 'skuStorage' => $this->skuStorageMock, + ]; - $this->importProduct = $objectManager->getObject( + $this->importProduct = $this->objectManager->getObject( Product::class, - [ - 'jsonHelper' => $this->jsonHelper, - 'importExportData' => $this->importExportData, - 'importData' => $this->_dataSourceModel, - 'config' => $this->config, - 'resource' => $this->resource, - 'resourceHelper' => $this->resourceHelper, - 'string' => $this->string, - 'errorAggregator' => $this->errorAggregator, - 'eventManager' => $this->_eventManager, - 'stockRegistry' => $this->stockRegistry, - 'stockConfiguration' => $this->stockConfiguration, - 'stockStateProvider' => $this->stockStateProvider, - 'catalogData' => $this->_catalogData, - 'importConfig' => $this->_importConfig, - 'resourceFactory' => $this->_resourceFactory, - 'optionFactory' => $this->optionFactory, - 'setColFactory' => $this->_setColFactory, - 'productTypeFactory' => $this->_productTypeFactory, - 'linkFactory' => $this->_linkFactory, - 'proxyProdFactory' => $this->_proxyProdFactory, - 'uploaderFactory' => $this->_uploaderFactory, - 'filesystem' => $this->_filesystem, - 'stockResItemFac' => $this->_stockResItemFac, - 'localeDate' => $this->_localeDate, - 'dateTime' => $this->dateTime, - 'logger' => $this->_logger, - 'indexerRegistry' => $this->indexerRegistry, - 'storeResolver' => $this->storeResolver, - 'skuProcessor' => $this->skuProcessor, - 'categoryProcessor' => $this->categoryProcessor, - 'validator' => $this->validator, - 'objectRelationProcessor' => $this->objectRelationProcessor, - 'transactionManager' => $this->transactionManager, - 'taxClassProcessor' => $this->taxClassProcessor, - 'scopeConfig' => $this->scopeConfig, - 'productUrl' => $this->productUrl, - 'data' => $this->data, - 'imageTypeProcessor' => $this->imageTypeProcessor, - 'skuStorage' => $this->skuStorageMock - ] + $this->productPropertiesMap ); $reflection = new \ReflectionClass(Product::class); $reflectionProperty = $reflection->getProperty('metadataPool'); @@ -556,9 +567,9 @@ protected function _objectConstructor() $this->optionEntity = $this->getMockBuilder(Option::class) ->disableOriginalConstructor() ->getMock(); - $this->optionFactory->expects($this->once())->method('create')->willReturn($this->optionEntity); + $this->optionFactory->expects($this->atLeastOnce())->method('create')->willReturn($this->optionEntity); - $this->_filesystem->expects($this->once()) + $this->_filesystem->expects($this->atLeastOnce()) ->method('getDirectoryWrite') ->with(DirectoryList::ROOT) ->willReturn($this->_mediaDirectory); @@ -581,7 +592,7 @@ protected function _parentObjectConstructor() $this->_connection = $this->getMockForAbstractClass(AdapterInterface::class); $this->select = $this->getMockBuilder(Select::class) ->disableOriginalConstructor() - ->onlyMethods(['from', 'where']) + ->onlyMethods(['from', 'where', 'joinLeft']) ->getMock(); $this->select->expects($this->any())->method('from')->willReturnSelf(); //$this->select->expects($this->any())->method('where')->willReturnSelf(); @@ -617,11 +628,11 @@ protected function _initAttributeSets() $collection = $this->getMockBuilder(Collection::class) ->disableOriginalConstructor() ->getMock(); - $collection->expects($this->once()) + $collection->expects($this->atLeastOnce()) ->method('setEntityTypeFilter') ->with(self::ENTITY_TYPE_ID) ->willReturn($attributeSetCol); - $this->_setColFactory->expects($this->once()) + $this->_setColFactory->expects($this->atLeastOnce()) ->method('create') ->willReturn($collection); return $this; @@ -641,20 +652,20 @@ protected function _initTypeModels() $this->getMockBuilder(AbstractType::class) ->disableOriginalConstructor() ->getMock(); - $productTypeInstance->expects($this->once()) + $productTypeInstance->expects($this->atLeastOnce()) ->method('isSuitable') ->willReturn(true); - $productTypeInstance->expects($this->once()) + $productTypeInstance->expects($this->atLeastOnce()) ->method('getParticularAttributes') ->willReturn([]); - $productTypeInstance->expects($this->once()) + $productTypeInstance->expects($this->atLeastOnce()) ->method('getCustomFieldsMapping') ->willReturn([]); - $this->_importConfig->expects($this->once()) + $this->_importConfig->expects($this->atLeastOnce()) ->method('getEntityTypes') ->with(self::ENTITY_TYPE_CODE) ->willReturn($entityTypes); - $this->_productTypeFactory->expects($this->once())->method('create')->willReturn($productTypeInstance); + $this->_productTypeFactory->expects($this->atLeastOnce())->method('create')->willReturn($productTypeInstance); return $this; } @@ -663,10 +674,8 @@ protected function _initTypeModels() */ protected function _initSkus() { - $this->skuProcessor->expects($this->once())->method('setTypeModels'); - $this->skuProcessor->expects($this->never())->method('reloadOldSkus')->willReturnSelf(); - $this->skuProcessor->expects($this->never())->method('getOldSkus')->willReturn([]); - $this->skuStorageMock->expects($this->once())->method('reset'); + $this->skuProcessor->expects($this->atLeastOnce())->method('setTypeModels'); + $this->skuStorageMock->expects($this->atLeastOnce())->method('reset'); return $this; } @@ -675,7 +684,7 @@ protected function _initSkus() */ protected function _initImagesArrayKeys() { - $this->imageTypeProcessor->expects($this->once())->method('getImageTypes')->willReturn( + $this->imageTypeProcessor->expects($this->atLeastOnce())->method('getImageTypes')->willReturn( ['image', 'small_image', 'thumbnail', 'swatch_image', '_media_image'] ); return $this; @@ -2273,4 +2282,194 @@ public function testGetRemoteFileContent() $property->invokeArgs($this->importProduct, ['php://filter']) ); } + + /** + * Test when import product throws an error when the file has duplicated Url Keys from another entity. + * + * @param array $dataProvider + * + * @dataProvider duplicatedUrlCheckDataProvider + * @return void + */ + public function testImportProductOnDuplicatedUrlKey(array $dataProvider): void + { + $tableName = $dataProvider['table_name']; + $tableNameProduct = $dataProvider['table_name_product']; + $callIndexTableName = 0; + $errorAggregator = $this->setUpPropertiesMap($dataProvider); + $importProduct = $this->objectManager->getObject( + Product::class, + $this->productPropertiesMap + ); + + $this->_resourceFactory->expects($this->once()) + ->method('create') + ->willReturn($this->resource); + + $this->resource->expects($this->exactly(2)) + ->method('getTable') + ->willReturnCallback(function ($table) use (&$callIndexTableName, $tableName, $tableNameProduct) { + if ($callIndexTableName === 0) { + $this->assertEquals($tableName, $table); + $callIndexTableName++; + return $tableName; + } + + $this->assertEquals($tableNameProduct, $table); + return $tableNameProduct; + }); + + $this->_connection->expects($this->once()) + ->method('select') + ->willReturn($this->select); + + $this->select->expects($this->once()) + ->method('from') + ->with(['url_rewrite' => $tableName], $dataProvider['fields']) + ->willReturn($this->select); + + $this->select->expects($this->once()) + ->method('joinLeft') + ->with(['cpe' => $tableNameProduct], 'cpe.entity_id = url_rewrite.entity_id') + ->willReturn($this->select); + + $callIndexSelect = 0; + $storeId = $dataProvider['store_id']; + + $this->select->expects($this->exactly(3)) + ->method('where') + ->willReturnCallback(function ($condition, $value) use (&$callIndexSelect, $storeId) { + if ($callIndexSelect === 0) { + $this->assertEquals('request_path IN (?)', $condition); + $this->assertEquals([$storeId => "adobe.html"], $value); + } elseif ($callIndexSelect === 1) { + $this->assertEquals('store_id IN (?)', $condition); + $this->assertEquals($storeId, $value); + } else { + $this->assertEquals('cpe.sku not in (?)', $condition); + } + $callIndexSelect++; + return $this->select; + }); + + $this->_connection->expects($this->once()) + ->method('fetchAssoc') + ->with($this->select) + ->willReturn([$dataProvider['entity']]); + + if ($dataProvider['is_error_expected']) { + if ($dataProvider['entity']['entity_type'] === 'product') { + $expectedErrorMessage = sprintf( + $dataProvider['error_message_template'], + $dataProvider['request_path'], + $dataProvider['entity']['sku'], + ); + } else { + $expectedErrorMessage = sprintf( + $dataProvider['error_message_template'], + $dataProvider['request_path'], + $dataProvider['entity']['entity_type'], + $dataProvider['entity']['entity_id'] + ); + } + + $errorAggregator->expects($this->once()) + ->method('addError') + ->with( + ValidatorInterface::ERROR_DUPLICATE_URL_KEY, + ProcessingError::ERROR_LEVEL_CRITICAL, + $this->productPropertiesMap['rowNumbers'][$storeId][$dataProvider['request_path']], + 'url_key', + $expectedErrorMessage + ); + } + + $this->invokeMethod( + $importProduct, + 'checkUrlKeyDuplicates', + ); + } + + /** + * Data provider for checking duplicated entries. + * + * @return array[] + */ + public static function duplicatedUrlCheckDataProvider(): array + { + return [ + 'Record duplicated by category. Should Throw Validation Error' => [ + 'data' => [ + 'is_error_expected' => true, + 'store_id' => 0, + 'table_name' => 'url_rewrite', + 'table_name_product' => 'catalog_product_entity', + 'request_path' => 'adobe.html', + 'entity' => [ + 'request_path' => 'adobe.html', + 'store_id' => 0, + 'entity_type' => 'category', + 'entity_id' => rand(), + 'url_rewrite_id' => rand(), + 'url_entity' => rand() + ], + 'error_message_template' => 'Url key: \'%s\' was already generated for a %s with the ID: %s. ' . + 'You need to specify the unique URL key manually', + 'fields' => [ + 'request_path', + 'store_id', + 'entity_type' + ] + ] + ], + 'Record duplicated by product. Should Throw Validation Error' => [ + 'data' => [ + 'is_error_expected' => true, + 'store_id' => 0, + 'table_name' => 'url_rewrite', + 'table_name_product' => 'catalog_product_entity', + 'request_path' => 'adobe.html', + 'entity' => [ + 'request_path' => 'adobe.html', + 'store_id' => 0, + 'entity_type' => 'product', + 'entity_id' => 42, + 'url_rewrite_id' => rand(), + 'url_entity' => rand(), + 'sku' => rand() + ], + 'fields' => [ + 'request_path', + 'store_id', + 'entity_type', + ], + 'error_message_template' => 'Url key: \'%s\' was already generated for an item with the SKU: ' . + '\'%s\'. You need to specify the unique URL key manually' + ] + ] + ]; + } + + /** + * Set up properties map. + * + * @param array $dataProvider + * + * @return MockObject + */ + private function setUpPropertiesMap(array $dataProvider): MockObject + { + $errorAggregator = $this->getMockBuilder(ProcessingErrorAggregatorInterface::class) + ->getMock(); + + $this->resource = $this->getMockBuilder(AbstractEntity::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->productPropertiesMap['urlKeys'] = [[$dataProvider['request_path'] => 'Entity Name']]; + $this->productPropertiesMap['rowNumbers'] = [$dataProvider['store_id'] => [$dataProvider['request_path'] => 1]]; + $this->productPropertiesMap['errorAggregator'] = $errorAggregator; + + return $errorAggregator; + } } From f4c9bf025cd63ce9cd96cd8f20d90293395bcc8e Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Mon, 23 Dec 2024 14:26:19 -0600 Subject: [PATCH 27/42] ACP2E-3334: [Internal] Fixture apply failure is not shown during execution or in logs --- .../Annotation/AbstractDataFixture.php | 15 +--- .../TestFramework/Annotation/AppArea.php | 9 +- .../TestFramework/Annotation/AppIsolation.php | 16 ++-- .../TestFramework/Annotation/Cache.php | 13 ++- .../Annotation/ComponentRegistrarFixture.php | 10 +-- .../TestFramework/Annotation/DbIsolation.php | 10 +-- .../Annotation/ExceptionHandler.php | 86 +++---------------- .../Annotation/IndexerDimensionMode.php | 17 ++-- .../TestFramework/Event/ExecutionState.php | 49 +++++++++++ .../TestFramework/Event/Subscribers.php | 18 ++-- .../Event/TestFinishedSubscriber.php | 19 +++- .../Event/TestPreparedSubscriber.php | 21 ++++- .../Event/TestPreprationStartedSubscriber.php | 20 ++++- 13 files changed, 157 insertions(+), 146 deletions(-) create mode 100644 dev/tests/integration/framework/Magento/TestFramework/Event/ExecutionState.php diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php index 35df3645a588..d4b5d9867dc5 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php @@ -1,7 +1,7 @@ name(), - $exception + $exception, + $test ); } @@ -125,8 +122,6 @@ protected function _applyFixtures(array $fixtures, TestCase $test) } catch (\Throwable $exception) { ExceptionHandler::handle( 'Unable to apply fixture: ' . $this->getFixtureReference($fixture), - $fixture['test']['class'], - $fixture['test']['method'], $exception, $test ); @@ -156,8 +151,6 @@ protected function _revertFixtures(?TestCase $test = null) } catch (\Throwable $exception) { ExceptionHandler::handle( 'Unable to revert fixture: ' . $this->getFixtureReference($fixture), - $fixture['test']['class'], - $fixture['test']['method'], $exception, $test ); diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/AppArea.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/AppArea.php index 46927458ec73..015a596e3b7f 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/AppArea.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/AppArea.php @@ -1,7 +1,7 @@ name(), - $exception + $exception, + $test ); } if ($values) { diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/Cache.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/Cache.php index 6d027f10a742..29130f167ce2 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/Cache.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/Cache.php @@ -1,9 +1,8 @@ getName(false), - $exception + $exception, + $test ); } @@ -93,8 +91,7 @@ private function setValues($values, TestCase $test) if (!isset($this->origValues[$type])) { ExceptionHandler::handle( "Unknown cache type specified: '{$type}' in @magentoCache", - get_class($test), - $test->getName(false) + test: $test ); } $states->setEnabled($type, $isEnabled); diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/ComponentRegistrarFixture.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/ComponentRegistrarFixture.php index 6288a8f1b5b6..79c18848cbdb 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/ComponentRegistrarFixture.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/ComponentRegistrarFixture.php @@ -1,9 +1,8 @@ getName(false), - $exception + $exception, + $test ); } if (!$values) { diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/DbIsolation.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DbIsolation.php index 3b1f41da9be5..19ff3912c485 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/DbIsolation.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DbIsolation.php @@ -1,11 +1,10 @@ name(), - $exception + $exception, + $test ); } return $state; diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/ExceptionHandler.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/ExceptionHandler.php index 6bc60a7dfd00..2b4b03c1d5e2 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/ExceptionHandler.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/ExceptionHandler.php @@ -1,98 +1,40 @@ getMessage(), - (int) $e->getCode(), - $e - ); + ?\Throwable $previous = null, + ?TestCase $test = null + ): never { + if (!$test) { + throw new Exception($message, 0, $previous); } - $name = $testMethod; - - if ($name && $reflected->hasMethod($name)) { - try { - $reflected = $reflected->getMethod($name); - } catch (ReflectionException $e) { - throw new Exception( - $e->getMessage(), - (int) $e->getCode(), - $e - ); - } - } - - $location = sprintf( - "%s(%d): %s->%s()", - $reflected->getFileName(), - $reflected->getStartLine(), - $testClass, - $testMethod - ); - - $summary = ''; if ($previous) { - $exception = $previous; - do { - $summary .= PHP_EOL - . PHP_EOL - . 'Caused By: ' - . $exception->getMessage() - . PHP_EOL - . $exception->getTraceAsString(); - } while ($exception = $exception->getPrevious()); - } - $exception = new Exception( - sprintf( - "%s\n#0 %s%s", - $message, - $location, - $summary - ), - 0, - $previous - ); - if ($test) { - \PHPUnit\Event\Facade::emitter()->testErrored( - $test->valueObjectForEvents(), - \PHPUnit\Event\Code\ThrowableBuilder::from($exception), - ); - $test::fail($exception->getMessage()); - } else { - throw $exception; + $throwable = ThrowableBuilder::from($previous); + $message .= PHP_EOL . 'Caused by' . PHP_EOL . $throwable->asString(); } + $test::fail($message); } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php index 0bfccd989f2d..e054eba94383 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/IndexerDimensionMode.php @@ -1,7 +1,7 @@ getName(false) + test: $test ); } } @@ -106,9 +105,8 @@ public function startTest(TestCase $test) } catch (\Throwable $exception) { ExceptionHandler::handle( 'Unable to parse fixtures', - get_class($test), - $test->getName(false), - $exception + $exception, + $test ); } @@ -117,8 +115,7 @@ public function startTest(TestCase $test) if ($dbIsolation) { ExceptionHandler::handle( '@magentoDbIsolation must be disabled when using @magentoIndexerDimensionMode', - get_class($test), - $test->getName(false) + test: $test ); } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Event/ExecutionState.php b/dev/tests/integration/framework/Magento/TestFramework/Event/ExecutionState.php new file mode 100644 index 000000000000..03c0399ee10b --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Event/ExecutionState.php @@ -0,0 +1,49 @@ +data[$test]['exception'] = $exception; + } + + /** + * @param string $test + * @return \Throwable|null + */ + public function popPreparationFailure(string $test): ?\Throwable + { + $exception = $this->data[$test]['exception'] ?? null; + if ($exception) { + unset($this->data[$test]['exception']); + } + + return $exception; + } + + /** + * @param string $test + * @return void + */ + public function clearTestData(string $test): void + { + unset($this->data[$test]); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Event/Subscribers.php b/dev/tests/integration/framework/Magento/TestFramework/Event/Subscribers.php index 7d9277f20e85..c0b2d55b6cd9 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Event/Subscribers.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Event/Subscribers.php @@ -3,15 +3,16 @@ * Copyright 2024 Adobe * All Rights Reserved. */ +declare(strict_types=1); -/** - * Subscribers of PHPUnit built-in events - */ namespace Magento\TestFramework\Event; use PHPUnit\Runner; use PHPUnit\TextUI; +/** + * Subscribers of PHPUnit built-in events + */ class Subscribers implements Runner\Extension\Extension { /** @@ -26,19 +27,20 @@ public function bootstrap( Runner\Extension\Facade $facade, Runner\Extension\ParameterCollection $parameters ): void { + $executionState = new ExecutionState(); if ($configuration->hasConfigurationFile() && str_contains($configuration->configurationFile(), 'setup-integration')) { $facade->registerSubscribers( - new TestPreprationStartedSubscriber(), - new TestFinishedSubscriber() + new TestPreprationStartedSubscriber($executionState), + new TestFinishedSubscriber($executionState) ); } else { $facade->registerSubscribers( new TestSuitStartedSubscriber(), new TestSuitEndSubscriber(), - new TestPreparedSubscriber(), - new TestPreprationStartedSubscriber(), - new TestFinishedSubscriber(), + new TestPreparedSubscriber($executionState), + new TestPreprationStartedSubscriber($executionState), + new TestFinishedSubscriber($executionState), new TestSkippedSubscriber(), new TestErroredSubscriber() ); diff --git a/dev/tests/integration/framework/Magento/TestFramework/Event/TestFinishedSubscriber.php b/dev/tests/integration/framework/Magento/TestFramework/Event/TestFinishedSubscriber.php index 73d8cddc4ce6..379b72b26f7e 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Event/TestFinishedSubscriber.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Event/TestFinishedSubscriber.php @@ -3,18 +3,27 @@ * Copyright 2024 Adobe * All Rights Reserved. */ +declare(strict_types=1); -/** - * Test Finished Subscriber - */ namespace Magento\TestFramework\Event; use PHPUnit\Event\Test\FinishedSubscriber; use PHPUnit\Event\Test\Finished; use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; +/** + * Test Finished Subscriber + */ class TestFinishedSubscriber implements FinishedSubscriber { + /** + * @param ExecutionState $executionState + */ + public function __construct(private readonly ExecutionState $executionState) + { + } + /** * Test finished Subscriber * @@ -24,12 +33,14 @@ public function notify(Finished $event): void { $className = $event->test()->className(); $methodName = $event->test()->methodName(); - $objectManager = Bootstrap::getObjectManager(); + $objectManager = Bootstrap::getObjectManager(); + /** @var TestCase $testObj */ $testObj = $objectManager->create($className, ['name' => $methodName]); $phpUnit = $objectManager->create(PhpUnit::class); $phpUnit->endTest($testObj, 0); + $this->executionState->clearTestData($testObj->toString()); Magento::setCurrentEventObject(null); Magento::setTestPrepared(false); } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php b/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php index 8a03af2d84ad..2a1669e4e6e5 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php @@ -3,10 +3,8 @@ * Copyright 2024 Adobe * All Rights Reserved. */ +declare(strict_types=1); -/** - * Test Prepared Subscriber - */ namespace Magento\TestFramework\Event; use PHPUnit\Event\Test\Prepared; @@ -14,8 +12,18 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Workaround\Override\Config; +/** + * Test Prepared Subscriber + */ class TestPreparedSubscriber implements PreparedSubscriber { + /** + * @param ExecutionState $executionState + */ + public function __construct(private readonly ExecutionState $executionState) + { + } + /** * Test prepared Subscriber * @@ -29,6 +37,13 @@ public function notify(Prepared $event): void $objectManager = Bootstrap::getObjectManager(); $testObj = $objectManager->create($className, ['name' => $methodName]); + // An exception can occur in PreparationStarted subscriber during applying fixtures. + // In order to prevent test execution it should be thrown here, from Prepared subscriber. + $exception = $this->executionState->popPreparationFailure($testObj->toString()); + if ($exception) { + throw $exception; + } + $testData = $event->test()->testData(); if ($testData->hasDataFromDataProvider()) { $dataSetName = $testData->dataFromDataProvider()->dataSetName(); diff --git a/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreprationStartedSubscriber.php b/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreprationStartedSubscriber.php index 7e8dfcd7addc..8ec5ae777008 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreprationStartedSubscriber.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreprationStartedSubscriber.php @@ -3,18 +3,26 @@ * Copyright 2024 Adobe * All Rights Reserved. */ +declare(strict_types=1); -/** - * TestPreparation Started Subscriber - */ namespace Magento\TestFramework\Event; use PHPUnit\Event\Test\PreparationStarted; use PHPUnit\Event\Test\PreparationStartedSubscriber; use Magento\TestFramework\Helper\Bootstrap; +/** + * TestPreparation Started Subscriber + */ class TestPreprationStartedSubscriber implements PreparationStartedSubscriber { + /** + * @param ExecutionState $executionState + */ + public function __construct(private readonly ExecutionState $executionState) + { + } + /** * Test Preparation Started Subscriber * @@ -31,6 +39,10 @@ public function notify(PreparationStarted $event): void Magento::setCurrentEventObject($event); $phpUnit = $objectManager->create(PhpUnit::class); - $phpUnit->startTest($testObj); + try { + $phpUnit->startTest($testObj); + } catch (\Throwable $e) { + $this->executionState->registerPreparationFailure($testObj->toString(), $e); + } } } From 27d02a28c69f6235988b80e23370c36bb310ff9d Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Mon, 23 Dec 2024 16:00:14 -0600 Subject: [PATCH 28/42] ACP2E-3334: [Internal] Fixture apply failure is not shown during execution or in logs --- .../Magento/TestFramework/Event/ExecutionState.php | 6 ++++++ .../Magento/TestFramework/Event/TestFinishedSubscriber.php | 3 --- .../Magento/TestFramework/Event/TestPreparedSubscriber.php | 3 --- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev/tests/integration/framework/Magento/TestFramework/Event/ExecutionState.php b/dev/tests/integration/framework/Magento/TestFramework/Event/ExecutionState.php index 03c0399ee10b..d4fc43e51866 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Event/ExecutionState.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Event/ExecutionState.php @@ -15,6 +15,8 @@ class ExecutionState private array $data = []; /** + * Register failure during preparation phase. + * * @param string $test * @param \Throwable $exception * @return void @@ -25,6 +27,8 @@ public function registerPreparationFailure(string $test, \Throwable $exception): } /** + * Pop failure registered during preparation phase. + * * @param string $test * @return \Throwable|null */ @@ -39,6 +43,8 @@ public function popPreparationFailure(string $test): ?\Throwable } /** + * Clear stored test data. + * * @param string $test * @return void */ diff --git a/dev/tests/integration/framework/Magento/TestFramework/Event/TestFinishedSubscriber.php b/dev/tests/integration/framework/Magento/TestFramework/Event/TestFinishedSubscriber.php index 379b72b26f7e..f03c9fe2cc44 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Event/TestFinishedSubscriber.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Event/TestFinishedSubscriber.php @@ -12,9 +12,6 @@ use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; -/** - * Test Finished Subscriber - */ class TestFinishedSubscriber implements FinishedSubscriber { /** diff --git a/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php b/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php index 2a1669e4e6e5..f0f17871f853 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Event/TestPreparedSubscriber.php @@ -12,9 +12,6 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Workaround\Override\Config; -/** - * Test Prepared Subscriber - */ class TestPreparedSubscriber implements PreparedSubscriber { /** From 807362ac799d263b5ee95801db2fc913d87eb004 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Mon, 23 Dec 2024 20:11:45 -0600 Subject: [PATCH 29/42] ACP2E-3334: [Internal] Fixture apply failure is not shown during execution or in logs --- .../framework/Magento/TestFramework/Event/Subscribers.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/integration/framework/Magento/TestFramework/Event/Subscribers.php b/dev/tests/integration/framework/Magento/TestFramework/Event/Subscribers.php index c0b2d55b6cd9..49ddacb65733 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Event/Subscribers.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Event/Subscribers.php @@ -21,6 +21,7 @@ class Subscribers implements Runner\Extension\Extension * @param TextUI\Configuration\Configuration $configuration * @param Runner\Extension\Facade $facade * @param Runner\Extension\ParameterCollection $parameters + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function bootstrap( TextUI\Configuration\Configuration $configuration, From 7d7193dff7fc49cfdedbafcdd07c467f25da9ff9 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Mon, 23 Dec 2024 20:40:01 -0600 Subject: [PATCH 30/42] ACP2E-3513: [CLOUD] Special price not showing in Configurable product --- .../Model/Product/Type/Configurable.php | 8 ++--- .../RenderingBasedOnIsProductListFlagTest.php | 29 +++++++++++++++++-- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php index c2f95bebdb88..b4316453e69a 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php @@ -1,9 +1,8 @@ get(ProductAttributeRepositoryInterface::class); + $specialPrice = $productAttributeRepository->get('special_price'); + $specialPrice->setUsedInProductListing(false); + $productAttributeRepository->save($specialPrice); + + try { + self::assertTrue($this->finalPriceBox->hasSpecialPrice()); + } finally { + $specialPrice->setUsedInProductListing(true); + $productAttributeRepository->save($specialPrice); + } + } + /** * Test when is_product_list flag is specified * From 3931e78f950adaff0245e4aecd501f577ee95d9f Mon Sep 17 00:00:00 2001 From: Olga Moyseyenko Date: Tue, 24 Dec 2024 17:45:00 -0600 Subject: [PATCH 31/42] ACP2E-3501: VAPT: Business Logic Error - future date as customer date of birth --- .../Magento/Customer/Model/Validator/Dob.php | 47 +++++-------------- 1 file changed, 11 insertions(+), 36 deletions(-) diff --git a/app/code/Magento/Customer/Model/Validator/Dob.php b/app/code/Magento/Customer/Model/Validator/Dob.php index 1246c825e1cd..c68700cac2b9 100644 --- a/app/code/Magento/Customer/Model/Validator/Dob.php +++ b/app/code/Magento/Customer/Model/Validator/Dob.php @@ -7,10 +7,9 @@ namespace Magento\Customer\Model\Validator; +use DateTimeZone; use Magento\Customer\Model\Customer; use Magento\Framework\Validator\AbstractValidator; -use Magento\Store\Api\Data\StoreInterface as StoreInterface; -use Magento\Store\Model\StoreManagerInterface; /** * Customer dob field validator. @@ -18,66 +17,42 @@ class Dob extends AbstractValidator { /** - * @var \DateTime - */ - private \DateTime $currentDate; - - /** - * @var StoreManagerInterface - */ - private StoreManagerInterface $storeManager; - - /** - * @param StoreManagerInterface $storeManager - */ - public function __construct(StoreManagerInterface $storeManager) - { - $this->currentDate = new \DateTime(); - $this->storeManager = $storeManager; - } - - /** - * Validate name fields. + * Validate dob field. * * @param Customer $customer * @return bool */ public function isValid($customer): bool { - if (!$this->isValidDob($customer->getDob(), $customer->getStoreId())) { - parent::_addMessages([['dob' => 'The Date of Birth should not be greater than today.']]); + $timezone = new DateTimeZone($customer->getStore()->getConfig('general/locale/timezone')); + if (!$this->isValidDob($customer->getDob(), $timezone)) { + $this->_addMessages([['dob' => 'The Date of Birth should not be greater than today.']]); } - return count($this->_messages) == 0; + return count($this->_messages) === 0; } /** * Check if specified dob is not in the future * * @param string|null $dobValue - * @param null|string|bool|int|StoreInterface $storeId + * @param DateTimeZone $timezone * @return bool */ - private function isValidDob(?string $dobValue, null|string|bool|int|StoreInterface $storeId): bool + private function isValidDob(?string $dobValue, ?DateTimeZone $timezone = null): bool { if ($dobValue != null) { - // Get the timezone of the store - $store = $this->storeManager->getStore($storeId); - $timezone = $store->getConfig('general/locale/timezone'); - // Get the date of birth and set the time to 00:00:00 - $dobDate = new \DateTime($dobValue, new \DateTimeZone($timezone)); + $dobDate = new \DateTime($dobValue, $timezone); $dobDate->setTime(0, 0, 0); // Get the timestamp of the date of birth and the current date $dobTimestamp = $dobDate->getTimestamp(); - $currentTimestamp = $this->currentDate->getTimestamp(); + $currentTimestamp = time(); // If the date's of birth first minute is in the future, return false - the day has not started yet - if ($dobTimestamp > $currentTimestamp) { - return false; - } + return ($dobTimestamp <= $currentTimestamp); } return true; From b200bdef6d1643e1d414dedd0df2b01f0b2913e0 Mon Sep 17 00:00:00 2001 From: Olga Moyseyenko Date: Wed, 25 Dec 2024 18:36:51 -0600 Subject: [PATCH 32/42] ACP2E-3501: VAPT: Business Logic Error - future date as customer date of birth --- .../Model/Export/CustomerTest.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php index c747474e7d92..664831bf2a08 100644 --- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php +++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php @@ -69,12 +69,12 @@ protected function setUp(): void } /** - * Export "Customer Main File". + * Export with Multi Websites "Customer Main File". * - * @magentoDataFixture Magento/Customer/_files/import_export/customers.php + * @magentoDataFixture Magento/Customer/_files/import_export/customers_with_websites.php * @return void */ - public function testExport() + public function testExportWithMultiWebsites(): void { $this->processCustomerAttribute(); $expectedAttributes = $this->getExpectedAttributes(); @@ -83,12 +83,12 @@ public function testExport() } /** - * Export with Multi Websites "Customer Main File". + * Export "Customer Main File". * - * @magentoDataFixture Magento/Customer/_files/import_export/customers_with_websites.php + * @magentoDataFixture Magento/Customer/_files/import_export/customers.php * @return void */ - public function testExportWithMultiWebsites(): void + public function testExport() { $this->processCustomerAttribute(); $expectedAttributes = $this->getExpectedAttributes(); @@ -96,6 +96,8 @@ public function testExportWithMultiWebsites(): void $this->checkExportData($lines, $expectedAttributes); } + + /** * Return attributes which should be exported. * From d6901297949255a374e44a1b32de9d3da43a0d9f Mon Sep 17 00:00:00 2001 From: Olga Moyseyenko Date: Thu, 26 Dec 2024 22:45:22 -0600 Subject: [PATCH 33/42] ACP2E-3501: VAPT: Business Logic Error - future date as customer date of birth --- .../CustomerImportExport/Model/Export/CustomerTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php index 664831bf2a08..bc0272b48914 100644 --- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php +++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php @@ -1,7 +1,7 @@ checkExportData($lines, $expectedAttributes); } - - /** * Return attributes which should be exported. * From 55ab026db9ebbd5ccfa4a8929fc9d3b35eadd16e Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin Date: Fri, 27 Dec 2024 14:29:23 -0600 Subject: [PATCH 34/42] ACP2E-3527: [Cloud] Import Processes Interfering with Each Other --- .../Block/Adminhtml/Import/Edit/Form.php | 14 +++++++-- .../Controller/Adminhtml/Import/Validate.php | 9 ++++-- .../Magento/ImportExport/Model/History.php | 29 ++++++++++--------- .../Adminhtml/Import/ValidateTest.php | 10 +++++-- 4 files changed, 42 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php index bf394c9ed0c4..11eaaa1a4a20 100644 --- a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php +++ b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php @@ -1,7 +1,7 @@ '', ] ); + $fieldset->addField( + '_import_history_id', + 'hidden', + [ + 'name' => '_import_history_id', + 'label' => __('Import History id'), + 'title' => __('Import History id'), + 'value' => '', + ] + ); $fieldsets['upload'] = $fieldset; $form->setUseContainer(true); $this->setForm($form); diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php index 35fe27667bbb..6140a2b982de 100644 --- a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php +++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php @@ -1,7 +1,7 @@ getValidatedIds(); if (count($ids) > 0) { $resultBlock->addAction('value', Import::FIELD_IMPORT_IDS, $ids); + $resultBlock->addAction( + 'value', + '_import_history_id', + $this->historyModel->getId() + ); } } catch (\Magento\Framework\Exception\LocalizedException $e) { $resultBlock->addError($e->getMessage()); diff --git a/app/code/Magento/ImportExport/Model/History.php b/app/code/Magento/ImportExport/Model/History.php index 9a97367ba845..ad0be7fbb8bc 100644 --- a/app/code/Magento/ImportExport/Model/History.php +++ b/app/code/Magento/ImportExport/Model/History.php @@ -1,7 +1,7 @@ isReportEntityType()) { - $this->load($this->getLastItemId()); + $this->load($import->getData('_import_history_id') ?? $this->getLastItemId()); $executionResult = self::IMPORT_IN_PROCESS; if ($updateSummary) { $executionResult = $this->reportHelper->getExecutionTime($this->getStartedAt()); diff --git a/app/code/Magento/ImportExport/Test/Unit/Controller/Adminhtml/Import/ValidateTest.php b/app/code/Magento/ImportExport/Test/Unit/Controller/Adminhtml/Import/ValidateTest.php index 1e03e0ad3ca6..5b346720924d 100644 --- a/app/code/Magento/ImportExport/Test/Unit/Controller/Adminhtml/Import/ValidateTest.php +++ b/app/code/Magento/ImportExport/Test/Unit/Controller/Adminhtml/Import/ValidateTest.php @@ -1,7 +1,7 @@ expects($this->once()) + ->method('addAction') + ->willReturn( + ['show', 'import_validation_container'], + ['value', '_import_history_id', 1] + ); $this->importMock->expects($this->exactly(3)) ->method('getProcessedRowsCount') ->willReturn(2); From 1528fd9515b6f7a2bb2737acbb6ca90b28d16db5 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin Date: Thu, 2 Jan 2025 11:46:25 -0600 Subject: [PATCH 35/42] ACP2E-3527: [Cloud] Import Processes Interfering with Each Other - i18n updated --- app/code/Magento/ImportExport/i18n/en_US.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/ImportExport/i18n/en_US.csv b/app/code/Magento/ImportExport/i18n/en_US.csv index cc1098841bab..378daac3afa2 100644 --- a/app/code/Magento/ImportExport/i18n/en_US.csv +++ b/app/code/Magento/ImportExport/i18n/en_US.csv @@ -29,6 +29,7 @@ Import,Import "File to Import","File to Import" "Select File to Import","Select File to Import" "Images File Directory","Images File Directory" +"Import History id","Import History id" "For Type ""Local Server"" use relative path to <Magento root directory>/var/import/images, e.g. product_images, import_images/batch1.

For example, in case product_images, files should be placed into <Magento root directory>/var/import/images/product_images folder.","For Type ""Local Server"" use relative path to <Magento root directory>/var/import/images, e.g. product_images, import_images/batch1.

For example, in case product_images, files should be placed into <Magento root directory>/var/import/images/product_images folder." "Download Sample File","Download Sample File" "Please correct the data sent value.","Please correct the data sent value." From 659c431ac8fc3eca1a244c61c734318cfcb89641 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Fri, 3 Jan 2025 15:56:39 -0600 Subject: [PATCH 36/42] ACP2E-3532: Sitemap Generation Warnings --- app/code/Magento/Sitemap/Model/Sitemap.php | 51 ++++++++++++++-------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Sitemap/Model/Sitemap.php b/app/code/Magento/Sitemap/Model/Sitemap.php index b88ff704b580..2d5034b317cf 100644 --- a/app/code/Magento/Sitemap/Model/Sitemap.php +++ b/app/code/Magento/Sitemap/Model/Sitemap.php @@ -1,9 +1,8 @@ _sitemapData = $sitemapData; $this->filesystem = $filesystem; $this->_directory = $filesystem->getDirectoryWrite(DirectoryList::PUB); + $this->tmpDirectory = $filesystem->getDirectoryWrite(DirectoryList::SYS_TMP); $this->_categoryFactory = $categoryFactory; $this->_productFactory = $productFactory; $this->_cmsFactory = $cmsFactory; @@ -480,13 +477,11 @@ public function generateXml() $this->_finalizeSitemap(); - if ($this->_sitemapIncrement == 1) { + if ($this->_sitemapIncrement == -1) { // In case when only one increment file was created use it as default sitemap - $sitemapPath = $this->getSitemapPath() !== null ? rtrim($this->getSitemapPath(), '/') : ''; - $path = $sitemapPath . '/' . $this->_getCurrentSitemapFilename($this->_sitemapIncrement); - $destination = $sitemapPath . '/' . $this->getSitemapFilename(); - - $this->_directory->renameFile($path, $destination); + $path = $this->getFilePath($this->_getCurrentSitemapFilename($this->_sitemapIncrement)); + $destination = $this->getFilePath($this->getSitemapFilename()); + $this->tmpDirectory->renameFile($path, $destination, $this->_directory); } else { // Otherwise create index file with list of generated sitemaps $this->_createSitemapIndex(); @@ -507,10 +502,15 @@ protected function _createSitemapIndex() { $this->_createSitemap($this->getSitemapFilename(), self::TYPE_INDEX); for ($i = 1; $i <= $this->_sitemapIncrement; $i++) { - $xml = $this->_getSitemapIndexRow($this->_getCurrentSitemapFilename($i), $this->_getCurrentDateTime()); + $fileName = $this->_getCurrentSitemapFilename($i); + $path = $this->getFilePath($fileName); + $this->tmpDirectory->renameFile($path, $path, $this->_directory); + $xml = $this->_getSitemapIndexRow($fileName, $this->_getCurrentDateTime()); $this->_writeSitemapRow($xml); } $this->_finalizeSitemap(self::TYPE_INDEX); + $path = $this->getFilePath($this->getSitemapFilename()); + $this->tmpDirectory->renameFile($path, $path, $this->_directory); } /** @@ -638,9 +638,8 @@ protected function _createSitemap($fileName = null, $type = self::TYPE_URL) $this->_sitemapIncrement++; $fileName = $this->_getCurrentSitemapFilename($this->_sitemapIncrement); } - - $path = ($this->getSitemapPath() !== null ? rtrim($this->getSitemapPath(), '/') : '') . '/' . $fileName; - $this->_stream = $this->_directory->openFile($path); + $path = $this->getFilePath($fileName); + $this->_stream = $this->tmpDirectory->openFile($path); $fileHeader = sprintf($this->_tags[$type][self::OPEN_TAG_KEY], $type); $this->_stream->write($fileHeader); @@ -688,6 +687,20 @@ protected function _getCurrentSitemapFilename($index) . '-' . $this->getStoreId() . '-' . $index . '.xml'; } + /** + * Get path to sitemap file + * + * @param string $fileName + * @return string + */ + private function getFilePath(string $fileName): string + { + $path = $this->getSitemapPath() !== null ? rtrim($this->getSitemapPath(), '/') : ''; + $path .= '/' . $fileName; + + return $path; + } + /** * Get base dir * From 7971d4c991d0c482120f4d3c9fc4fd241918fcb9 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Fri, 3 Jan 2025 15:59:11 -0600 Subject: [PATCH 37/42] ACP2E-3532: Sitemap Generation Warnings --- app/code/Magento/Sitemap/Model/Sitemap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sitemap/Model/Sitemap.php b/app/code/Magento/Sitemap/Model/Sitemap.php index 2d5034b317cf..b70e695e7046 100644 --- a/app/code/Magento/Sitemap/Model/Sitemap.php +++ b/app/code/Magento/Sitemap/Model/Sitemap.php @@ -477,7 +477,7 @@ public function generateXml() $this->_finalizeSitemap(); - if ($this->_sitemapIncrement == -1) { + if ($this->_sitemapIncrement == 1) { // In case when only one increment file was created use it as default sitemap $path = $this->getFilePath($this->_getCurrentSitemapFilename($this->_sitemapIncrement)); $destination = $this->getFilePath($this->getSitemapFilename()); From 0d79b65ceb3fabc5cf5c89ebbef296030d5dadb2 Mon Sep 17 00:00:00 2001 From: Olga Moyseyenko Date: Sat, 4 Jan 2025 17:41:10 -0600 Subject: [PATCH 38/42] ACP2E-3501: VAPT: Business Logic Error - future date as customer date of birth --- .../Magento/Customer/Model/Validator/Dob.php | 21 +++++++++++++++++-- .../Model/Export/CustomerTest.php | 16 +++++++------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Customer/Model/Validator/Dob.php b/app/code/Magento/Customer/Model/Validator/Dob.php index c68700cac2b9..482cef123b8e 100644 --- a/app/code/Magento/Customer/Model/Validator/Dob.php +++ b/app/code/Magento/Customer/Model/Validator/Dob.php @@ -9,13 +9,28 @@ use DateTimeZone; use Magento\Customer\Model\Customer; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Framework\Validator\AbstractValidator; +use Magento\Store\Model\ScopeInterface; /** * Customer dob field validator. */ class Dob extends AbstractValidator { + /** + * @var TimezoneInterface + */ + private $timezone; + + /** + * @param TimezoneInterface $timezone + */ + public function __construct(TimezoneInterface $timezone) + { + $this->timezone = $timezone; + } + /** * Validate dob field. * @@ -24,7 +39,9 @@ class Dob extends AbstractValidator */ public function isValid($customer): bool { - $timezone = new DateTimeZone($customer->getStore()->getConfig('general/locale/timezone')); + $storeId = (int)$customer->getStoreId(); + $timezone = new DateTimeZone($this->timezone->getConfigTimezone(ScopeInterface::SCOPE_STORE, $storeId)); + if (!$this->isValidDob($customer->getDob(), $timezone)) { $this->_addMessages([['dob' => 'The Date of Birth should not be greater than today.']]); } @@ -36,7 +53,7 @@ public function isValid($customer): bool * Check if specified dob is not in the future * * @param string|null $dobValue - * @param DateTimeZone $timezone + * @param DateTimeZone|null $timezone * @return bool */ private function isValidDob(?string $dobValue, ?DateTimeZone $timezone = null): bool diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php index bc0272b48914..c747474e7d92 100644 --- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php +++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php @@ -1,7 +1,7 @@ processCustomerAttribute(); $expectedAttributes = $this->getExpectedAttributes(); @@ -83,12 +83,12 @@ public function testExportWithMultiWebsites(): void } /** - * Export "Customer Main File". + * Export with Multi Websites "Customer Main File". * - * @magentoDataFixture Magento/Customer/_files/import_export/customers.php + * @magentoDataFixture Magento/Customer/_files/import_export/customers_with_websites.php * @return void */ - public function testExport() + public function testExportWithMultiWebsites(): void { $this->processCustomerAttribute(); $expectedAttributes = $this->getExpectedAttributes(); From 46a1c4cf13fa8ff126f0b2b5e1f0ff39fc03ae36 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Mon, 6 Jan 2025 10:16:54 -0600 Subject: [PATCH 39/42] ACP2E-3532: Sitemap Generation Warnings --- app/code/Magento/Sitemap/Model/Sitemap.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Sitemap/Model/Sitemap.php b/app/code/Magento/Sitemap/Model/Sitemap.php index b70e695e7046..5b678b086a5a 100644 --- a/app/code/Magento/Sitemap/Model/Sitemap.php +++ b/app/code/Magento/Sitemap/Model/Sitemap.php @@ -828,6 +828,7 @@ public function getSitemapUrl($sitemapPath, $sitemapFileName) * @return bool * @deprecated 100.1.5 Because the robots.txt file is not generated anymore, * this method is not needed and will be removed in major release. + * @see no alternatives */ protected function _isEnabledSubmissionRobots() { @@ -842,6 +843,7 @@ protected function _isEnabledSubmissionRobots() * @return void * @deprecated 100.1.5 Because the robots.txt file is not generated anymore, * this method is not needed and will be removed in major release. + * @see no alternatives */ protected function _addSitemapToRobotsTxt($sitemapFileName) { From b934304ea81308111548b016fb4e0de17558a3e4 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Mon, 6 Jan 2025 13:20:16 -0600 Subject: [PATCH 40/42] ACP2E-3505: Disabled product still appears in related, upsell, crosssell items in grpahQL query --- .../RelatedProduct/GetRelatedProductsTest.php | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php index 63bbc70b13c9..c6666b78ebb2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php @@ -50,44 +50,6 @@ public function testQueryRelatedProducts() self::assertRelatedProducts($relatedProducts); } - /** - * @magentoApiDataFixture Magento/Catalog/_files/products_related_disabled.php - * @magentoConfigFixture default_store cataloginventory/options/show_out_of_stock 1 - * @magentoConfigFixture default_store catalog/magento_catalogpermissions/enabled 1 - * @magentoConfigFixture default_store catalog/magento_catalogpermissions/grant_catalog_category_view 1 - * @magentoConfigFixture default_store catalog/magento_catalogpermissions/grant_catalog_product_price 1 - * @magentoConfigFixture default_store catalog/magento_catalogpermissions/grant_checkout_items 1 - */ - public function testQueryDisableRelatedProductWithShowOutOfStock() - { - $productSku = 'simple_with_cross'; - - $query = <<graphQlQuery($query); - - self::assertArrayHasKey('products', $response); - self::assertArrayHasKey('items', $response['products']); - self::assertCount(1, $response['products']['items']); - self::assertArrayHasKey(0, $response['products']['items']); - self::assertArrayHasKey('related_products', $response['products']['items'][0]); - $relatedProducts = $response['products']['items'][0]['related_products']; - self::assertCount(0, $relatedProducts); - } - /** * @magentoApiDataFixture Magento/Catalog/_files/products_related_disabled.php */ From e466720ee1de60d75eaba722a2bb9e80095c5707 Mon Sep 17 00:00:00 2001 From: Olga Moyseyenko Date: Mon, 6 Jan 2025 16:29:36 -0600 Subject: [PATCH 41/42] ACP2E-3501: VAPT: Business Logic Error - future date as customer date of birth --- app/code/Magento/Customer/Model/Validator/Dob.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Model/Validator/Dob.php b/app/code/Magento/Customer/Model/Validator/Dob.php index 482cef123b8e..81438830e981 100644 --- a/app/code/Magento/Customer/Model/Validator/Dob.php +++ b/app/code/Magento/Customer/Model/Validator/Dob.php @@ -53,12 +53,12 @@ public function isValid($customer): bool * Check if specified dob is not in the future * * @param string|null $dobValue - * @param DateTimeZone|null $timezone + * @param DateTimeZone $timezone * @return bool */ - private function isValidDob(?string $dobValue, ?DateTimeZone $timezone = null): bool + private function isValidDob(?string $dobValue, DateTimeZone $timezone): bool { - if ($dobValue != null) { + if ($dobValue) { // Get the date of birth and set the time to 00:00:00 $dobDate = new \DateTime($dobValue, $timezone); From 028323978a1454c156b70553754fc8d69b1f8a96 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Mon, 6 Jan 2025 17:05:54 -0600 Subject: [PATCH 42/42] ACP2E-3532: Sitemap Generation Warnings --- .../Sitemap/Test/Unit/Model/SitemapTest.php | 139 +++++++----------- 1 file changed, 51 insertions(+), 88 deletions(-) diff --git a/app/code/Magento/Sitemap/Test/Unit/Model/SitemapTest.php b/app/code/Magento/Sitemap/Test/Unit/Model/SitemapTest.php index 0ccd32729c1a..6c40d257876d 100644 --- a/app/code/Magento/Sitemap/Test/Unit/Model/SitemapTest.php +++ b/app/code/Magento/Sitemap/Test/Unit/Model/SitemapTest.php @@ -1,12 +1,13 @@ sitemapCategoryMock = $this->getMockBuilder(Category::class) - ->disableOriginalConstructor() - ->getMock(); - $this->sitemapProductMock = $this->getMockBuilder(Product::class) - ->disableOriginalConstructor() - ->getMock(); - $this->sitemapCmsPageMock = $this->getMockBuilder(Page::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->helperMockSitemap = $this->getMockBuilder(Data::class) - ->disableOriginalConstructor() - ->getMock(); - $resourceMethods = [ '_construct', 'beginTransaction', @@ -133,35 +107,37 @@ protected function setUp(): void 'commit', '__wakeup', ]; - $this->resourceMock = $this->getMockBuilder(SitemapResource::class) ->onlyMethods($resourceMethods) ->disableOriginalConstructor() ->getMock(); - $this->resourceMock->method('addCommitCallback') ->willReturnSelf(); $this->fileMock = $this->createMock(Write::class); - $this->directoryMock = $this->createMock(DirectoryWrite::class); - $this->directoryMock->method('openFile') ->willReturn($this->fileMock); - - $this->filesystemMock = $this->getMockBuilder(Filesystem::class) - ->onlyMethods(['getDirectoryWrite']) - ->disableOriginalConstructor() - ->getMock(); - + $this->tmpFileMock = $this->createMock(Write::class); + $this->tmpDirectoryMock = $this->createMock(DirectoryWrite::class); + $this->tmpDirectoryMock->method('openFile') + ->willReturn($this->tmpFileMock); + $this->mediaDirectoryMock = $this->createMock(DirectoryWrite::class); + $this->filesystemMock = $this->createMock(Filesystem::class); $this->filesystemMock->method('getDirectoryWrite') - ->willReturn($this->directoryMock); + ->willReturnMap( + [ + [DirectoryList::PUB, $this->directoryMock], + [DirectoryList::SYS_TMP, $this->tmpDirectoryMock], + [DirectoryList::MEDIA, $this->mediaDirectoryMock], + ] + ); + + $this->configReaderMock = $this->createMock(SitemapConfigReaderInterface::class); + $this->itemProviderMock = $this->createMock(ItemProviderInterface::class); - $this->configReaderMock = $this->getMockForAbstractClass(SitemapConfigReaderInterface::class); - $this->itemProviderMock = $this->getMockForAbstractClass(ItemProviderInterface::class); - $this->request = $this->createMock(Http::class); $this->store = $this->createPartialMock(Store::class, ['isFrontUrlSecure', 'getBaseUrl']); - $this->storeManagerMock = $this->getMockForAbstractClass(StoreManagerInterface::class); + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); $this->storeManagerMock->method('getStore') ->willReturn($this->store); } @@ -423,35 +399,28 @@ protected function prepareSitemapModelMock( } $actualData[$currentFile] .= $str; }; - // Check that all expected lines were written - $this->fileMock->expects( - $this->exactly($expectedWrites) - )->method( - 'write' - )->willReturnCallback( - $streamWriteCallback - ); + $this->tmpFileMock->expects($this->exactly($expectedWrites)) + ->method('write') + ->willReturnCallback($streamWriteCallback); $checkFileCallback = function ($file) use (&$currentFile) { $currentFile = $file; - };// Check that all expected file descriptors were created - $this->directoryMock->expects($this->exactly(count($expectedFile)))->method('openFile') + }; + // Check that all expected file descriptors were created + $this->tmpDirectoryMock->expects($this->exactly(count($expectedFile))) + ->method('openFile') ->willReturnCallback($checkFileCallback); // Check that all file descriptors were closed - $this->fileMock->expects($this->exactly(count($expectedFile))) + $this->tmpFileMock->expects($this->exactly(count($expectedFile))) ->method('close'); if (count($expectedFile) == 1) { - $this->directoryMock->expects($this->once()) + $this->tmpDirectoryMock->expects($this->once()) ->method('renameFile') - ->willReturnCallback( - function ($from, $to) { - Assert::assertEquals('/sitemap-1-1.xml', $from); - Assert::assertEquals('/sitemap.xml', $to); - } - ); + ->with('/sitemap-1-1.xml', '/sitemap.xml', $this->directoryMock) + ->willReturn(true); } // Check robots txt @@ -591,17 +560,11 @@ protected function getModelMock($mockBeforeSave = false) */ private function getModelConstructorArgs() { - $categoryFactory = $this->getMockBuilder(CategoryFactory::class) - ->disableOriginalConstructor() - ->getMock(); - - $productFactory = $this->getMockBuilder(ProductFactory::class) - ->disableOriginalConstructor() - ->getMock(); - - $cmsFactory = $this->getMockBuilder(PageFactory::class) - ->disableOriginalConstructor() - ->getMock(); + $categoryFactory = $this->createMock(CategoryFactory::class); + $productFactory = $this->createMock(ProductFactory::class); + $cmsFactory = $this->createMock(PageFactory::class); + $helperMockSitemap = $this->createMock(Data::class); + $request = $this->createMock(Http::class); $objectManager = new ObjectManager($this); $escaper = $objectManager->getObject(Escaper::class); @@ -614,12 +577,12 @@ private function getModelConstructorArgs() 'productFactory' => $productFactory, 'cmsFactory' => $cmsFactory, 'storeManager' => $this->storeManagerMock, - 'sitemapData' => $this->helperMockSitemap, + 'sitemapData' => $helperMockSitemap, 'filesystem' => $this->filesystemMock, 'itemProvider' => $this->itemProviderMock, 'configReader' => $this->configReaderMock, 'escaper' => $escaper, - 'request' => $this->request, + 'request' => $request, ] ); $constructArguments['resource'] = null;