From aa4b89ca9777d022873557928108f407aed8644f Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" Date: Tue, 29 Jan 2019 09:19:48 +0000 Subject: [PATCH 1/7] MAGETWO-97866: [Magento Cloud] news_from_date and news_to_date dates incorrect in database with scheduled updates Due to unstable work of intl parsing on different platforms --- app/code/Magento/Cron/Model/Schedule.php | 28 +++- .../Cron/Test/Unit/Model/ScheduleTest.php | 144 ++++++++++++++---- 2 files changed, 136 insertions(+), 36 deletions(-) diff --git a/app/code/Magento/Cron/Model/Schedule.php b/app/code/Magento/Cron/Model/Schedule.php index 200b0fd690882..b1dd800e48073 100644 --- a/app/code/Magento/Cron/Model/Schedule.php +++ b/app/code/Magento/Cron/Model/Schedule.php @@ -9,6 +9,7 @@ use Magento\Framework\Exception\CronException; use Magento\Framework\App\ObjectManager; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\Intl\DateTimeFactory; /** * Crontab schedule model @@ -50,6 +51,11 @@ class Schedule extends \Magento\Framework\Model\AbstractModel */ private $timezoneConverter; + /** + * @var DateTimeFactory + */ + private $dateTimeFactory; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -57,6 +63,7 @@ class Schedule extends \Magento\Framework\Model\AbstractModel * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data * @param TimezoneInterface $timezoneConverter + * @param DateTimeFactory $dateTimeFactory */ public function __construct( \Magento\Framework\Model\Context $context, @@ -64,10 +71,12 @@ public function __construct( \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], - TimezoneInterface $timezoneConverter = null + TimezoneInterface $timezoneConverter = null, + DateTimeFactory $dateTimeFactory = null ) { parent::__construct($context, $registry, $resource, $resourceCollection, $data); $this->timezoneConverter = $timezoneConverter ?: ObjectManager::getInstance()->get(TimezoneInterface::class); + $this->dateTimeFactory = $dateTimeFactory ?: ObjectManager::getInstance()->get(DateTimeFactory::class); } /** @@ -111,17 +120,20 @@ public function trySchedule() if (!$e || !$time) { return false; } + $configTimeZone = $this->timezoneConverter->getConfigTimezone(); + $storeDateTime = $this->dateTimeFactory->create(null, new \DateTimeZone($configTimeZone)); if (!is_numeric($time)) { //convert time from UTC to admin store timezone //we assume that all schedules in configuration (crontab.xml and DB tables) are in admin store timezone - $time = $this->timezoneConverter->date($time)->format('Y-m-d H:i'); - $time = strtotime($time); + $dateTimeUtc = $this->dateTimeFactory->create($time, new \DateTimeZone('UTC')); + $time = $dateTimeUtc->getTimestamp(); } - $match = $this->matchCronExpression($e[0], strftime('%M', $time)) - && $this->matchCronExpression($e[1], strftime('%H', $time)) - && $this->matchCronExpression($e[2], strftime('%d', $time)) - && $this->matchCronExpression($e[3], strftime('%m', $time)) - && $this->matchCronExpression($e[4], strftime('%w', $time)); + $time = $storeDateTime->setTimestamp($time); + $match = $this->matchCronExpression($e[0], $time->format('i')) + && $this->matchCronExpression($e[1], $time->format('H')) + && $this->matchCronExpression($e[2], $time->format('d')) + && $this->matchCronExpression($e[3], $time->format('m')) + && $this->matchCronExpression($e[4], $time->format('w')); return $match; } diff --git a/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php b/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php index e9f4c61c7f551..ecee3cdd5d323 100644 --- a/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php +++ b/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php @@ -6,6 +6,9 @@ namespace Magento\Cron\Test\Unit\Model; use Magento\Cron\Model\Schedule; +use Magento\Framework\Intl\DateTimeFactory; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; /** * Class \Magento\Cron\Test\Unit\Model\ObserverTest @@ -18,11 +21,27 @@ class ScheduleTest extends \PHPUnit\Framework\TestCase */ protected $helper; + /** + * @var \Magento\Cron\Model\ResourceModel\Schedule + */ protected $resourceJobMock; + /** + * @var TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $timezoneConverter; + + /** + * @var DateTimeFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $dateTimeFactory; + + /** + * @inheritdoc + */ protected function setUp() { - $this->helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->helper = new ObjectManager($this); $this->resourceJobMock = $this->getMockBuilder(\Magento\Cron\Model\ResourceModel\Schedule::class) ->disableOriginalConstructor() @@ -32,18 +51,30 @@ protected function setUp() $this->resourceJobMock->expects($this->any()) ->method('getIdFieldName') ->will($this->returnValue('id')); + + $this->timezoneConverter = $this->getMockBuilder(TimezoneInterface::class) + ->setMethods(['date']) + ->getMockForAbstractClass(); + + $this->dateTimeFactory = $this->getMockBuilder(DateTimeFactory::class) + ->setMethods(['create']) + ->getMock(); } /** + * Test for SetCronExpr + * * @param string $cronExpression * @param array $expected + * + * @return void * @dataProvider setCronExprDataProvider */ - public function testSetCronExpr($cronExpression, $expected) + public function testSetCronExpr($cronExpression, $expected): void { // 1. Create mocks - /** @var \Magento\Cron\Model\Schedule $model */ - $model = $this->helper->getObject(\Magento\Cron\Model\Schedule::class); + /** @var Schedule $model */ + $model = $this->helper->getObject(Schedule::class); // 2. Run tested method $model->setCronExpr($cronExpression); @@ -61,7 +92,7 @@ public function testSetCronExpr($cronExpression, $expected) * * @return array */ - public function setCronExprDataProvider() + public function setCronExprDataProvider(): array { return [ ['1 2 3 4 5', [1, 2, 3, 4, 5]], @@ -121,27 +152,33 @@ public function setCronExprDataProvider() } /** + * Test for SetCronExprException + * * @param string $cronExpression + * + * @return void * @expectedException \Magento\Framework\Exception\CronException * @dataProvider setCronExprExceptionDataProvider */ - public function testSetCronExprException($cronExpression) + public function testSetCronExprException($cronExpression): void { // 1. Create mocks - /** @var \Magento\Cron\Model\Schedule $model */ - $model = $this->helper->getObject(\Magento\Cron\Model\Schedule::class); + /** @var Schedule $model */ + $model = $this->helper->getObject(Schedule::class); // 2. Run tested method $model->setCronExpr($cronExpression); } /** + * Data provider + * * Here is a list of allowed characters and values for Cron expression * http://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm * * @return array */ - public function setCronExprExceptionDataProvider() + public function setCronExprExceptionDataProvider(): array { return [ [''], @@ -153,17 +190,31 @@ public function setCronExprExceptionDataProvider() } /** + * Test for trySchedule + * * @param int $scheduledAt * @param array $cronExprArr * @param $expected + * + * @return void * @dataProvider tryScheduleDataProvider */ - public function testTrySchedule($scheduledAt, $cronExprArr, $expected) + public function testTrySchedule($scheduledAt, $cronExprArr, $expected): void { // 1. Create mocks + $this->timezoneConverter->method('getConfigTimezone') + ->willReturn('UTC'); + + $this->dateTimeFactory->method('create') + ->willReturn(new \DateTime(null, new \DateTimeZone('UTC'))); + /** @var \Magento\Cron\Model\Schedule $model */ $model = $this->helper->getObject( - \Magento\Cron\Model\Schedule::class + \Magento\Cron\Model\Schedule::class, + [ + 'timezoneConverter' => $this->timezoneConverter, + 'dateTimeFactory' => $this->dateTimeFactory + ] ); // 2. Set fixtures @@ -177,22 +228,29 @@ public function testTrySchedule($scheduledAt, $cronExprArr, $expected) $this->assertEquals($expected, $result); } - public function testTryScheduleWithConversionToAdminStoreTime() + /** + * Test for tryScheduleWithConversionToAdminStoreTime + * + * @return void + */ + public function testTryScheduleWithConversionToAdminStoreTime(): void { $scheduledAt = '2011-12-13 14:15:16'; $cronExprArr = ['*', '*', '*', '*', '*']; - // 1. Create mocks - $timezoneConverter = $this->createMock(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); - $timezoneConverter->expects($this->once()) - ->method('date') - ->with($scheduledAt) - ->willReturn(new \DateTime($scheduledAt)); + $this->timezoneConverter->method('getConfigTimezone') + ->willReturn('UTC'); + + $this->dateTimeFactory->method('create') + ->willReturn(new \DateTime(null, new \DateTimeZone('UTC'))); /** @var \Magento\Cron\Model\Schedule $model */ $model = $this->helper->getObject( \Magento\Cron\Model\Schedule::class, - ['timezoneConverter' => $timezoneConverter] + [ + 'timezoneConverter' => $this->timezoneConverter, + 'dateTimeFactory' => $this->dateTimeFactory + ] ); // 2. Set fixtures @@ -207,9 +265,11 @@ public function testTryScheduleWithConversionToAdminStoreTime() } /** + * Data provider + * * @return array */ - public function tryScheduleDataProvider() + public function tryScheduleDataProvider(): array { $date = '2011-12-13 14:15:16'; return [ @@ -229,12 +289,16 @@ public function tryScheduleDataProvider() } /** + * Test for matchCronExpression + * * @param string $cronExpressionPart * @param int $dateTimePart * @param bool $expectedResult + * + * @return void * @dataProvider matchCronExpressionDataProvider */ - public function testMatchCronExpression($cronExpressionPart, $dateTimePart, $expectedResult) + public function testMatchCronExpression($cronExpressionPart, $dateTimePart, $expectedResult): void { // 1. Create mocks /** @var \Magento\Cron\Model\Schedule $model */ @@ -248,9 +312,11 @@ public function testMatchCronExpression($cronExpressionPart, $dateTimePart, $exp } /** + * Data provider + * * @return array */ - public function matchCronExpressionDataProvider() + public function matchCronExpressionDataProvider(): array { return [ ['*', 0, true], @@ -287,11 +353,15 @@ public function matchCronExpressionDataProvider() } /** + * Test for matchCronExpressionException + * * @param string $cronExpressionPart + * + * @return void * @expectedException \Magento\Framework\Exception\CronException * @dataProvider matchCronExpressionExceptionDataProvider */ - public function testMatchCronExpressionException($cronExpressionPart) + public function testMatchCronExpressionException($cronExpressionPart): void { $dateTimePart = 10; @@ -304,9 +374,11 @@ public function testMatchCronExpressionException($cronExpressionPart) } /** + * Data provider + * * @return array */ - public function matchCronExpressionExceptionDataProvider() + public function matchCronExpressionExceptionDataProvider(): array { return [ ['1/2/3'], //Invalid cron expression, expecting 'match/modulus': 1/2/3 @@ -317,11 +389,15 @@ public function matchCronExpressionExceptionDataProvider() } /** + * Test for GetNumeric + * * @param mixed $param * @param int $expectedResult + * + * @return void * @dataProvider getNumericDataProvider */ - public function testGetNumeric($param, $expectedResult) + public function testGetNumeric($param, $expectedResult): void { // 1. Create mocks /** @var \Magento\Cron\Model\Schedule $model */ @@ -335,9 +411,11 @@ public function testGetNumeric($param, $expectedResult) } /** + * Data provider + * * @return array */ - public function getNumericDataProvider() + public function getNumericDataProvider(): array { return [ [null, false], @@ -362,7 +440,12 @@ public function getNumericDataProvider() ]; } - public function testTryLockJobSuccess() + /** + * Test for tryLockJobSuccess + * + * @return void + */ + public function testTryLockJobSuccess(): void { $scheduleId = 1; @@ -386,7 +469,12 @@ public function testTryLockJobSuccess() $this->assertEquals(Schedule::STATUS_RUNNING, $model->getStatus()); } - public function testTryLockJobFailure() + /** + * Test for tryLockJobFailure + * + * @return void + */ + public function testTryLockJobFailure(): void { $scheduleId = 1; From ba4053a8035d91f4949a6175ee5c9050c6d0ecc3 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" Date: Tue, 29 Jan 2019 11:55:13 +0000 Subject: [PATCH 2/7] MAGETWO-97866: [Magento Cloud] news_from_date and news_to_date dates incorrect in database with scheduled updates Due to unstable work of intl parsing on different platforms --- .../Magento/Cron/Test/Unit/Model/ScheduleTest.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php b/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php index ecee3cdd5d323..7677d11e1a70c 100644 --- a/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php +++ b/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php @@ -272,6 +272,8 @@ public function testTryScheduleWithConversionToAdminStoreTime(): void public function tryScheduleDataProvider(): array { $date = '2011-12-13 14:15:16'; + $timestamp = (new \DateTime($date, new \DateTimeZone('UTC')))->getTimestamp(); + $day = 'Monday'; return [ [$date, [], false], [$date, null, false], @@ -279,12 +281,12 @@ public function tryScheduleDataProvider(): array [$date, [], false], [$date, null, false], [$date, false, false], - [strtotime($date), ['*', '*', '*', '*', '*'], true], - [strtotime($date), ['15', '*', '*', '*', '*'], true], - [strtotime($date), ['*', '14', '*', '*', '*'], true], - [strtotime($date), ['*', '*', '13', '*', '*'], true], - [strtotime($date), ['*', '*', '*', '12', '*'], true], - [strtotime('Monday'), ['*', '*', '*', '*', '1'], true], + [$timestamp, ['*', '*', '*', '*', '*'], true], + [$timestamp, ['15', '*', '*', '*', '*'], true], + [$timestamp, ['*', '14', '*', '*', '*'], true], + [$timestamp, ['*', '*', '13', '*', '*'], true], + [$timestamp, ['*', '*', '*', '12', '*'], true], + [(new \DateTime($day, new \DateTimeZone('UTC')))->getTimestamp(), ['*', '*', '*', '*', '1'], true], ]; } From e9499767ea0ed0671654d187cf8f446399ff539a Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" Date: Wed, 30 Jan 2019 09:35:05 +0000 Subject: [PATCH 3/7] MAGETWO-97866: [Magento Cloud] news_from_date and news_to_date dates incorrect in database with scheduled updates Due to unstable work of intl parsing on different platforms --- app/code/Magento/Cron/Model/Schedule.php | 6 +++--- .../Magento/Cron/Test/Unit/Model/ScheduleTest.php | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Cron/Model/Schedule.php b/app/code/Magento/Cron/Model/Schedule.php index b1dd800e48073..582c7c811b71f 100644 --- a/app/code/Magento/Cron/Model/Schedule.php +++ b/app/code/Magento/Cron/Model/Schedule.php @@ -62,8 +62,8 @@ class Schedule extends \Magento\Framework\Model\AbstractModel * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data - * @param TimezoneInterface $timezoneConverter - * @param DateTimeFactory $dateTimeFactory + * @param TimezoneInterface|null $timezoneConverter + * @param DateTimeFactory|null $dateTimeFactory */ public function __construct( \Magento\Framework\Model\Context $context, @@ -125,7 +125,7 @@ public function trySchedule() if (!is_numeric($time)) { //convert time from UTC to admin store timezone //we assume that all schedules in configuration (crontab.xml and DB tables) are in admin store timezone - $dateTimeUtc = $this->dateTimeFactory->create($time, new \DateTimeZone('UTC')); + $dateTimeUtc = $this->dateTimeFactory->create($time); $time = $dateTimeUtc->getTimestamp(); } $time = $storeDateTime->setTimestamp($time); diff --git a/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php b/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php index 7677d11e1a70c..da5539859a4b5 100644 --- a/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php +++ b/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php @@ -206,14 +206,14 @@ public function testTrySchedule($scheduledAt, $cronExprArr, $expected): void ->willReturn('UTC'); $this->dateTimeFactory->method('create') - ->willReturn(new \DateTime(null, new \DateTimeZone('UTC'))); + ->willReturn(new \DateTime()); /** @var \Magento\Cron\Model\Schedule $model */ $model = $this->helper->getObject( \Magento\Cron\Model\Schedule::class, [ 'timezoneConverter' => $this->timezoneConverter, - 'dateTimeFactory' => $this->dateTimeFactory + 'dateTimeFactory' => $this->dateTimeFactory, ] ); @@ -242,14 +242,14 @@ public function testTryScheduleWithConversionToAdminStoreTime(): void ->willReturn('UTC'); $this->dateTimeFactory->method('create') - ->willReturn(new \DateTime(null, new \DateTimeZone('UTC'))); + ->willReturn(new \DateTime()); /** @var \Magento\Cron\Model\Schedule $model */ $model = $this->helper->getObject( \Magento\Cron\Model\Schedule::class, [ 'timezoneConverter' => $this->timezoneConverter, - 'dateTimeFactory' => $this->dateTimeFactory + 'dateTimeFactory' => $this->dateTimeFactory, ] ); @@ -272,7 +272,7 @@ public function testTryScheduleWithConversionToAdminStoreTime(): void public function tryScheduleDataProvider(): array { $date = '2011-12-13 14:15:16'; - $timestamp = (new \DateTime($date, new \DateTimeZone('UTC')))->getTimestamp(); + $timestamp = (new \DateTime($date))->getTimestamp(); $day = 'Monday'; return [ [$date, [], false], @@ -286,7 +286,7 @@ public function tryScheduleDataProvider(): array [$timestamp, ['*', '14', '*', '*', '*'], true], [$timestamp, ['*', '*', '13', '*', '*'], true], [$timestamp, ['*', '*', '*', '12', '*'], true], - [(new \DateTime($day, new \DateTimeZone('UTC')))->getTimestamp(), ['*', '*', '*', '*', '1'], true], + [(new \DateTime($day))->getTimestamp(), ['*', '*', '*', '*', '1'], true], ]; } From fb7d9fc0c3f89e2eec020be2a489d5c4e9795f94 Mon Sep 17 00:00:00 2001 From: serhii balko Date: Mon, 11 Feb 2019 09:35:03 +0200 Subject: [PATCH 4/7] MAGETWO-97549: Custom Customer Attribute is not updating on one website --- .../Controller/Adminhtml/Index/Save.php | 38 ++++++++-- .../Controller/Adminhtml/Index/SaveTest.php | 69 +++++++++++-------- .../Controller/Adminhtml/IndexTest.php | 36 ++++++++++ 3 files changed, 109 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index cb0343f4ec43b..38ed688a835bc 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -289,11 +289,9 @@ protected function _extractCustomerAddressData(array & $extractedCustomerData) public function execute() { $returnToEdit = false; - $originalRequestData = $this->getRequest()->getPostValue(); - $customerId = $this->getCurrentCustomerId(); - if ($originalRequestData) { + if ($this->getRequest()->getPostValue()) { try { // optional fields might be set in request for future processing by observers in other modules $customerData = $this->_extractCustomerData(); @@ -364,7 +362,7 @@ public function execute() $messages = $exception->getMessage(); } $this->_addSessionErrorMessages($messages); - $this->_getSession()->setCustomerFormData($originalRequestData); + $this->_getSession()->setCustomerFormData($this->retrieveFormattedFormData()); $returnToEdit = true; } catch (\Magento\Framework\Exception\AbstractAggregateException $exception) { $errors = $exception->getErrors(); @@ -373,18 +371,19 @@ public function execute() $messages[] = $error->getMessage(); } $this->_addSessionErrorMessages($messages); - $this->_getSession()->setCustomerFormData($originalRequestData); + $this->_getSession()->setCustomerFormData($this->retrieveFormattedFormData()); $returnToEdit = true; } catch (LocalizedException $exception) { $this->_addSessionErrorMessages($exception->getMessage()); - $this->_getSession()->setCustomerFormData($originalRequestData); + $this->_getSession()->setCustomerFormData($this->retrieveFormattedFormData()); $returnToEdit = true; } catch (\Exception $exception) { $this->messageManager->addException($exception, __('Something went wrong while saving the customer.')); - $this->_getSession()->setCustomerFormData($originalRequestData); + $this->_getSession()->setCustomerFormData($this->retrieveFormattedFormData()); $returnToEdit = true; } } + $resultRedirect = $this->resultRedirectFactory->create(); if ($returnToEdit) { if ($customerId) { @@ -489,4 +488,29 @@ private function disableAddressValidation($customer) $addressModel->setShouldIgnoreValidation(true); } } + + /** + * Retrieve formatted form data + * + * @return array + */ + private function retrieveFormattedFormData(): array + { + $originalRequestData = $this->getRequest()->getPostValue(); + + /* Customer data filtration */ + if (isset($originalRequestData['customer'])) { + $customerData = $this->_extractData( + 'adminhtml_customer', + CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + [], + 'customer' + ); + + $customerData = array_intersect_key($customerData, $originalRequestData['customer']); + $originalRequestData['customer'] = array_merge($originalRequestData['customer'], $customerData); + } + + return $originalRequestData; + } } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php index 8d802e907a810..57f384d32d980 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php @@ -699,22 +699,24 @@ public function testExecuteWithNewCustomerAndValidationException() 'customer' => [ 'coolness' => false, 'disable_auto_group_change' => 'false', + 'dob' => '3/12/1996', ], 'subscription' => $subscription, ]; $extractedData = [ 'coolness' => false, 'disable_auto_group_change' => 'false', + 'dob' => '1996-03-12', ]; /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( \Magento\Customer\Api\Data\AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); - $attributeMock->expects($this->once()) + $attributeMock->expects($this->exactly(2)) ->method('getAttributeCode') ->willReturn('coolness'); - $attributeMock->expects($this->once()) + $attributeMock->expects($this->exactly(2)) ->method('getFrontendInput') ->willReturn('int'); $attributes = [$attributeMock]; @@ -737,12 +739,12 @@ public function testExecuteWithNewCustomerAndValidationException() $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() ->getMock(); - $objectMock->expects($this->once()) + $objectMock->expects($this->exactly(2)) ->method('getData') ->with('customer') ->willReturn($postValue['customer']); - $this->objectFactoryMock->expects($this->once()) + $this->objectFactoryMock->expects($this->exactly(2)) ->method('create') ->with(['data' => $postValue]) ->willReturn($objectMock); @@ -750,19 +752,19 @@ public function testExecuteWithNewCustomerAndValidationException() $customerFormMock = $this->getMockBuilder( \Magento\Customer\Model\Metadata\Form::class )->disableOriginalConstructor()->getMock(); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('extractData') ->with($this->requestMock, 'customer') ->willReturn($extractedData); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('compactData') ->with($extractedData) ->willReturn($extractedData); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('getAttributes') ->willReturn($attributes); - $this->formFactoryMock->expects($this->once()) + $this->formFactoryMock->expects($this->exactly(2)) ->method('create') ->with( CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, @@ -810,7 +812,10 @@ public function testExecuteWithNewCustomerAndValidationException() $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') - ->with($postValue); + ->with([ + 'customer' => $extractedData, + 'subscription' => $subscription, + ]); /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) @@ -841,22 +846,24 @@ public function testExecuteWithNewCustomerAndLocalizedException() 'customer' => [ 'coolness' => false, 'disable_auto_group_change' => 'false', + 'dob' => '3/12/1996', ], 'subscription' => $subscription, ]; $extractedData = [ 'coolness' => false, 'disable_auto_group_change' => 'false', + 'dob' => '1996-03-12', ]; /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( \Magento\Customer\Api\Data\AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); - $attributeMock->expects($this->once()) + $attributeMock->expects($this->exactly(2)) ->method('getAttributeCode') ->willReturn('coolness'); - $attributeMock->expects($this->once()) + $attributeMock->expects($this->exactly(2)) ->method('getFrontendInput') ->willReturn('int'); $attributes = [$attributeMock]; @@ -879,12 +886,12 @@ public function testExecuteWithNewCustomerAndLocalizedException() $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() ->getMock(); - $objectMock->expects($this->once()) + $objectMock->expects($this->exactly(2)) ->method('getData') ->with('customer') ->willReturn($postValue['customer']); - $this->objectFactoryMock->expects($this->once()) + $this->objectFactoryMock->expects($this->exactly(2)) ->method('create') ->with(['data' => $postValue]) ->willReturn($objectMock); @@ -893,19 +900,19 @@ public function testExecuteWithNewCustomerAndLocalizedException() $customerFormMock = $this->getMockBuilder( \Magento\Customer\Model\Metadata\Form::class )->disableOriginalConstructor()->getMock(); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('extractData') ->with($this->requestMock, 'customer') ->willReturn($extractedData); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('compactData') ->with($extractedData) ->willReturn($extractedData); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('getAttributes') ->willReturn($attributes); - $this->formFactoryMock->expects($this->once()) + $this->formFactoryMock->expects($this->exactly(2)) ->method('create') ->with( CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, @@ -952,7 +959,10 @@ public function testExecuteWithNewCustomerAndLocalizedException() $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') - ->with($postValue); + ->with([ + 'customer' => $extractedData, + 'subscription' => $subscription, + ]); /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) @@ -983,22 +993,24 @@ public function testExecuteWithNewCustomerAndException() 'customer' => [ 'coolness' => false, 'disable_auto_group_change' => 'false', + 'dob' => '3/12/1996', ], 'subscription' => $subscription, ]; $extractedData = [ 'coolness' => false, 'disable_auto_group_change' => 'false', + 'dob' => '1996-03-12', ]; /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( \Magento\Customer\Api\Data\AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); - $attributeMock->expects($this->once()) + $attributeMock->expects($this->exactly(2)) ->method('getAttributeCode') ->willReturn('coolness'); - $attributeMock->expects($this->once()) + $attributeMock->expects($this->exactly(2)) ->method('getFrontendInput') ->willReturn('int'); $attributes = [$attributeMock]; @@ -1021,12 +1033,12 @@ public function testExecuteWithNewCustomerAndException() $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() ->getMock(); - $objectMock->expects($this->once()) + $objectMock->expects($this->exactly(2)) ->method('getData') ->with('customer') ->willReturn($postValue['customer']); - $this->objectFactoryMock->expects($this->once()) + $this->objectFactoryMock->expects($this->exactly(2)) ->method('create') ->with(['data' => $postValue]) ->willReturn($objectMock); @@ -1034,19 +1046,19 @@ public function testExecuteWithNewCustomerAndException() $customerFormMock = $this->getMockBuilder( \Magento\Customer\Model\Metadata\Form::class )->disableOriginalConstructor()->getMock(); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('extractData') ->with($this->requestMock, 'customer') ->willReturn($extractedData); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('compactData') ->with($extractedData) ->willReturn($extractedData); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('getAttributes') ->willReturn($attributes); - $this->formFactoryMock->expects($this->once()) + $this->formFactoryMock->expects($this->exactly(2)) ->method('create') ->with( CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, @@ -1095,7 +1107,10 @@ public function testExecuteWithNewCustomerAndException() $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') - ->with($postValue); + ->with([ + 'customer' => $extractedData, + 'subscription' => $subscription, + ]); /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index 292d61c392d06..e477a6c5adb84 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -296,6 +296,42 @@ public function testSaveActionCoreException() $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new/key/')); } + /** + * @magentoDataFixture Magento/Customer/_files/customer_sample.php + */ + public function testSaveActionCoreExceptionFormatFormData() + { + $post = [ + 'customer' => [ + 'website_id' => 1, + 'email' => 'customer@example.com', + 'dob' => '12/3/1996', + ], + ]; + $postFormatted = [ + 'customer' => [ + 'website_id' => 1, + 'email' => 'customer@example.com', + 'dob' => '1996-12-03', + ], + ]; + $this->getRequest()->setPostValue($post); + $this->dispatch('backend/customer/index/save'); + /* + * Check that error message is set + */ + $this->assertSessionMessages( + $this->equalTo(['A customer with the same email already exists in an associated website.']), + \Magento\Framework\Message\MessageInterface::TYPE_ERROR + ); + $this->assertEquals( + $postFormatted, + Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getCustomerFormData(), + 'Customer form data should be formatted' + ); + $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new/key/')); + } + /** * @magentoDataFixture Magento/Customer/_files/customer_sample.php */ From d4ae9417e1efd8eb68c8dcf7141d67e6fa3f277f Mon Sep 17 00:00:00 2001 From: serhii balko Date: Mon, 11 Feb 2019 11:39:52 +0200 Subject: [PATCH 5/7] MAGETWO-97549: Custom Customer Attribute is not updating on one website --- .../Magento/Customer/Controller/Adminhtml/IndexTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index e477a6c5adb84..d9e20461ca534 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -321,7 +321,7 @@ public function testSaveActionCoreExceptionFormatFormData() * Check that error message is set */ $this->assertSessionMessages( - $this->equalTo(['A customer with the same email already exists in an associated website.']), + $this->equalTo(['A customer with the same email address already exists in an associated website.']), \Magento\Framework\Message\MessageInterface::TYPE_ERROR ); $this->assertEquals( From a9cc22f9be01b60317f5be57462fd5b2545c59b1 Mon Sep 17 00:00:00 2001 From: serhii balko Date: Mon, 11 Feb 2019 13:59:43 +0200 Subject: [PATCH 6/7] MAGETWO-97549: Custom Customer Attribute is not updating on one website --- .../Controller/Adminhtml/IndexTest.php | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index d9e20461ca534..1b7f2c1f7efdd 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -303,30 +303,39 @@ public function testSaveActionCoreExceptionFormatFormData() { $post = [ 'customer' => [ + 'middlename' => 'test middlename', 'website_id' => 1, + 'firstname' => 'test firstname', + 'lastname' => 'test lastname', 'email' => 'customer@example.com', 'dob' => '12/3/1996', ], ]; - $postFormatted = [ - 'customer' => [ - 'website_id' => 1, - 'email' => 'customer@example.com', - 'dob' => '1996-12-03', - ], + $postCustomerFormatted = [ + 'middlename' => 'test middlename', + 'website_id' => 1, + 'firstname' => 'test firstname', + 'lastname' => 'test lastname', + 'email' => 'customer@example.com', + 'dob' => '1996-12-03', ]; - $this->getRequest()->setPostValue($post); + + $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/save'); /* - * Check that error message is set - */ + * Check that error message is set + */ $this->assertSessionMessages( $this->equalTo(['A customer with the same email address already exists in an associated website.']), \Magento\Framework\Message\MessageInterface::TYPE_ERROR ); + + $customerFormData = Bootstrap::getObjectManager() + ->get(\Magento\Backend\Model\Session::class) + ->getCustomerFormData(); $this->assertEquals( - $postFormatted, - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getCustomerFormData(), + $postCustomerFormatted, + $customerFormData['customer'], 'Customer form data should be formatted' ); $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new/key/')); From 0e373538ded9abe3051d8e9e7a04ca1b6e1d173f Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr Date: Wed, 13 Feb 2019 17:17:09 +0200 Subject: [PATCH 7/7] MAGETWO-91607: Incorrect page caching with enabled maintenance mode when accessing from the exclude IP list. --- .../Observer/SwitchPageCacheOnMaintenance.php | 108 ++++++++++ .../PageCacheState.php | 74 +++++++ .../SwitchPageCacheOnMaintenanceTest.php | 164 +++++++++++++++ app/code/Magento/PageCache/etc/events.xml | 3 + .../PageCacheStateTest.php | 69 ++++++ .../Magento/Framework/App/MaintenanceMode.php | 14 +- .../App/Test/Unit/MaintenanceModeTest.php | 199 +++++++++++++----- 7 files changed, 572 insertions(+), 59 deletions(-) create mode 100644 app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance.php create mode 100644 app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php create mode 100644 app/code/Magento/PageCache/Test/Unit/Observer/SwitchPageCacheOnMaintenanceTest.php create mode 100644 dev/tests/integration/testsuite/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheStateTest.php diff --git a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance.php b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance.php new file mode 100644 index 0000000000000..7017da27eee93 --- /dev/null +++ b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance.php @@ -0,0 +1,108 @@ +cacheManager = $cacheManager; + $this->pageCacheStateStorage = $pageCacheStateStorage; + } + + /** + * Switches Full Page Cache. + * + * Depending on enabling or disabling Maintenance Mode it turns off or restores Full Page Cache state. + * + * @param Observer $observer + * @return void + */ + public function execute(Observer $observer): void + { + if ($observer->getData('isOn')) { + $this->pageCacheStateStorage->save($this->isFullPageCacheEnabled()); + $this->turnOffFullPageCache(); + } else { + $this->restoreFullPageCacheState(); + } + } + + /** + * Turns off Full Page Cache. + * + * @return void + */ + private function turnOffFullPageCache(): void + { + if (!$this->isFullPageCacheEnabled()) { + return; + } + + $this->cacheManager->clean([PageCacheType::TYPE_IDENTIFIER]); + $this->cacheManager->setEnabled([PageCacheType::TYPE_IDENTIFIER], false); + } + + /** + * Full Page Cache state. + * + * @return bool + */ + private function isFullPageCacheEnabled(): bool + { + $cacheStatus = $this->cacheManager->getStatus(); + + if (!array_key_exists(PageCacheType::TYPE_IDENTIFIER, $cacheStatus)) { + return false; + } + + return (bool)$cacheStatus[PageCacheType::TYPE_IDENTIFIER]; + } + + /** + * Restores Full Page Cache state. + * + * Returns FPC to previous state that was before maintenance mode turning on. + * + * @return void + */ + private function restoreFullPageCacheState(): void + { + $storedPageCacheState = $this->pageCacheStateStorage->isEnabled(); + $this->pageCacheStateStorage->flush(); + + if ($storedPageCacheState) { + $this->cacheManager->setEnabled([PageCacheType::TYPE_IDENTIFIER], true); + } + } +} diff --git a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php new file mode 100644 index 0000000000000..e4cadf728f2ea --- /dev/null +++ b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php @@ -0,0 +1,74 @@ +flagDir = $fileSystem->getDirectoryWrite(DirectoryList::VAR_DIR); + } + + /** + * Saves Full Page Cache state. + * + * Saves FPC state across requests. + * + * @param bool $state + * @return void + */ + public function save(bool $state): void + { + $this->flagDir->writeFile(self::PAGE_CACHE_STATE_FILENAME, (string)$state); + } + + /** + * Returns stored Full Page Cache state. + * + * @return bool + */ + public function isEnabled(): bool + { + if (!$this->flagDir->isExist(self::PAGE_CACHE_STATE_FILENAME)) { + return false; + } + + return (bool)$this->flagDir->readFile(self::PAGE_CACHE_STATE_FILENAME); + } + + /** + * Flushes Page Cache state storage. + * + * @return void + */ + public function flush(): void + { + $this->flagDir->delete(self::PAGE_CACHE_STATE_FILENAME); + } +} diff --git a/app/code/Magento/PageCache/Test/Unit/Observer/SwitchPageCacheOnMaintenanceTest.php b/app/code/Magento/PageCache/Test/Unit/Observer/SwitchPageCacheOnMaintenanceTest.php new file mode 100644 index 0000000000000..2dbb815c70925 --- /dev/null +++ b/app/code/Magento/PageCache/Test/Unit/Observer/SwitchPageCacheOnMaintenanceTest.php @@ -0,0 +1,164 @@ +cacheManager = $this->createMock(Manager::class); + $this->pageCacheStateStorage = $this->createMock(PageCacheState::class); + $this->observer = $this->createMock(Observer::class); + + $this->model = $objectManager->getObject(SwitchPageCacheOnMaintenance::class, [ + 'cacheManager' => $this->cacheManager, + 'pageCacheStateStorage' => $this->pageCacheStateStorage, + ]); + } + + /** + * Tests execute when setting maintenance mode to on. + * + * @param array $cacheStatus + * @param bool $cacheState + * @param int $flushCacheCalls + * @return void + * @dataProvider enablingPageCacheStateProvider + */ + public function testExecuteWhileMaintenanceEnabling( + array $cacheStatus, + bool $cacheState, + int $flushCacheCalls + ): void { + $this->observer->method('getData') + ->with('isOn') + ->willReturn(true); + $this->cacheManager->method('getStatus') + ->willReturn($cacheStatus); + + // Page Cache state will be stored. + $this->pageCacheStateStorage->expects($this->once()) + ->method('save') + ->with($cacheState); + + // Page Cache will be cleaned and disabled + $this->cacheManager->expects($this->exactly($flushCacheCalls)) + ->method('clean') + ->with([PageCacheType::TYPE_IDENTIFIER]); + $this->cacheManager->expects($this->exactly($flushCacheCalls)) + ->method('setEnabled') + ->with([PageCacheType::TYPE_IDENTIFIER], false); + + $this->model->execute($this->observer); + } + + /** + * Tests execute when setting Maintenance Mode to off. + * + * @param bool $storedCacheState + * @param int $enableCacheCalls + * @return void + * @dataProvider disablingPageCacheStateProvider + */ + public function testExecuteWhileMaintenanceDisabling(bool $storedCacheState, int $enableCacheCalls): void + { + $this->observer->method('getData') + ->with('isOn') + ->willReturn(false); + + $this->pageCacheStateStorage->method('isEnabled') + ->willReturn($storedCacheState); + + // Nullify Page Cache state. + $this->pageCacheStateStorage->expects($this->once()) + ->method('flush'); + + // Page Cache will be enabled. + $this->cacheManager->expects($this->exactly($enableCacheCalls)) + ->method('setEnabled') + ->with([PageCacheType::TYPE_IDENTIFIER]); + + $this->model->execute($this->observer); + } + + /** + * Page Cache state data provider. + * + * @return array + */ + public function enablingPageCacheStateProvider(): array + { + return [ + 'page_cache_is_enable' => [ + 'cache_status' => [PageCacheType::TYPE_IDENTIFIER => 1], + 'cache_state' => true, + 'flush_cache_calls' => 1, + ], + 'page_cache_is_missing_in_system' => [ + 'cache_status' => [], + 'cache_state' => false, + 'flush_cache_calls' => 0, + ], + 'page_cache_is_disable' => [ + 'cache_status' => [PageCacheType::TYPE_IDENTIFIER => 0], + 'cache_state' => false, + 'flush_cache_calls' => 0, + ], + ]; + } + + /** + * Page Cache state data provider. + * + * @return array + */ + public function disablingPageCacheStateProvider(): array + { + return [ + ['stored_cache_state' => true, 'enable_cache_calls' => 1], + ['stored_cache_state' => false, 'enable_cache_calls' => 0], + ]; + } +} diff --git a/app/code/Magento/PageCache/etc/events.xml b/app/code/Magento/PageCache/etc/events.xml index 7584f5f36d69c..3f0a2532ae60a 100644 --- a/app/code/Magento/PageCache/etc/events.xml +++ b/app/code/Magento/PageCache/etc/events.xml @@ -57,4 +57,7 @@ + + + diff --git a/dev/tests/integration/testsuite/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheStateTest.php b/dev/tests/integration/testsuite/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheStateTest.php new file mode 100644 index 0000000000000..dc2447e8b4c1f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheStateTest.php @@ -0,0 +1,69 @@ +pageCacheStateStorage = $objectManager->get(PageCacheState::class); + } + + /** + * Tests save state. + * + * @param bool $state + * @return void + * @dataProvider saveStateProvider + */ + public function testSave(bool $state): void + { + $this->pageCacheStateStorage->save($state); + $this->assertEquals($state, $this->pageCacheStateStorage->isEnabled()); + } + + /** + * Tests flush state. + * + * @return void + */ + public function testFlush(): void + { + $this->pageCacheStateStorage->save(true); + $this->assertTrue($this->pageCacheStateStorage->isEnabled()); + $this->pageCacheStateStorage->flush(); + $this->assertFalse($this->pageCacheStateStorage->isEnabled()); + } + + /** + * Save state provider. + * + * @return array + */ + public function saveStateProvider(): array + { + return [[true], [false]]; + } +} diff --git a/lib/internal/Magento/Framework/App/MaintenanceMode.php b/lib/internal/Magento/Framework/App/MaintenanceMode.php index 4e4328cb72aef..e813522a01513 100644 --- a/lib/internal/Magento/Framework/App/MaintenanceMode.php +++ b/lib/internal/Magento/Framework/App/MaintenanceMode.php @@ -7,6 +7,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; +use Magento\Framework\Event\Manager; /** * Application Maintenance Mode @@ -39,13 +40,18 @@ class MaintenanceMode protected $flagDir; /** - * Constructor - * + * @var Manager + */ + private $eventManager; + + /** * @param \Magento\Framework\Filesystem $filesystem + * @param Manager|null $eventManager */ - public function __construct(Filesystem $filesystem) + public function __construct(Filesystem $filesystem, ?Manager $eventManager = null) { $this->flagDir = $filesystem->getDirectoryWrite(self::FLAG_DIR); + $this->eventManager = $eventManager ?: ObjectManager::getInstance()->get(Manager::class); } /** @@ -73,6 +79,8 @@ public function isOn($remoteAddr = '') */ public function set($isOn) { + $this->eventManager->dispatch('maintenance_mode_changed', ['isOn' => $isOn]); + if ($isOn) { return $this->flagDir->touch(self::FLAG_FILENAME); } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/MaintenanceModeTest.php b/lib/internal/Magento/Framework/App/Test/Unit/MaintenanceModeTest.php index 5d1c22a38af4d..5970d2561660a 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/MaintenanceModeTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/MaintenanceModeTest.php @@ -6,9 +6,17 @@ namespace Magento\Framework\App\Test\Unit; -use \Magento\Framework\App\MaintenanceMode; +use Magento\Framework\App\MaintenanceMode; +use Magento\Framework\Event\Manager; +use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Filesystem; +use PHPUnit\Framework\TestCase; -class MaintenanceModeTest extends \PHPUnit\Framework\TestCase +/** + * MaintenanceMode Test + */ +class MaintenanceModeTest extends TestCase { /** * @var MaintenanceMode @@ -16,141 +24,213 @@ class MaintenanceModeTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \Magento\Framework\Filesystem\Directory\WriteInterface | \PHPUnit_Framework_MockObject_MockObject + * @var WriteInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $flagDir; + /** + * @var Manager|\PHPUnit\Framework\MockObject\MockObject + */ + private $eventManager; + + /** + * @inheritdoc + */ protected function setup() { - $this->flagDir = $this->getMockForAbstractClass(\Magento\Framework\Filesystem\Directory\WriteInterface::class); - $filesystem = $this->createMock(\Magento\Framework\Filesystem::class); - $filesystem->expects($this->any()) - ->method('getDirectoryWrite') - ->will($this->returnValue($this->flagDir)); + $this->flagDir = $this->getMockForAbstractClass(WriteInterface::class); + $filesystem = $this->createMock(Filesystem::class); + $filesystem->method('getDirectoryWrite') + ->willReturn($this->flagDir); + $this->eventManager = $this->createMock(Manager::class); - $this->model = new MaintenanceMode($filesystem); + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject(MaintenanceMode::class, [ + 'filesystem' => $filesystem, + 'eventManager' => $this->eventManager, + ]); } + /** + * Is On initial test + * + * @return void + */ public function testIsOnInitial() { - $this->flagDir->expects($this->once())->method('isExist') + $this->flagDir->expects($this->once()) + ->method('isExist') ->with(MaintenanceMode::FLAG_FILENAME) - ->will($this->returnValue(false)); + ->willReturn(false); $this->assertFalse($this->model->isOn()); } + /** + * Is On without ip test + * + * @return void + */ public function testisOnWithoutIP() { $mapisExist = [ [MaintenanceMode::FLAG_FILENAME, true], [MaintenanceMode::IP_FILENAME, false], ]; - $this->flagDir->expects($this->exactly(2))->method('isExist') - ->will(($this->returnValueMap($mapisExist))); + $this->flagDir->expects($this->exactly(2)) + ->method('isExist') + ->willReturnMap($mapisExist); $this->assertTrue($this->model->isOn()); } + /** + * Is On with IP test + * + * @return void + */ public function testisOnWithIP() { $mapisExist = [ [MaintenanceMode::FLAG_FILENAME, true], [MaintenanceMode::IP_FILENAME, true], ]; - $this->flagDir->expects($this->exactly(2))->method('isExist') - ->will(($this->returnValueMap($mapisExist))); + $this->flagDir->expects($this->exactly(2)) + ->method('isExist') + ->willReturnMap($mapisExist); $this->assertFalse($this->model->isOn()); } + /** + * Is On with IP but no Maintenance files test + * + * @return void + */ public function testisOnWithIPNoMaintenance() { - $this->flagDir->expects($this->once())->method('isExist') + $this->flagDir->expects($this->once()) + ->method('isExist') ->with(MaintenanceMode::FLAG_FILENAME) ->willReturn(false); $this->assertFalse($this->model->isOn()); } + /** + * Maintenance Mode On test + * + * Tests common scenario with Full Page Cache is set to On + * + * @return void + */ public function testMaintenanceModeOn() { - $this->flagDir->expects($this->at(0))->method('isExist')->with(MaintenanceMode::FLAG_FILENAME) - ->will($this->returnValue(false)); - $this->flagDir->expects($this->at(1))->method('touch')->will($this->returnValue(true)); - $this->flagDir->expects($this->at(2))->method('isExist')->with(MaintenanceMode::FLAG_FILENAME) - ->will($this->returnValue(true)); - $this->flagDir->expects($this->at(3))->method('isExist')->with(MaintenanceMode::IP_FILENAME) - ->will($this->returnValue(false)); + $this->eventManager->expects($this->once()) + ->method('dispatch') + ->with('maintenance_mode_changed', ['isOn' => true]); - $this->assertFalse($this->model->isOn()); - $this->assertTrue($this->model->set(true)); - $this->assertTrue($this->model->isOn()); + $this->flagDir->expects($this->once()) + ->method('touch') + ->with(MaintenanceMode::FLAG_FILENAME); + + $this->model->set(true); } + /** + * Maintenance Mode Off test + * + * Tests common scenario when before Maintenance Mode Full Page Cache was setted to on + * + * @return void + */ public function testMaintenanceModeOff() { - $this->flagDir->expects($this->at(0))->method('isExist')->with(MaintenanceMode::FLAG_FILENAME) - ->will($this->returnValue(true)); - $this->flagDir->expects($this->at(1))->method('delete')->with(MaintenanceMode::FLAG_FILENAME) - ->will($this->returnValue(false)); - $this->flagDir->expects($this->at(2))->method('isExist')->with(MaintenanceMode::FLAG_FILENAME) - ->will($this->returnValue(false)); - - $this->assertFalse($this->model->set(false)); - $this->assertFalse($this->model->isOn()); + $this->eventManager->expects($this->once()) + ->method('dispatch') + ->with('maintenance_mode_changed', ['isOn' => false]); + + $this->flagDir->method('isExist') + ->with(MaintenanceMode::FLAG_FILENAME) + ->willReturn(true); + + $this->flagDir->expects($this->once()) + ->method('delete') + ->with(MaintenanceMode::FLAG_FILENAME); + + $this->model->set(false); } + /** + * Set empty addresses test + * + * @return void + */ public function testSetAddresses() { $mapisExist = [ [MaintenanceMode::FLAG_FILENAME, true], [MaintenanceMode::IP_FILENAME, true], ]; - $this->flagDir->expects($this->any())->method('isExist')->will($this->returnValueMap($mapisExist)); - $this->flagDir->expects($this->any())->method('writeFile') + $this->flagDir->method('isExist') + ->willReturnMap($mapisExist); + $this->flagDir->method('writeFile') ->with(MaintenanceMode::IP_FILENAME) - ->will($this->returnValue(true)); + ->willReturn(true); - $this->flagDir->expects($this->any())->method('readFile') + $this->flagDir->method('readFile') ->with(MaintenanceMode::IP_FILENAME) - ->will($this->returnValue('')); + ->willReturn(''); $this->model->setAddresses(''); $this->assertEquals([''], $this->model->getAddressInfo()); } + /** + * Set single address test + * + * @return void + */ public function testSetSingleAddresses() { $mapisExist = [ [MaintenanceMode::FLAG_FILENAME, true], [MaintenanceMode::IP_FILENAME, true], ]; - $this->flagDir->expects($this->any())->method('isExist')->will($this->returnValueMap($mapisExist)); - $this->flagDir->expects($this->any())->method('delete')->will($this->returnValueMap($mapisExist)); + $this->flagDir->method('isExist') + ->willReturnMap($mapisExist); + $this->flagDir->method('delete') + ->willReturnMap($mapisExist); - $this->flagDir->expects($this->any())->method('writeFile') - ->will($this->returnValue(10)); + $this->flagDir->method('writeFile') + ->willReturn(10); - $this->flagDir->expects($this->any())->method('readFile') + $this->flagDir->method('readFile') ->with(MaintenanceMode::IP_FILENAME) - ->will($this->returnValue('address1')); + ->willReturn('address1'); $this->model->setAddresses('address1'); $this->assertEquals(['address1'], $this->model->getAddressInfo()); } + /** + * Is On when multiple addresses test was setted + * + * @return void + */ public function testOnSetMultipleAddresses() { $mapisExist = [ [MaintenanceMode::FLAG_FILENAME, true], [MaintenanceMode::IP_FILENAME, true], ]; - $this->flagDir->expects($this->any())->method('isExist')->will($this->returnValueMap($mapisExist)); - $this->flagDir->expects($this->any())->method('delete')->will($this->returnValueMap($mapisExist)); + $this->flagDir->method('isExist') + ->willReturnMap($mapisExist); + $this->flagDir->method('delete') + ->willReturnMap($mapisExist); - $this->flagDir->expects($this->any())->method('writeFile') - ->will($this->returnValue(10)); + $this->flagDir->method('writeFile') + ->willReturn(10); - $this->flagDir->expects($this->any())->method('readFile') + $this->flagDir->method('readFile') ->with(MaintenanceMode::IP_FILENAME) - ->will($this->returnValue('address1,10.50.60.123')); + ->willReturn('address1,10.50.60.123'); $expectedArray = ['address1', '10.50.60.123']; $this->model->setAddresses('address1,10.50.60.123'); @@ -159,18 +239,25 @@ public function testOnSetMultipleAddresses() $this->assertTrue($this->model->isOn('address3')); } + /** + * Is Off when multiple addresses test was setted + * + * @return void + */ public function testOffSetMultipleAddresses() { $mapisExist = [ [MaintenanceMode::FLAG_FILENAME, false], [MaintenanceMode::IP_FILENAME, true], ]; - $this->flagDir->expects($this->any())->method('isExist')->will($this->returnValueMap($mapisExist)); - $this->flagDir->expects($this->any())->method('delete')->will($this->returnValueMap($mapisExist)); + $this->flagDir->method('isExist') + ->willReturnMap($mapisExist); + $this->flagDir->method('delete') + ->willReturnMap($mapisExist); - $this->flagDir->expects($this->any())->method('readFile') + $this->flagDir->method('readFile') ->with(MaintenanceMode::IP_FILENAME) - ->will($this->returnValue('address1,10.50.60.123')); + ->willReturn('address1,10.50.60.123'); $expectedArray = ['address1', '10.50.60.123']; $this->model->setAddresses('address1,10.50.60.123');