diff --git a/app/code/Magento/Catalog/Setup/UpgradeData.php b/app/code/Magento/Catalog/Setup/UpgradeData.php index 7ffb345202836..629c6c45e7049 100644 --- a/app/code/Magento/Catalog/Setup/UpgradeData.php +++ b/app/code/Magento/Catalog/Setup/UpgradeData.php @@ -401,13 +401,14 @@ private function changePriceAttributeDefaultScope($categorySetup) $entityTypeId = $categorySetup->getEntityTypeId(\Magento\Catalog\Model\Product::ENTITY); foreach (['price', 'cost', 'special_price'] as $attributeCode) { $attribute = $categorySetup->getAttribute($entityTypeId, $attributeCode); - $categorySetup->updateAttribute( - $entityTypeId, - $attribute['attribute_id'], - 'is_global', - \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL - ); - + if (isset($attribute['attribute_id'])) { + $categorySetup->updateAttribute( + $entityTypeId, + $attribute['attribute_id'], + 'is_global', + \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL + ); + } } } } diff --git a/app/code/Magento/Config/Console/Command/ConfigSet/ProcessorFacade.php b/app/code/Magento/Config/Console/Command/ConfigSet/ProcessorFacade.php new file mode 100644 index 0000000000000..1d9f12b7a194d --- /dev/null +++ b/app/code/Magento/Config/Console/Command/ConfigSet/ProcessorFacade.php @@ -0,0 +1,90 @@ +scopeValidator = $scopeValidator; + $this->pathValidator = $pathValidator; + $this->configSetProcessorFactory = $configSetProcessorFactory; + } + + /** + * Processes config:set command. + * + * @param string $path The configuration path in format group/section/field_name + * @param string $value The configuration value + * @param string $scope The configuration scope (default, website, or store) + * @param string $scopeCode The scope code + * @param boolean $lock The lock flag + * @return string Processor response message + * @throws LocalizedException If scope validation failed + * @throws ValidatorException If path validation failed + * @throws CouldNotSaveException If processing failed + * @throws ConfigurationMismatchException If processor can not be instantiated + */ + public function process($path, $value, $scope, $scopeCode, $lock) + { + $this->scopeValidator->isValid($scope, $scopeCode); + $this->pathValidator->validate($path); + + $processor = $lock + ? $this->configSetProcessorFactory->create(ConfigSetProcessorFactory::TYPE_LOCK) + : $this->configSetProcessorFactory->create(ConfigSetProcessorFactory::TYPE_DEFAULT); + $message = $lock + ? 'Value was saved and locked.' + : 'Value was saved.'; + + // The processing flow depends on --lock option. + $processor->process($path, $value, $scope, $scopeCode); + + return $message; + } +} diff --git a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php index f757115068da2..747ae620ea6a8 100644 --- a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php +++ b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php @@ -5,18 +5,17 @@ */ namespace Magento\Config\Console\Command; -use Magento\Config\Console\Command\ConfigSet\ConfigSetProcessorFactory; -use Magento\Config\Model\Config\PathValidatorFactory; use Magento\Framework\App\Area; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\App\Scope\ValidatorInterface; +use Magento\Framework\App\State; use Magento\Framework\Config\ScopeInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Console\Cli; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Magento\Config\Console\Command\ConfigSet\ProcessorFacadeFactory; /** * Command provides possibility to change system configuration. @@ -34,49 +33,39 @@ class ConfigSetCommand extends Command /**#@-*/ /** - * The factory for config:set processors. + * Scope manager. * - * @var ConfigSetProcessorFactory + * @var ScopeInterface */ - private $configSetProcessorFactory; + private $scope; /** - * Scope validator. + * Application state. * - * @var ValidatorInterface + * @var State */ - private $validator; + private $state; /** - * Scope manager. + * The processor facade factory * - * @var ScopeInterface + * @var ProcessorFacadeFactory */ - private $scope; + private $processorFacadeFactory; /** - * The factory for path validator. - * - * @var PathValidatorFactory - */ - private $pathValidatorFactory; - - /** - * @param ConfigSetProcessorFactory $configSetProcessorFactory The factory for config:set processors - * @param ValidatorInterface $validator Scope validator * @param ScopeInterface $scope Scope manager - * @param PathValidatorFactory $pathValidatorFactory The factory for path validator + * @param State $state Application state + * @param ProcessorFacadeFactory $processorFacadeFactory The processor facade factory */ public function __construct( - ConfigSetProcessorFactory $configSetProcessorFactory, - ValidatorInterface $validator, ScopeInterface $scope, - PathValidatorFactory $pathValidatorFactory + State $state, + ProcessorFacadeFactory $processorFacadeFactory ) { - $this->configSetProcessorFactory = $configSetProcessorFactory; - $this->validator = $validator; $this->scope = $scope; - $this->pathValidatorFactory = $pathValidatorFactory; + $this->state = $state; + $this->processorFacadeFactory = $processorFacadeFactory; parent::__construct(); } @@ -127,38 +116,20 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { try { - $this->validator->isValid( - $input->getOption(static::OPTION_SCOPE), - $input->getOption(static::OPTION_SCOPE_CODE) - ); - // Emulating adminhtml scope to be able to read configs. - $this->scope->setCurrentScope(Area::AREA_ADMINHTML); - - /** - * Validates the entered config path. - * Requires emulated area. - */ - $this->pathValidatorFactory->create()->validate( - $input->getArgument(ConfigSetCommand::ARG_PATH) - ); - - $processor = $input->getOption(static::OPTION_LOCK) - ? $this->configSetProcessorFactory->create(ConfigSetProcessorFactory::TYPE_LOCK) - : $this->configSetProcessorFactory->create(ConfigSetProcessorFactory::TYPE_DEFAULT); - $message = $input->getOption(static::OPTION_LOCK) - ? 'Value was saved and locked.' - : 'Value was saved.'; - - // The processing flow depends on --lock option. - $processor->process( - $input->getArgument(ConfigSetCommand::ARG_PATH), - $input->getArgument(ConfigSetCommand::ARG_VALUE), - $input->getOption(ConfigSetCommand::OPTION_SCOPE), - $input->getOption(ConfigSetCommand::OPTION_SCOPE_CODE) - ); - - $output->writeln('' . $message . ''); + $this->state->emulateAreaCode(Area::AREA_ADMINHTML, function () use ($input, $output) { + $this->scope->setCurrentScope(Area::AREA_ADMINHTML); + + $message = $this->processorFacadeFactory->create()->process( + $input->getArgument(static::ARG_PATH), + $input->getArgument(static::ARG_VALUE), + $input->getOption(static::OPTION_SCOPE), + $input->getOption(static::OPTION_SCOPE_CODE), + $input->getOption(static::OPTION_LOCK) + ); + + $output->writeln('' . $message . ''); + }); return Cli::RETURN_SUCCESS; } catch (\Exception $exception) { diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ProcessorFacadeTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ProcessorFacadeTest.php new file mode 100644 index 0000000000000..cc6efb939b9ce --- /dev/null +++ b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ProcessorFacadeTest.php @@ -0,0 +1,112 @@ +scopeValidatorMock = $this->getMockBuilder(ValidatorInterface::class) + ->getMockForAbstractClass(); + $this->pathValidatorMock = $this->getMockBuilder(PathValidator::class) + ->disableOriginalConstructor() + ->getMock(); + $this->configSetProcessorFactoryMock = $this->getMockBuilder(ConfigSetProcessorFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->processorMock = $this->getMockBuilder(ConfigSetProcessorInterface::class) + ->getMockForAbstractClass(); + + $this->configSetProcessorFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($this->processorMock); + + $this->model = new ProcessorFacade( + $this->scopeValidatorMock, + $this->pathValidatorMock, + $this->configSetProcessorFactoryMock + ); + } + + public function testProcess() + { + $this->scopeValidatorMock->expects($this->once()) + ->method('isValid') + ->willReturn(true); + $this->configSetProcessorFactoryMock->expects($this->once()) + ->method('create') + ->with(ConfigSetProcessorFactory::TYPE_DEFAULT) + ->willReturn($this->processorMock); + $this->processorMock->expects($this->once()) + ->method('process') + ->with('test/test/test', 'test', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null); + + $this->assertSame( + 'Value was saved.', + $this->model->process('test/test/test', 'test', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null, false) + ); + } + + public function testExecuteLock() + { + $this->scopeValidatorMock->expects($this->once()) + ->method('isValid') + ->willReturn(true); + $this->configSetProcessorFactoryMock->expects($this->once()) + ->method('create') + ->with(ConfigSetProcessorFactory::TYPE_LOCK) + ->willReturn($this->processorMock); + $this->processorMock->expects($this->once()) + ->method('process') + ->with('test/test/test', 'test', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null); + + $this->assertSame( + 'Value was saved and locked.', + $this->model->process('test/test/test', 'test', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null, true) + ); + } +} diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php index 1a417073548b2..e4dcb92b203b6 100644 --- a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php +++ b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php @@ -5,15 +5,12 @@ */ namespace Magento\Config\Test\Unit\Console\Command; -use Magento\Config\Console\Command\ConfigSet\ConfigSetProcessorFactory; -use Magento\Config\Console\Command\ConfigSet\ConfigSetProcessorInterface; +use Magento\Config\Console\Command\ConfigSet\ProcessorFacade; +use Magento\Config\Console\Command\ConfigSet\ProcessorFacadeFactory; use Magento\Config\Console\Command\ConfigSetCommand; -use Magento\Config\Model\Config\PathValidator; -use Magento\Config\Model\Config\PathValidatorFactory; -use Magento\Framework\App\Scope\ValidatorInterface; +use Magento\Framework\App\State; use Magento\Framework\Config\ScopeInterface; use Magento\Framework\Console\Cli; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\ValidatorException; use PHPUnit_Framework_MockObject_MockObject as Mock; use Symfony\Component\Console\Tester\CommandTester; @@ -22,7 +19,6 @@ * Test for ConfigSetCommand. * * @see ConfigSetCommand - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ConfigSetCommandTest extends \PHPUnit_Framework_TestCase { @@ -32,129 +28,59 @@ class ConfigSetCommandTest extends \PHPUnit_Framework_TestCase private $command; /** - * @var ConfigSetProcessorFactory|Mock - */ - private $configSetProcessorFactoryMock; - - /** - * @var ValidatorInterface|Mock - */ - private $validatorMock; - - /** - * @var ConfigSetProcessorInterface|Mock + * @var ScopeInterface|Mock */ - private $processorMock; + private $scopeMock; /** - * @var ScopeInterface|Mock + * @var State|Mock */ - private $scopeMock; + private $stateMock; /** - * @var PathValidatorFactory|Mock + * @var ProcessorFacadeFactory|Mock */ - private $pathValidatorFactoryMock; + private $processorFacadeFactoryMock; /** - * @var PathValidator|Mock + * @var ProcessorFacade|Mock */ - private $pathValidator; + private $processorFacadeMock; /** * @inheritdoc */ protected function setUp() { - $this->configSetProcessorFactoryMock = $this->getMockBuilder(ConfigSetProcessorFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->validatorMock = $this->getMockBuilder(ValidatorInterface::class) - ->getMockForAbstractClass(); - $this->processorMock = $this->getMockBuilder(ConfigSetProcessorInterface::class) - ->getMockForAbstractClass(); $this->scopeMock = $this->getMockBuilder(ScopeInterface::class) ->getMockForAbstractClass(); - $this->pathValidatorFactoryMock = $this->getMockBuilder(PathValidatorFactory::class) + $this->stateMock = $this->getMockBuilder(State::class) + ->disableOriginalConstructor() + ->getMock(); + $this->processorFacadeFactoryMock = $this->getMockBuilder(ProcessorFacadeFactory::class) ->disableOriginalConstructor() - ->setMethods(['create']) ->getMock(); - $this->pathValidator = $this->getMockBuilder(PathValidator::class) + $this->processorFacadeMock = $this->getMockBuilder(ProcessorFacade::class) ->disableOriginalConstructor() ->getMock(); - $this->pathValidatorFactoryMock->expects($this->any()) + $this->processorFacadeFactoryMock->expects($this->any()) ->method('create') - ->willReturn($this->pathValidator); + ->willReturn($this->processorFacadeMock); $this->command = new ConfigSetCommand( - $this->configSetProcessorFactoryMock, - $this->validatorMock, $this->scopeMock, - $this->pathValidatorFactoryMock + $this->stateMock, + $this->processorFacadeFactoryMock ); } - public function testExecute() - { - $this->validatorMock->expects($this->once()) - ->method('isValid') - ->willReturn(true); - $this->configSetProcessorFactoryMock->expects($this->once()) - ->method('create') - ->with(ConfigSetProcessorFactory::TYPE_DEFAULT) - ->willReturn($this->processorMock); - - $tester = new CommandTester($this->command); - $tester->execute([ - ConfigSetCommand::ARG_PATH => 'test/test/test', - ConfigSetCommand::ARG_VALUE => 'value' - ]); - } - - public function testExecuteLock() - { - $this->validatorMock->expects($this->once()) - ->method('isValid') - ->willReturn(true); - $this->configSetProcessorFactoryMock->expects($this->once()) - ->method('create') - ->with(ConfigSetProcessorFactory::TYPE_LOCK) - ->willReturn($this->processorMock); - - $tester = new CommandTester($this->command); - $tester->execute([ - ConfigSetCommand::ARG_PATH => 'test/test/test', - ConfigSetCommand::ARG_VALUE => 'value', - '--' . ConfigSetCommand::OPTION_LOCK => true - ]); - } - - public function testExecuteNotValidScope() - { - $this->validatorMock->expects($this->once()) - ->method('isValid') - ->willThrowException(new LocalizedException(__('Test exception'))); - - $tester = new CommandTester($this->command); - $tester->execute([ - ConfigSetCommand::ARG_PATH => 'test/test/test', - ConfigSetCommand::ARG_VALUE => 'value' - ]); - - $this->assertSame(Cli::RETURN_FAILURE, $tester->getStatusCode()); - } - public function testExecuteWithException() { - $this->validatorMock->expects($this->once()) - ->method('isValid') - ->willReturn(true); - $this->pathValidator->expects($this->once()) - ->method('validate') + $this->stateMock->expects($this->once()) + ->method('emulateAreaCode') ->willThrowException(new ValidatorException(__('The "test/test/test" path does not exists'))); - $this->configSetProcessorFactoryMock->expects($this->never()) + $this->processorFacadeFactoryMock->expects($this->never()) ->method('create'); $tester = new CommandTester($this->command); diff --git a/app/code/Magento/Deploy/Console/Command/App/ConfigImport/Importer.php b/app/code/Magento/Deploy/Console/Command/App/ConfigImport/Importer.php index c8802224aa874..51e15f33ad098 100644 --- a/app/code/Magento/Deploy/Console/Command/App/ConfigImport/Importer.php +++ b/app/code/Magento/Deploy/Console/Command/App/ConfigImport/Importer.php @@ -7,12 +7,13 @@ use Magento\Framework\App\DeploymentConfig\ImporterInterface; use Magento\Framework\App\DeploymentConfig; -use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\RuntimeException; use Psr\Log\LoggerInterface as Logger; use Magento\Deploy\Model\DeploymentConfig\Validator; use Magento\Deploy\Model\DeploymentConfig\ImporterPool; use Magento\Deploy\Model\DeploymentConfig\Hash; use Symfony\Component\Console\Output\OutputInterface; +use Magento\Deploy\Model\DeploymentConfig\ImporterFactory; /** * Runs importing of config data from deployment configuration files. @@ -47,6 +48,13 @@ class Importer */ private $configHash; + /** + * Factory for creation of importer instance. + * + * @var ImporterFactory + */ + private $importerFactory; + /** * Logger. * @@ -57,6 +65,7 @@ class Importer /** * @param Validator $configValidator the manager of deployment configuration hash * @param ImporterPool $configImporterPool the pool of all deployment configuration importers + * @param ImporterFactory $importerFactory the factory for creation of importer instance * @param DeploymentConfig $deploymentConfig the application deployment configuration * @param Hash $configHash the hash updater of config data * @param Logger $logger the logger @@ -64,12 +73,14 @@ class Importer public function __construct( Validator $configValidator, ImporterPool $configImporterPool, + ImporterFactory $importerFactory, DeploymentConfig $deploymentConfig, Hash $configHash, Logger $logger ) { $this->configValidator = $configValidator; $this->configImporterPool = $configImporterPool; + $this->importerFactory = $importerFactory; $this->deploymentConfig = $deploymentConfig; $this->configHash = $configHash; $this->logger = $logger; @@ -80,32 +91,35 @@ public function __construct( * * @param OutputInterface $output the CLI output * @return void - * @throws LocalizedException + * @throws RuntimeException is thrown when import has failed */ public function import(OutputInterface $output) { - $output->writeln('Start import:'); - try { $importers = $this->configImporterPool->getImporters(); - if (!$importers || $this->configValidator->isValid()) { - $output->writeln('Nothing to import'); + $output->writeln('Nothing to import.'); + return; } else { - /** - * @var string $namespace - * @var ImporterInterface $importer - */ - foreach ($importers as $namespace => $importer) { - $messages = $importer->import($this->deploymentConfig->getConfigData($namespace)); + $output->writeln('Start import:'); + } + + /** + * @var string $section + * @var string $importerClassName + */ + foreach ($importers as $section => $importerClassName) { + if (!$this->configValidator->isValid($section)) { + /** @var ImporterInterface $importer */ + $importer = $this->importerFactory->create($importerClassName); + $messages = $importer->import((array)$this->deploymentConfig->getConfigData($section)); $output->writeln($messages); + $this->configHash->regenerate($section); } - - $this->configHash->regenerate(); } - } catch (LocalizedException $exception) { + } catch (\Exception $exception) { $this->logger->error($exception); - throw new LocalizedException(__('Import is failed'), $exception); + throw new RuntimeException(__('Import is failed: %1', $exception->getMessage()), $exception); } } } diff --git a/app/code/Magento/Deploy/Console/Command/App/ConfigImportCommand.php b/app/code/Magento/Deploy/Console/Command/App/ConfigImportCommand.php index 809211c2314b8..9060916c8f2f6 100644 --- a/app/code/Magento/Deploy/Console/Command/App/ConfigImportCommand.php +++ b/app/code/Magento/Deploy/Console/Command/App/ConfigImportCommand.php @@ -5,7 +5,7 @@ */ namespace Magento\Deploy\Console\Command\App; -use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\RuntimeException; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -27,12 +27,14 @@ class ConfigImportCommand extends Command const COMMAND_NAME = 'app:config:import'; /** + * Configuration importer. + * * @var Importer */ private $importer; /** - * @param Importer $importer + * @param Importer $importer the configuration importer */ public function __construct(Importer $importer) { @@ -60,7 +62,7 @@ protected function execute(InputInterface $input, OutputInterface $output) { try { $this->importer->import($output); - } catch (LocalizedException $e) { + } catch (RuntimeException $e) { $output->writeln('' . $e->getMessage() . ''); return Cli::RETURN_FAILURE; diff --git a/app/code/Magento/Deploy/Model/DeploymentConfig/DataCollector.php b/app/code/Magento/Deploy/Model/DeploymentConfig/DataCollector.php index 63fb770d468aa..c7534ffcf0657 100644 --- a/app/code/Magento/Deploy/Model/DeploymentConfig/DataCollector.php +++ b/app/code/Magento/Deploy/Model/DeploymentConfig/DataCollector.php @@ -15,7 +15,7 @@ * * * - * Magento\Store\Model\StoreImporter + * Magento\SomeModule\Model\SomeImporter * * * @@ -30,12 +30,11 @@ * ] * ``` * - * In here we define section "scopes" and its importer Magento\Store\Model\StoreImporter. + * In here we define section "scopes" and its importer Magento\SomeModule\Model\SomeImporter. * The data of this section will be collected then will be used in importing process from the shared configuration * files to appropriate application sources. * * @see \Magento\Deploy\Console\Command\App\ConfigImport\Importer::import() - * @see \Magento\Deploy\Model\DeploymentConfig\Hash::regenerate() */ class DataCollector { @@ -65,6 +64,7 @@ public function __construct(ImporterPool $configImporterPool, DeploymentConfig $ /** * Retrieves configuration data of specific section from deployment configuration files. + * Or retrieves configuration data of specific sections by its name. * * E.g. * ```php @@ -75,19 +75,30 @@ public function __construct(ImporterPool $configImporterPool, DeploymentConfig $ * ... * ] * ``` + * + * This method retrieves the same structure for the specific section with only its data. + * ```php + * [ + * 'scopes' => [...] + * ] + * * In this example key of the array is the section name, value of the array is configuration data of the section. * + * @param string $sectionName the section name for retrieving its configuration data * @return array */ - public function getConfig() + public function getConfig($sectionName = null) { $result = []; - foreach ($this->configImporterPool->getSections() as $section) { - $data = $this->deploymentConfig->getConfigData($section); - if ($data) { - $result[$section] = $data; - } + if ($sectionName) { + $sections = [$sectionName]; + } else { + $sections = $this->configImporterPool->getSections(); + } + + foreach ($sections as $section) { + $result[$section] = $this->deploymentConfig->getConfigData($section); } return $result; diff --git a/app/code/Magento/Deploy/Model/DeploymentConfig/Hash.php b/app/code/Magento/Deploy/Model/DeploymentConfig/Hash.php index 6a904e9004a35..1decf148d0d0a 100644 --- a/app/code/Magento/Deploy/Model/DeploymentConfig/Hash.php +++ b/app/code/Magento/Deploy/Model/DeploymentConfig/Hash.php @@ -5,11 +5,10 @@ */ namespace Magento\Deploy\Model\DeploymentConfig; -use Magento\Framework\Config\File\ConfigFilePool; -use Magento\Framework\App\DeploymentConfig\Writer; -use Magento\Framework\App\DeploymentConfig; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Flag\FlagResource; +use Magento\Framework\Flag; +use Magento\Framework\FlagFactory; /** * Saves and Retrieves deployment configuration hash. @@ -27,77 +26,106 @@ class Hash const CONFIG_KEY = 'config_hash'; /** - * Application deployment configuration. + * Hash generator. * - * @var DeploymentConfig + * @var Hash\Generator */ - private $deploymentConfig; + private $configHashGenerator; /** - * Deployment configuration writer to files. + * Config data collector. * - * @var Writer + * @var DataCollector */ - private $writer; + private $dataConfigCollector; /** - * Hash generator. + * Flag Resource model. * - * @var Hash\Generator + * @var FlagResource */ - private $configHashGenerator; + private $flagResource; /** - * Config data collector. + * Factory class for \Magento\Framework\Flag * - * @var DataCollector + * @var FlagFactory */ - private $dataConfigCollector; + private $flagFactory; /** - * @param DeploymentConfig $deploymentConfig the application deployment configuration - * @param Writer $writer the configuration writer that writes to files * @param Hash\Generator $configHashGenerator the hash generator * @param DataCollector $dataConfigCollector the config data collector + * @param FlagResource $flagResource + * @param FlagFactory $flagFactory */ public function __construct( - DeploymentConfig $deploymentConfig, - Writer $writer, Hash\Generator $configHashGenerator, - DataCollector $dataConfigCollector + DataCollector $dataConfigCollector, + FlagResource $flagResource, + FlagFactory $flagFactory ) { - $this->deploymentConfig = $deploymentConfig; - $this->writer = $writer; $this->configHashGenerator = $configHashGenerator; $this->dataConfigCollector = $dataConfigCollector; + $this->flagResource = $flagResource; + $this->flagFactory = $flagFactory; } /** * Updates hash in the storage. * - * The hash is generated based on data from configuration files + * If the specific section name is set, then hash will be updated only for this section, + * in another case hash will be updated for all sections which defined in di.xml + * The hash is generated based on data from configuration files. * + * @param string $sectionName the specific section name * @return void * @throws LocalizedException is thrown when hash was not saved */ - public function regenerate() + public function regenerate($sectionName = null) { try { - $config = $this->dataConfigCollector->getConfig(); - $hash = $this->configHashGenerator->generate($config); - $this->writer->saveConfig([ConfigFilePool::APP_ENV => [self::CONFIG_KEY => $hash]]); - } catch (FileSystemException $exception) { - throw new LocalizedException(__('Hash has not been saved'), $exception); + $hashes = $this->get(); + $configs = $this->dataConfigCollector->getConfig($sectionName); + + foreach ($configs as $section => $config) { + $hashes[$section] = $this->configHashGenerator->generate($config); + } + + /** @var Flag $flag */ + $flag = $this->getFlagObject(); + $flag->setFlagData($hashes); + $this->flagResource->save($flag); + } catch (\Exception $exception) { + throw new LocalizedException(__('Hash has not been saved.'), $exception); } } /** - * Retrieves saved hash from storage. + * Retrieves saved hashes from storage. * - * @return string|null + * @return array */ public function get() { - return $this->deploymentConfig->getConfigData(self::CONFIG_KEY); + /** @var Flag $flag */ + $flag = $this->getFlagObject(); + return (array) ($flag->getFlagData() ?: []); + } + + /** + * Returns flag object. + * + * We use it for saving hashes of sections in the DB. + * + * @return Flag + */ + private function getFlagObject() + { + /** @var Flag $flag */ + $flag = $this->flagFactory + ->create(['data' => ['flag_code' => self::CONFIG_KEY]]); + $this->flagResource->load($flag, self::CONFIG_KEY, 'flag_code'); + return $flag; } } diff --git a/app/code/Magento/Deploy/Model/DeploymentConfig/ImporterFactory.php b/app/code/Magento/Deploy/Model/DeploymentConfig/ImporterFactory.php new file mode 100644 index 0000000000000..706f33f685317 --- /dev/null +++ b/app/code/Magento/Deploy/Model/DeploymentConfig/ImporterFactory.php @@ -0,0 +1,53 @@ +objectManager = $objectManager; + } + + /** + * Creates object instance by class name. + * + * @param string $className the name of class for creation of its object instance + * @param array $data the array with some additional configuration data for creation of object instance + * @return ImporterInterface the created object instance + * @throws \InvalidArgumentException is thrown when object instance does not implement ImporterInterface + */ + public function create($className, array $data = []) + { + $importer = $this->objectManager->create($className, $data); + + if (!$importer instanceof ImporterInterface) { + throw new \InvalidArgumentException( + 'Type "' . $className . '" is not instance on ' . ImporterInterface::class + ); + } + + return $importer; + } +} diff --git a/app/code/Magento/Deploy/Model/DeploymentConfig/ImporterPool.php b/app/code/Magento/Deploy/Model/DeploymentConfig/ImporterPool.php index b2b2fece8a192..8f2495dd0e444 100644 --- a/app/code/Magento/Deploy/Model/DeploymentConfig/ImporterPool.php +++ b/app/code/Magento/Deploy/Model/DeploymentConfig/ImporterPool.php @@ -6,9 +6,7 @@ namespace Magento\Deploy\Model\DeploymentConfig; use Magento\Framework\Exception\ConfigurationMismatchException; -use Magento\Framework\Phrase; use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\App\DeploymentConfig\ImporterInterface; /** * Pool of all deployment configuration importers. @@ -26,7 +24,14 @@ class ImporterPool * * * - * Magento\Store\Model\StoreImporter + * + * 20 + * Magento\Store\Model\StoreImporter + * + * + * 10 + * Magento\Theme\Model\ThemeImporter + * * * * @@ -54,6 +59,23 @@ class ImporterPool */ private $importers = []; + /** + * Sorted list of importers class names. + * + * This list sorted by parameter "sortOrder", that defined in di.xml + * + * ```php + * [ + * 'themes' => 'Magento\Theme\Model\ThemeImporter', + * 'scopes' => 'Magento\Store\Model\StoreImporter', + * ... + * ] + * ``` + * + * @var array + */ + private $sortedImporters = []; + /** * Magento object manager. * @@ -91,34 +113,68 @@ public function getSections() } /** - * Retrieves list of all sections with their importer instances. + * Retrieves list of all sections with their importer class names, sorted by sortOrder. * * E.g. * ```php * [ - * 'scopes' => SomeScopeImporter(), + * 'scopes' => Magento\Store\Model\StoreImporter, * ... * ] * ``` * - * @return array the list of all sections with their importer instances - * @throws ConfigurationMismatchException is thrown when instance of importer implements a wrong interface + * @return array the list of all sections with their importer class names + * @throws ConfigurationMismatchException is thrown when parameter class is empty */ public function getImporters() { - $result = []; - - foreach ($this->importers as $section => $importer) { - $importerObj = $this->objectManager->get($importer); - if (!$importerObj instanceof ImporterInterface) { - throw new ConfigurationMismatchException(new Phrase( - '%1: Instance of %2 is expected, got %3 instead', - [$section, ImporterInterface::class, get_class($importerObj)] - )); + if (!$this->sortedImporters) { + $sortedImporters = []; + + foreach ($this->sort($this->importers) as $section => $importer) { + if (empty($importer['class'])) { + throw new ConfigurationMismatchException(__('Parameter "class" must be present.')); + } + + $sortedImporters[$section] = $importer['class']; } - $result[$section] = $importerObj; + + $this->sortedImporters = $sortedImporters; } - return $result; + return $this->sortedImporters; + } + + /** + * Sorts importers according to sort order. + * + * @param array $data + * @return array + */ + private function sort(array $data) + { + uasort($data, function (array $a, array $b) { + $a['sortOrder'] = $this->getSortOrder($a); + $b['sortOrder'] = $this->getSortOrder($b); + + if ($a['sortOrder'] == $b['sortOrder']) { + return 0; + } + + return ($a['sortOrder'] < $b['sortOrder']) ? -1 : 1; + }); + + return $data; + } + + /** + * Retrieves sort order from array. + * + * @param array $variable + * @return int + */ + private function getSortOrder(array $variable) + { + return !empty($variable['sortOrder']) ? $variable['sortOrder'] : 0; } } diff --git a/app/code/Magento/Deploy/Model/DeploymentConfig/Validator.php b/app/code/Magento/Deploy/Model/DeploymentConfig/Validator.php index 7f6df6efdec19..9fd4e7a801e80 100644 --- a/app/code/Magento/Deploy/Model/DeploymentConfig/Validator.php +++ b/app/code/Magento/Deploy/Model/DeploymentConfig/Validator.php @@ -59,16 +59,22 @@ public function __construct( * If config data is empty always returns true. * In the other cases returns true. * + * @param string $sectionName is section name for check data of the specific section * @return bool */ - public function isValid() + public function isValid($sectionName = null) { - $config = $this->dataConfigCollector->getConfig(); + $configs = $this->dataConfigCollector->getConfig($sectionName); + $hashes = $this->configHash->get(); - if (!$config) { - return true; + foreach ($configs as $section => $config) { + $savedHash = isset($hashes[$section]) ? $hashes[$section] : null; + $generatedHash = empty($config) && !$savedHash ? null : $this->hashGenerator->generate($config); + if ($generatedHash !== $savedHash) { + return false; + } } - return $this->hashGenerator->generate($config) === $this->configHash->get(); + return true; } } diff --git a/app/code/Magento/Deploy/Model/Plugin/ConfigValidator.php b/app/code/Magento/Deploy/Model/Plugin/ConfigValidator.php index f5fc2bdc46dcb..38ed867058f93 100644 --- a/app/code/Magento/Deploy/Model/Plugin/ConfigValidator.php +++ b/app/code/Magento/Deploy/Model/Plugin/ConfigValidator.php @@ -6,7 +6,7 @@ namespace Magento\Deploy\Model\Plugin; use Magento\Deploy\Model\DeploymentConfig\Validator as DeploymentConfigValidator; -use Magento\Framework\App\FrontController; +use Magento\Framework\App\FrontControllerInterface; use Magento\Framework\App\RequestInterface; use Magento\Framework\Exception\LocalizedException; @@ -37,13 +37,13 @@ public function __construct(DeploymentConfigValidator $configValidator) /** * Performs check that config data from deployment configuration files is valid. * - * @param FrontController $subject the object of controller is wrapped by this plugin + * @param FrontControllerInterface $subject the interface of frontend controller is wrapped by this plugin * @param RequestInterface $request the object that contains request params * @return void * @throws LocalizedException is thrown if config data from deployment configuration files is not valid * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function beforeDispatch(FrontController $subject, RequestInterface $request) + public function beforeDispatch(FrontControllerInterface $subject, RequestInterface $request) { if (!$this->configValidator->isValid()) { throw new LocalizedException( diff --git a/app/code/Magento/Deploy/Test/Unit/Console/Command/App/ConfigImport/ImporterTest.php b/app/code/Magento/Deploy/Test/Unit/Console/Command/App/ConfigImport/ImporterTest.php index 58ccf22eda71e..dd2457ae9a08e 100644 --- a/app/code/Magento/Deploy/Test/Unit/Console/Command/App/ConfigImport/ImporterTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Console/Command/App/ConfigImport/ImporterTest.php @@ -7,13 +7,13 @@ use Magento\Framework\App\DeploymentConfig\ImporterInterface; use Magento\Framework\App\DeploymentConfig; -use Magento\Framework\Exception\LocalizedException; use Psr\Log\LoggerInterface as Logger; use Magento\Deploy\Console\Command\App\ConfigImport\Importer; use Magento\Deploy\Model\DeploymentConfig\Validator; use Magento\Deploy\Model\DeploymentConfig\Hash; use Magento\Deploy\Model\DeploymentConfig\ImporterPool; use Symfony\Component\Console\Output\OutputInterface; +use Magento\Deploy\Model\DeploymentConfig\ImporterFactory; class ImporterTest extends \PHPUnit_Framework_TestCase { @@ -27,6 +27,11 @@ class ImporterTest extends \PHPUnit_Framework_TestCase */ private $configImporterPoolMock; + /** + * @var ImporterFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $importerFactoryMock; + /** * @var DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject */ @@ -52,11 +57,11 @@ class ImporterTest extends \PHPUnit_Framework_TestCase */ private $importer; - /** - * @return void - */ protected function setUp() { + $this->importerFactoryMock = $this->getMockBuilder(ImporterFactory::class) + ->disableOriginalConstructor() + ->getMock(); $this->configValidatorMock = $this->getMockBuilder(Validator::class) ->disableOriginalConstructor() ->getMock(); @@ -78,27 +83,30 @@ protected function setUp() $this->importer = new Importer( $this->configValidatorMock, $this->configImporterPoolMock, + $this->importerFactoryMock, $this->deploymentConfigMock, $this->configHashMock, $this->loggerMock ); } - /** - * @return void - */ public function testImport() { $configData = ['some data']; $messages = ['Import has done']; $expectsMessages = ['Import has done']; + $importerClassName = 'someImporterClassName'; + $importers = ['someSection' => $importerClassName]; $importerMock = $this->getMockBuilder(ImporterInterface::class) ->getMockForAbstractClass(); - $importers = ['someSection' => $importerMock]; $this->configImporterPoolMock->expects($this->once()) ->method('getImporters') ->willReturn($importers); + $this->importerFactoryMock->expects($this->once()) + ->method('create') + ->with($importerClassName) + ->willReturn($importerMock); $this->configValidatorMock->expects($this->any()) ->method('isValid') ->willReturn(false); @@ -126,16 +134,20 @@ public function testImport() } /** - * @return void - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Import is failed + * @expectedException \Magento\Framework\Exception\RuntimeException + * @expectedExceptionMessage Import is failed: Some error */ public function testImportWithException() { - $exception = new LocalizedException(__('Some error')); - $this->outputMock->expects($this->at(0)) - ->method('writeln') - ->with('Start import:'); + $exception = new \Exception('Some error'); + $this->outputMock->expects($this->never()) + ->method('writeln'); + $this->configHashMock->expects($this->never()) + ->method('regenerate'); + $this->configValidatorMock->expects($this->never()) + ->method('isValid'); + $this->deploymentConfigMock->expects($this->never()) + ->method('getConfigData'); $this->configImporterPoolMock->expects($this->once()) ->method('getImporters') ->willThrowException($exception); @@ -149,7 +161,6 @@ public function testImportWithException() /** * @param array $importers * @param bool $isValid - * @return void * @dataProvider importNothingToImportDataProvider */ public function testImportNothingToImport(array $importers, $isValid) @@ -169,10 +180,7 @@ public function testImportNothingToImport(array $importers, $isValid) $this->outputMock->expects($this->at(0)) ->method('writeln') - ->with('Start import:'); - $this->outputMock->expects($this->at(1)) - ->method('writeln') - ->with('Nothing to import'); + ->with('Nothing to import.'); $this->importer->import($this->outputMock); } diff --git a/app/code/Magento/Deploy/Test/Unit/Console/Command/App/ConfigImportCommandTest.php b/app/code/Magento/Deploy/Test/Unit/Console/Command/App/ConfigImportCommandTest.php index f684615b1fb9a..4ab3cbe61622f 100644 --- a/app/code/Magento/Deploy/Test/Unit/Console/Command/App/ConfigImportCommandTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Console/Command/App/ConfigImportCommandTest.php @@ -8,7 +8,7 @@ use Magento\Deploy\Console\Command\App\ConfigImportCommand; use Magento\Deploy\Console\Command\App\ConfigImport\Importer; use Magento\Framework\Console\Cli; -use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\RuntimeException; use Symfony\Component\Console\Tester\CommandTester; class ConfigImportCommandTest extends \PHPUnit_Framework_TestCase @@ -32,9 +32,7 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $configImportCommand = new ConfigImportCommand( - $this->importerMock - ); + $configImportCommand = new ConfigImportCommand($this->importerMock); $this->commandTester = new CommandTester($configImportCommand); } @@ -57,7 +55,7 @@ public function testExecuteWithException() { $this->importerMock->expects($this->once()) ->method('import') - ->willThrowException(new LocalizedException(__('Some error'))); + ->willThrowException(new RuntimeException(__('Some error'))); $this->assertSame(Cli::RETURN_FAILURE, $this->commandTester->execute([])); $this->assertContains('Some error', $this->commandTester->getDisplay()); diff --git a/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/DataCollectorTest.php b/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/DataCollectorTest.php index 484c7c6407a85..1373defdee761 100644 --- a/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/DataCollectorTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/DataCollectorTest.php @@ -50,10 +50,23 @@ public function testGetConfig() $this->configImporterPoolMock->expects($this->once()) ->method('getSections') ->willReturn($sections); - $this->deploymentConfigMock->expects($this->any()) + $this->deploymentConfigMock->expects($this->atLeastOnce()) ->method('getConfigData') ->willReturnMap([['first', 'some data']]); - $this->assertSame(['first' => 'some data'], $this->dataCollector->getConfig()); + $this->assertSame(['first' => 'some data', 'second' => null], $this->dataCollector->getConfig()); + } + + /** + * @return void + */ + public function testGetConfigSpecificSection() + { + $this->configImporterPoolMock->expects($this->never()) + ->method('getSections'); + $this->deploymentConfigMock->expects($this->atLeastOnce()) + ->method('getConfigData') + ->willReturnMap([['someSection', 'some data']]); + $this->assertSame(['someSection' => 'some data'], $this->dataCollector->getConfig('someSection')); } } diff --git a/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/HashTest.php b/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/HashTest.php index 74956f98aa817..41309c8f88f52 100644 --- a/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/HashTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/HashTest.php @@ -5,35 +5,40 @@ */ namespace Magento\Deploy\Test\Unit\Model\DeploymentConfig; -use Magento\Framework\Config\File\ConfigFilePool; -use Magento\Framework\App\DeploymentConfig\Writer; use Magento\Framework\App\DeploymentConfig; -use Magento\Framework\Exception\FileSystemException; use Magento\Deploy\Model\DeploymentConfig\Hash; -use Magento\Deploy\Model\DeploymentConfig\Hash\Generator; use Magento\Deploy\Model\DeploymentConfig\DataCollector; +use Magento\Deploy\Model\DeploymentConfig\Hash\Generator; +use Magento\Framework\Flag\FlagResource; +use Magento\Framework\Flag; +use Magento\Framework\FlagFactory; class HashTest extends \PHPUnit_Framework_TestCase { /** - * @var DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject + * @var Generator|\PHPUnit_Framework_MockObject_MockObject */ - private $deploymentConfigMock; + private $configHashGeneratorMock; /** - * @var Writer|\PHPUnit_Framework_MockObject_MockObject + * @var DataCollector|\PHPUnit_Framework_MockObject_MockObject */ - private $writerMock; + private $dataConfigCollectorMock; /** - * @var Generator|\PHPUnit_Framework_MockObject_MockObject + * @var FlagFactory|\PHPUnit_Framework_MockObject_MockObject */ - private $configHashGeneratorMock; + private $flagFactoryMock; /** - * @var DataCollector|\PHPUnit_Framework_MockObject_MockObject + * @var FlagResource|\PHPUnit_Framework_MockObject_MockObject */ - private $dataConfigCollectorMock; + private $flagResourceMock; + + /** + * @var Flag|\PHPUnit_Framework_MockObject_MockObject + */ + private $flagMock; /** * @var Hash @@ -45,10 +50,16 @@ class HashTest extends \PHPUnit_Framework_TestCase */ protected function setUp() { - $this->deploymentConfigMock = $this->getMockBuilder(DeploymentConfig::class) + $this->flagResourceMock = $this->getMockBuilder(FlagResource::class) + ->disableOriginalConstructor() + ->getMock(); + $this->flagFactoryMock = $this->getMockBuilder(FlagFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->writerMock = $this->getMockBuilder(Writer::class) + $this->flagMock = $this->getMockBuilder(Flag::class) + ->disableOriginalConstructor() + ->getMock(); + $this->deploymentConfigMock = $this->getMockBuilder(DeploymentConfig::class) ->disableOriginalConstructor() ->getMock(); $this->configHashGeneratorMock = $this->getMockBuilder(Generator::class) @@ -59,25 +70,47 @@ protected function setUp() ->getMock(); $this->hash = new Hash( - $this->deploymentConfigMock, - $this->writerMock, $this->configHashGeneratorMock, - $this->dataConfigCollectorMock + $this->dataConfigCollectorMock, + $this->flagResourceMock, + $this->flagFactoryMock ); } /** + * @param string|array|null $dataFromStorage + * @param array $expectedResult * @return void + * @dataProvider getDataProvider */ - public function testGet() + public function testGet($dataFromStorage, $expectedResult) { - $result = 'some data'; - $this->deploymentConfigMock->expects($this->once()) - ->method('getConfigData') - ->with(Hash::CONFIG_KEY) - ->willReturn($result); + $this->flagMock->expects($this->once()) + ->method('getFlagData') + ->willReturn($dataFromStorage); + $this->flagFactoryMock->expects($this->once()) + ->method('create') + ->with(['data' => ['flag_code' => Hash::CONFIG_KEY]]) + ->willReturn($this->flagMock); + $this->flagResourceMock->expects($this->once()) + ->method('load') + ->with($this->flagMock, Hash::CONFIG_KEY, 'flag_code') + ->willReturnSelf(); + + $this->assertSame($expectedResult, $this->hash->get()); + } - $this->assertSame($result, $this->hash->get()); + /** + * @return array + */ + public function getDataProvider() + { + return [ + [['section' => 'hash'], ['section' => 'hash']], + ['hash', ['hash']], + ['', []], + [null, []], + ]; } /** @@ -85,13 +118,18 @@ public function testGet() */ public function testRegenerate() { + $section = 'section'; $config = 'some config'; + $fullConfig = ['section' => $config]; $hash = 'some hash'; + $hashes = [$section => $hash]; + + $this->generalRegenerateMocks($fullConfig, $config, $hash, $hashes); - $this->generalRegenerateMocks($config, $hash); - $this->writerMock->expects($this->once()) - ->method('saveConfig') - ->with([ConfigFilePool::APP_ENV => [Hash::CONFIG_KEY => $hash]]); + $this->flagResourceMock->expects($this->once()) + ->method('save') + ->with($this->flagMock) + ->willReturnSelf(); $this->hash->regenerate(); } @@ -103,31 +141,48 @@ public function testRegenerate() */ public function testRegenerateWithException() { + $section = 'section'; $config = 'some config'; + $fullConfig = ['section' => $config]; $hash = 'some hash'; + $hashes = [$section => $hash]; - $this->generalRegenerateMocks($config, $hash); - $this->writerMock->expects($this->once()) - ->method('saveConfig') - ->with([ConfigFilePool::APP_ENV => [Hash::CONFIG_KEY => $hash]]) - ->willThrowException(new FileSystemException(__('Some error'))); + $this->generalRegenerateMocks($fullConfig, $config, $hash, $hashes, $section); - $this->hash->regenerate(); + $this->flagResourceMock->expects($this->once()) + ->method('save') + ->with($this->flagMock) + ->willThrowException(new \Exception('Some error')); + $this->hash->regenerate($section); } /** + * @param array $fullConfig * @param string $config * @param string $hash + * @param array $hashes + * @param string|null $sectionName * @return void */ - private function generalRegenerateMocks($config, $hash) + private function generalRegenerateMocks($fullConfig, $config, $hash, $hashes, $sectionName = null) { $this->dataConfigCollectorMock->expects($this->once()) ->method('getConfig') - ->willReturn($config); + ->with($sectionName) + ->willReturn($fullConfig); $this->configHashGeneratorMock->expects($this->once()) ->method('generate') ->with($config) ->willReturn($hash); + $this->flagMock->expects($this->once()) + ->method('setFlagData') + ->willReturn($hashes); + $this->flagFactoryMock->expects($this->exactly(2)) + ->method('create') + ->with(['data' => ['flag_code' => Hash::CONFIG_KEY]]) + ->willReturn($this->flagMock); + $this->flagResourceMock->expects($this->exactly(2)) + ->method('load') + ->with($this->flagMock, Hash::CONFIG_KEY, 'flag_code'); } } diff --git a/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/ImporterFactoryTest.php b/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/ImporterFactoryTest.php new file mode 100644 index 0000000000000..b6005753245ed --- /dev/null +++ b/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/ImporterFactoryTest.php @@ -0,0 +1,69 @@ +objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class) + ->getMockForAbstractClass(); + $this->importerFactory = new ImporterFactory($this->objectManagerMock); + } + + public function testCreate() + { + $className = 'some/class/name'; + + /** @var ImporterInterface|\PHPUnit_Framework_MockObject_MockObject $importerMock */ + $importerMock = $this->getMockBuilder(ImporterInterface::class) + ->getMockForAbstractClass(); + + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with($className, []) + ->willReturn($importerMock); + + $this->assertSame($importerMock, $this->importerFactory->create($className)); + } + + /** + * @expectedException \InvalidArgumentException + * @codingStandardsIgnoreStart + * @expectedExceptionMessage Type "some/class/name" is not instance on Magento\Framework\App\DeploymentConfig\ImporterInterface + * @codingStandardsIgnoreEnd + */ + public function testCreateWithInvalidArgumentException() + { + $className = 'some/class/name'; + + /** @var \StdClass|\PHPUnit_Framework_MockObject_MockObject $importerMock */ + $importerMock = $this->getMockBuilder(\StdClass::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with($className, []) + ->willReturn($importerMock); + + $this->importerFactory->create($className); + } +} diff --git a/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/ImporterPoolTest.php b/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/ImporterPoolTest.php index f3dcfdda3e04d..0426358d46a12 100644 --- a/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/ImporterPoolTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/ImporterPoolTest.php @@ -6,7 +6,6 @@ namespace Magento\Deploy\Test\Unit\Model\DeploymentConfig; use Magento\Deploy\Model\DeploymentConfig\ImporterPool; -use Magento\Framework\App\DeploymentConfig\ImporterInterface; use Magento\Framework\ObjectManagerInterface; class ImporterPoolTest extends \PHPUnit_Framework_TestCase @@ -21,35 +20,20 @@ class ImporterPoolTest extends \PHPUnit_Framework_TestCase */ private $objectManagerMock; - /** - * @var ImporterInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $importerMock; - - /** - * @var \StdClass - */ - private $wrongImporter; - /** * @return void */ protected function setUp() { - $this->importerMock = $this->getMockBuilder(ImporterInterface::class) - ->getMockForAbstractClass(); - $this->wrongImporter = new \StdClass(); $this->objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class) ->getMockForAbstractClass(); - $this->objectManagerMock->expects($this->any()) - ->method('get') - ->willReturnMap([ - ['Magento\Importer\SomeSection', $this->importerMock], - ['Magento\Importer\WrongSection', $this->wrongImporter], - ]); $this->configImporterPool = new ImporterPool( $this->objectManagerMock, - ['someSection' => 'Magento\Importer\SomeSection'] + [ + 'firstSection' => ['class' => 'Magento\Importer\SomeImporter', 'sortOrder' => 20], + 'secondSection' => ['class' => 'Magento\Importer\SomeImporter'], + 'thirdSection' => ['class' => 'Magento\Importer\SomeImporter', 'sortOrder' => 10] + ] ); } @@ -58,22 +42,24 @@ protected function setUp() */ public function testGetImporters() { - $expectedResult = ['someSection' => $this->importerMock]; + $expectedResult = [ + 'secondSection' => 'Magento\Importer\SomeImporter', + 'thirdSection' => 'Magento\Importer\SomeImporter', + 'firstSection' => 'Magento\Importer\SomeImporter', + ]; $this->assertSame($expectedResult, $this->configImporterPool->getImporters()); } /** * @return void * @expectedException \Magento\Framework\Exception\ConfigurationMismatchException - * @codingStandardsIgnoreStart - * @expectedExceptionMessage wrongSection: Instance of Magento\Framework\App\DeploymentConfig\ImporterInterface is expected, got stdClass instead - * @codingStandardsIgnoreEnd + * @expectedExceptionMessage Parameter "class" must be present. */ - public function testGetImportersWithException() + public function testGetImportersEmptyParameterClass() { $this->configImporterPool = new ImporterPool( $this->objectManagerMock, - ['wrongSection' => 'Magento\Importer\WrongSection'] + ['wrongSection' => ['class' => '']] ); $this->configImporterPool->getImporters(); @@ -84,6 +70,9 @@ public function testGetImportersWithException() */ public function testGetSections() { - $this->assertSame(['someSection'], $this->configImporterPool->getSections()); + $this->assertSame( + ['firstSection', 'secondSection', 'thirdSection'], + $this->configImporterPool->getSections() + ); } } diff --git a/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/ValidatorTest.php b/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/ValidatorTest.php index 616d2da9291b9..b52bdb7019264 100644 --- a/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/ValidatorTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/ValidatorTest.php @@ -55,18 +55,27 @@ protected function setUp() } /** - * @param string $configData + * @param string $sectionName + * @param array $fullConfigData + * @param string|null $configData * @param string $generatedHash * @param string $savedHash * @param bool $expectedResult * @return void * @dataProvider isValidDataProvider */ - public function testIsValid($configData, $generatedHash, $savedHash, $expectedResult) - { + public function testIsValid( + $sectionName, + $fullConfigData, + $configData, + $generatedHash, + $savedHash, + $expectedResult + ) { $this->dataConfigCollectorMock->expects($this->once()) ->method('getConfig') - ->willReturn($configData); + ->with($sectionName) + ->willReturn($fullConfigData); $this->hashGeneratorMock->expects($this->any()) ->method('generate') ->with($configData) @@ -75,7 +84,7 @@ public function testIsValid($configData, $generatedHash, $savedHash, $expectedRe ->method('get') ->willReturn($savedHash); - $this->assertSame($expectedResult, $this->validator->isValid()); + $this->assertSame($expectedResult, $this->validator->isValid($sectionName)); } /** @@ -84,11 +93,45 @@ public function testIsValid($configData, $generatedHash, $savedHash, $expectedRe public function isValidDataProvider() { return [ - ['configData' => 'some data', 'generatedHash' => '123', 'savedHash' => '123', 'expectedResult' => true], - ['configData' => 'some data', 'generatedHash' => '321', 'savedHash' => '123', 'expectedResult' => false], - ['configData' => 'some data', 'generatedHash' => '321', 'savedHash' => null, 'expectedResult' => false], - ['configData' => null, 'generatedHash' => '321', 'savedHash' => '123', 'expectedResult' => true], - ['configData' => null, 'generatedHash' => '321', 'savedHash' => null, 'expectedResult' => true], + [ + 'sectionName' => null, + 'fullConfigData' => ['section' => 'some data'], + 'configData' => 'some data', + 'generatedHash' => '123', + 'savedHash' => ['section' => '123'], + 'expectedResult' => true + ], + [ + 'sectionName' => 'section', + 'fullConfigData' => ['section' => 'some data'], + 'configData' => 'some data', + 'generatedHash' => '321', + 'savedHash' => ['section' => '123'], + 'expectedResult' => false + ], + [ + 'sectionName' => null, + 'fullConfigData' => ['section' => 'some data'], + 'configData' => 'some data', + 'generatedHash' => '321', + 'savedHash' => [], + 'expectedResult' => false + ], + [ + 'sectionName' => 'section', + 'fullConfigData' => [], + 'configData' => null, + 'generatedHash' => '321', + 'savedHash' => ['section' => '123'], + 'expectedResult' => true], + [ + 'sectionName' => null, + 'fullConfigData' => [], + 'configData' => null, + 'generatedHash' => '321', + 'savedHash' => [], + 'expectedResult' => true + ], ]; } } diff --git a/app/code/Magento/Deploy/Test/Unit/Model/Plugin/ConfigValidatorTest.php b/app/code/Magento/Deploy/Test/Unit/Model/Plugin/ConfigValidatorTest.php index 5121f373dec8c..f3b8255617036 100644 --- a/app/code/Magento/Deploy/Test/Unit/Model/Plugin/ConfigValidatorTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Model/Plugin/ConfigValidatorTest.php @@ -7,7 +7,7 @@ use Magento\Deploy\Model\Plugin\ConfigValidator; use Magento\Deploy\Model\DeploymentConfig\Validator; -use Magento\Framework\App\FrontController; +use Magento\Framework\App\FrontControllerInterface; use Magento\Framework\App\RequestInterface; class ConfigValidatorTest extends \PHPUnit_Framework_TestCase @@ -23,7 +23,7 @@ class ConfigValidatorTest extends \PHPUnit_Framework_TestCase private $configValidatorMock; /** - * @var FrontController|\PHPUnit_Framework_MockObject_MockObject + * @var FrontControllerInterface|\PHPUnit_Framework_MockObject_MockObject */ private $frontControllerMock; @@ -40,9 +40,8 @@ protected function setUp() $this->configValidatorMock = $this->getMockBuilder(Validator::class) ->disableOriginalConstructor() ->getMock(); - $this->frontControllerMock = $this->getMockBuilder(FrontController::class) - ->disableOriginalConstructor() - ->getMock(); + $this->frontControllerMock = $this->getMockBuilder(FrontControllerInterface::class) + ->getMockForAbstractClass(); $this->requestMock = $this->getMockBuilder(RequestInterface::class) ->getMockForAbstractClass(); diff --git a/app/code/Magento/Deploy/composer.json b/app/code/Magento/Deploy/composer.json index 436c3a6451c39..5d351d23d05c3 100644 --- a/app/code/Magento/Deploy/composer.json +++ b/app/code/Magento/Deploy/composer.json @@ -3,7 +3,7 @@ "description": "N/A", "require": { "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", - "magento/framework": "100.2.*", + "magento/framework": "100.*.*", "magento/module-store": "100.2.*", "magento/module-require-js": "100.2.*", "magento/module-user": "100.2.*", diff --git a/app/code/Magento/Deploy/etc/di.xml b/app/code/Magento/Deploy/etc/di.xml index 86d6ce1da1c75..b7b59ed68bad3 100644 --- a/app/code/Magento/Deploy/etc/di.xml +++ b/app/code/Magento/Deploy/etc/di.xml @@ -20,7 +20,7 @@ - + diff --git a/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php b/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php index 2570c2b6c92e5..1724f964f1301 100644 --- a/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php @@ -5,8 +5,6 @@ */ namespace Magento\Config\Console\Command; -use Magento\Config\Model\Config\PathValidator; -use Magento\Config\Model\Config\PathValidatorFactory; use Magento\Framework\App\Config\ConfigPathResolver; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\DeploymentConfig\FileReader; @@ -46,16 +44,6 @@ class ConfigSetCommandTest extends \PHPUnit_Framework_TestCase */ private $outputMock; - /** - * @var PathValidatorFactory|Mock - */ - private $pathValidatorFactoryMock; - - /** - * @var PathValidator|Mock - */ - private $pathValidatorMock; - /** * @var ScopeConfigInterface */ @@ -106,18 +94,6 @@ protected function setUp() ->getMockForAbstractClass(); $this->outputMock = $this->getMockBuilder(OutputInterface::class) ->getMockForAbstractClass(); - $this->pathValidatorFactoryMock = $this->getMockBuilder(PathValidatorFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->pathValidatorMock = $this->getMockBuilder(PathValidator::class) - ->disableOriginalConstructor() - ->setMethods(['validate']) - ->getMock(); - - $this->pathValidatorFactoryMock->expects($this->any()) - ->method('create') - ->willReturn($this->pathValidatorMock); } /** @@ -175,9 +151,7 @@ public function testRun($path, $value, $scope = ScopeConfigInterface::SCOPE_TYPE ); /** @var ConfigSetCommand $command */ - $command = $this->objectManager->create(ConfigSetCommand::class, [ - 'pathValidatorFactory' => $this->pathValidatorFactoryMock, - ]); + $command = $this->objectManager->create(ConfigSetCommand::class); $status = $command->run($this->inputMock, $this->outputMock); $this->assertSame(Cli::RETURN_SUCCESS, $status); @@ -195,8 +169,9 @@ public function testRun($path, $value, $scope = ScopeConfigInterface::SCOPE_TYPE public function runDataProvider() { return [ - ['test/test/test', 'value'], - ['test/test/test2', 'value2', ScopeInterface::SCOPE_WEBSITE, 'base'] + ['general/region/display_all', '1'], + ['general/region/state_required', 'BR,FR', ScopeInterface::SCOPE_WEBSITE, 'base'], + ['admin/security/use_form_key', '0'], ]; } @@ -234,9 +209,7 @@ public function testRunLock($path, $value, $scope = ScopeConfigInterface::SCOPE_ ); /** @var ConfigSetCommand $command */ - $command = $this->objectManager->create(ConfigSetCommand::class, [ - 'pathValidatorFactory' => $this->pathValidatorFactoryMock, - ]); + $command = $this->objectManager->create(ConfigSetCommand::class); /** @var ConfigPathResolver $resolver */ $resolver = $this->objectManager->get(ConfigPathResolver::class); $status = $command->run($this->inputMock, $this->outputMock); @@ -334,9 +307,7 @@ private function runCommand( ->with($expectedMessage); /** @var ConfigSetCommand $command */ - $command = $this->objectManager->create(ConfigSetCommand::class, [ - 'pathValidatorFactory' => $this->pathValidatorFactoryMock, - ]); + $command = $this->objectManager->create(ConfigSetCommand::class); $status = $command->run($input, $output); $this->assertSame($expectedCode, $status); @@ -386,9 +357,7 @@ public function testRunScopeValidation( $expectations($this->outputMock); /** @var ConfigSetCommand $command */ - $command = $this->objectManager->create(ConfigSetCommand::class, [ - 'pathValidatorFactory' => $this->pathValidatorFactoryMock, - ]); + $command = $this->objectManager->create(ConfigSetCommand::class); $command->run($this->inputMock, $this->outputMock); } @@ -406,8 +375,8 @@ function (Mock $output) { ->method('writeln') ->with('Value was saved.'); }, - 'test/test/test', - 'value', + 'general/region/state_required', + 'CA', ScopeInterface::SCOPE_WEBSITE, 'base' ], diff --git a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ApplicationDumpCommandTest.php b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ApplicationDumpCommandTest.php index 7b32104ca8521..9fc45cb13a9a5 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ApplicationDumpCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ApplicationDumpCommandTest.php @@ -51,9 +51,9 @@ class ApplicationDumpCommandTest extends \PHPUnit_Framework_TestCase private $config; /** - * @var array + * @var Hash */ - private $envConfig; + private $hash; /** * @inheritdoc @@ -67,10 +67,10 @@ public function setUp() $this->reader = $this->objectManager->get(DeploymentConfig\Reader::class); $this->writer = $this->objectManager->get(DeploymentConfig\Writer::class); $this->configFilePool = $this->objectManager->get(ConfigFilePool::class); + $this->hash = $this->objectManager->get(Hash::class); // Snapshot of configuration. $this->config = $this->loadConfig(); - $this->envConfig = $this->loadEnvConfig(); } /** @@ -81,21 +81,12 @@ private function loadConfig() return $this->reader->load(ConfigFilePool::APP_CONFIG); } - /** - * @return array - */ - private function loadEnvConfig() - { - return $this->reader->load(ConfigFilePool::APP_ENV); - } - /** * @magentoDbIsolation enabled * @magentoDataFixture Magento/Deploy/_files/config_data.php */ public function testExecute() { - $this->assertArrayNotHasKey(Hash::CONFIG_KEY, $this->envConfig); $this->objectManager->configure([ \Magento\Config\Model\Config\Export\ExcludeList::class => [ 'arguments' => [ @@ -127,7 +118,7 @@ public function testExecute() $this->validateSystemSection($config); $this->validateThemesSection($config); - $this->assertArrayHasKey(Hash::CONFIG_KEY, $this->loadEnvConfig()); + $this->assertSame([], $this->hash->get()); } /** @@ -214,11 +205,5 @@ public function tearDown() /** @var DeploymentConfig $deploymentConfig */ $deploymentConfig = $this->objectManager->get(DeploymentConfig::class); $deploymentConfig->resetData(); - - $this->filesystem->getDirectoryWrite(DirectoryList::CONFIG)->writeFile( - $this->configFilePool->getPath(ConfigFilePool::APP_ENV), - "writer->saveConfig([ConfigFilePool::APP_ENV => $this->envConfig]); } } diff --git a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ConfigImportCommand/IntegrationTestImporter.php b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ConfigImportCommand/IntegrationTestImporter.php index 131be392d00c9..6877a23b2435f 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ConfigImportCommand/IntegrationTestImporter.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ConfigImportCommand/IntegrationTestImporter.php @@ -12,6 +12,7 @@ class IntegrationTestImporter implements ImporterInterface /** * @param array $data * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function import(array $data) { diff --git a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ConfigImportCommand/IntegrationTestSecondImporter.php b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ConfigImportCommand/IntegrationTestSecondImporter.php new file mode 100644 index 0000000000000..27c9c69f6edb7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ConfigImportCommand/IntegrationTestSecondImporter.php @@ -0,0 +1,23 @@ +Integration second test data is imported!'; + + return $messages; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ConfigImportCommandTest.php b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ConfigImportCommandTest.php index 6c32edee01200..49dc066f813c1 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ConfigImportCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ConfigImportCommandTest.php @@ -15,6 +15,7 @@ use Magento\Deploy\Model\DeploymentConfig\ImporterPool; use Symfony\Component\Console\Tester\CommandTester; use Magento\Deploy\Console\Command\App\ConfigImportCommand\IntegrationTestImporter; +use Magento\Deploy\Console\Command\App\ConfigImportCommand\IntegrationTestSecondImporter; use Magento\Deploy\Model\DeploymentConfig\Hash; /** @@ -48,9 +49,9 @@ class ConfigImportCommandTest extends \PHPUnit_Framework_TestCase private $configFilePool; /** - * @var array + * @var Hash */ - private $envConfig; + private $hash; /** * @var array @@ -64,7 +65,13 @@ protected function setUp() ImporterPool::class => [ 'arguments' => [ 'importers' => [ - 'integrationTestImporter' => IntegrationTestImporter::class + 'integrationTestImporter' => [ + 'class' => IntegrationTestImporter::class, + 'sortOrder' => 20 + ], + 'integrationTestSecondImporter' => [ + 'class' => IntegrationTestSecondImporter::class, + ] ] ] ] @@ -73,8 +80,8 @@ protected function setUp() $this->writer = $this->objectManager->get(DeploymentConfig\Writer::class); $this->filesystem = $this->objectManager->get(Filesystem::class); $this->configFilePool = $this->objectManager->get(ConfigFilePool::class); + $this->hash = $this->objectManager->get(Hash::class); - $this->envConfig = $this->loadEnvConfig(); $this->config = $this->loadConfig(); } @@ -87,30 +94,28 @@ public function tearDown() /** @var DeploymentConfig\Writer $writer */ $writer = $this->objectManager->get(DeploymentConfig\Writer::class); $writer->saveConfig([ConfigFilePool::APP_CONFIG => $this->config]); - - $this->filesystem = $this->objectManager->get(Filesystem::class); - $this->filesystem->getDirectoryWrite(DirectoryList::CONFIG)->writeFile( - $this->configFilePool->getPath(ConfigFilePool::APP_ENV), - "writer->saveConfig([ConfigFilePool::APP_ENV => $this->envConfig]); } + /** + * @magentoDbIsolation enabled + */ public function testExecuteNothingImport() { - $this->assertArrayNotHasKey(Hash::CONFIG_KEY, $this->envConfig); + $this->assertEmpty($this->hash->get()); $command = $this->objectManager->create(ConfigImportCommand::class); $commandTester = new CommandTester($command); $commandTester->execute([]); $this->assertSame(Cli::RETURN_SUCCESS, $commandTester->getStatusCode()); - $this->assertContains('Start import', $commandTester->getDisplay()); - $this->assertContains('Nothing to import', $commandTester->getDisplay()); - $this->assertArrayNotHasKey(Hash::CONFIG_KEY, $this->loadEnvConfig()); + $this->assertContains('Nothing to import.', $commandTester->getDisplay()); + $this->assertEmpty($this->hash->get()); } + /** + * @magentoDbIsolation enabled + */ public function testExecuteWithImport() { - $this->assertArrayNotHasKey(Hash::CONFIG_KEY, $this->envConfig); + $this->assertEmpty($this->hash->get()); $this->filesystem->getDirectoryWrite(DirectoryList::CONFIG)->writeFile( $this->configFilePool->getPath(ConfigFilePool::APP_CONFIG), file_get_contents(__DIR__ . '/../../../_files/config.php') @@ -120,8 +125,12 @@ public function testExecuteWithImport() $commandTester->execute([]); $this->assertSame(Cli::RETURN_SUCCESS, $commandTester->getStatusCode()); $this->assertContains('Start import', $commandTester->getDisplay()); - $this->assertContains('Integration test data is imported!', $commandTester->getDisplay()); - $this->assertArrayHasKey(Hash::CONFIG_KEY, $this->loadEnvConfig()); + $this->assertContains( + "Integration second test data is imported!\nIntegration test data is imported!", + $commandTester->getDisplay() + ); + $this->assertArrayHasKey('integrationTestImporter', $this->hash->get()); + $this->assertArrayHasKey('integrationTestSecondImporter', $this->hash->get()); } /** @@ -131,12 +140,4 @@ private function loadConfig() { return $this->reader->load(ConfigFilePool::APP_CONFIG); } - - /** - * @return array - */ - private function loadEnvConfig() - { - return $this->reader->load(ConfigFilePool::APP_ENV); - } } diff --git a/dev/tests/integration/testsuite/Magento/Deploy/_files/config.php b/dev/tests/integration/testsuite/Magento/Deploy/_files/config.php index 1a392714716c2..d000e97fa695e 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/_files/config.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/_files/config.php @@ -25,4 +25,9 @@ 'someField' => 'testValue', ] ], + 'integrationTestSecondImporter' => [ + 'someGroup' => [ + 'someField' => 'testSecondValue', + ] + ], ]; diff --git a/lib/internal/Magento/Framework/Exception/RuntimeException.php b/lib/internal/Magento/Framework/Exception/RuntimeException.php new file mode 100644 index 0000000000000..f46bdff7df0bd --- /dev/null +++ b/lib/internal/Magento/Framework/Exception/RuntimeException.php @@ -0,0 +1,14 @@ +