![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/old/vendor/magento/framework/Setup/Test/Unit/Patch/ |
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ declare(strict_types=1); namespace Magento\Framework\Setup\Test\Unit\Patch; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\Module\ModuleResource; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; use Magento\Framework\Setup\Patch\PatchApplier; use Magento\Framework\Setup\Patch\PatchBackwardCompatability; use Magento\Framework\Setup\Patch\PatchFactory; use Magento\Framework\Setup\Patch\PatchHistory; use Magento\Framework\Setup\Patch\PatchInterface; use Magento\Framework\Setup\Patch\PatchReader; use Magento\Framework\Setup\Patch\PatchRegistry; use Magento\Framework\Setup\Patch\PatchRegistryFactory; use Magento\Framework\Setup\SchemaSetupInterface; use Magento\Framework\Setup\SetupInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PatchApplierTest extends TestCase { /** * @var PatchRegistryFactory|MockObject */ private $patchRegistryFactoryMock; /** * @var PatchReader|MockObject */ private $dataPatchReaderMock; /** * @var PatchReader|MockObject */ private $schemaPatchReaderMock; /** * @var ResourceConnection|MockObject */ private $resourceConnectionMock; /** * @var ModuleResource|MockObject */ private $moduleResourceMock; /** * @var PatchHistory|MockObject */ private $patchHistoryMock; /** * @var PatchFactory|MockObject */ private $patchFactoryMock; /** * @var SetupInterface|MockObject */ private $schemaSetupMock; /** * @var ModuleDataSetupInterface|MockObject */ private $moduleDataSetupMock; /** * @var ObjectManagerInterface|MockObject */ private $objectManagerMock; /** * @var PatchApplier */ private $patchApllier; /** * @var AdapterInterface|MockObject */ private $connectionMock; /** * @var PatchBackwardCompatability|MockObject */ private $patchBackwardCompatability; protected function setUp(): void { $this->patchRegistryFactoryMock = $this->createMock(PatchRegistryFactory::class); $this->dataPatchReaderMock = $this->createMock(PatchReader::class); $this->schemaPatchReaderMock = $this->createMock(PatchReader::class); $this->resourceConnectionMock = $this->createMock(ResourceConnection::class); $this->moduleResourceMock = $this->createMock(ModuleResource::class); $this->patchHistoryMock = $this->createMock(PatchHistory::class); $this->patchFactoryMock = $this->createMock(PatchFactory::class); $this->schemaSetupMock = $this->getMockForAbstractClass(SchemaSetupInterface::class); $this->moduleDataSetupMock = $this->getMockForAbstractClass(ModuleDataSetupInterface::class); $this->objectManagerMock = $this->getMockForAbstractClass(ObjectManagerInterface::class); $this->connectionMock = $this->getMockForAbstractClass(AdapterInterface::class); $this->moduleDataSetupMock->expects($this->any())->method('getConnection')->willReturn($this->connectionMock); $objectManager = new ObjectManager($this); $this->patchBackwardCompatability = $objectManager->getObject( PatchBackwardCompatability::class, [ 'moduleResource' => $this->moduleResourceMock ] ); $this->patchApllier = $objectManager->getObject( PatchApplier::class, [ 'patchRegistryFactory' => $this->patchRegistryFactoryMock, 'dataPatchReader' => $this->dataPatchReaderMock, 'schemaPatchReader' => $this->schemaPatchReaderMock, 'resourceConnection' => $this->resourceConnectionMock, 'moduleResource' => $this->moduleResourceMock, 'patchHistory' => $this->patchHistoryMock, 'patchFactory' => $this->patchFactoryMock, 'objectManager' => $this->objectManagerMock, 'schemaSetup' => $this->schemaSetupMock, 'moduleDataSetup' => $this->moduleDataSetupMock, 'patchBackwardCompatability' => $this->patchBackwardCompatability ] ); require_once __DIR__ . '/../_files/data_patch_classes.php'; require_once __DIR__ . '/../_files/schema_patch_classes.php'; } /** * @param $moduleName * @param $dataPatches * @param $moduleVersionInDb * * @dataProvider applyDataPatchDataNewModuleProvider() */ public function testApplyDataPatchForNewlyInstalledModule($moduleName, $dataPatches, $moduleVersionInDb) { $this->dataPatchReaderMock->expects($this->once()) ->method('read') ->with($moduleName) ->willReturn($dataPatches); $this->moduleResourceMock->expects($this->any())->method('getDataVersion')->willReturnMap( [ [$moduleName, $moduleVersionInDb] ] ); // phpstan:ignore $patches = [ \SomeDataPatch::class, // phpstan:ignore \OtherDataPatch::class ]; $patchRegistryMock = $this->createAggregateIteratorMock(PatchRegistry::class, $patches, ['registerPatch']); $patchRegistryMock->expects($this->exactly(2)) ->method('registerPatch'); $this->patchRegistryFactoryMock->expects($this->any()) ->method('create') ->willReturn($patchRegistryMock); // phpstan:ignore "Class SomeDataPatch not found." $patch1 = $this->createMock(\SomeDataPatch::class); $patch1->expects($this->once())->method('apply'); $patch1->expects($this->once())->method('getAliases')->willReturn([]); // phpstan:ignore "Class OtherDataPatch not found." $patch2 = $this->createMock(\OtherDataPatch::class); $patch2->expects($this->once())->method('apply'); $patch2->expects($this->once())->method('getAliases')->willReturn([]); $this->objectManagerMock->expects($this->any())->method('create')->willReturnMap( [ // phpstan:ignore ['\\' . \SomeDataPatch::class, ['moduleDataSetup' => $this->moduleDataSetupMock], $patch1], // phpstan:ignore ['\\' . \OtherDataPatch::class, ['moduleDataSetup' => $this->moduleDataSetupMock], $patch2], ] ); $this->connectionMock->expects($this->exactly(2))->method('beginTransaction'); $this->connectionMock->expects($this->exactly(2))->method('commit'); $this->patchHistoryMock->expects($this->any())->method('fixPatch')->willReturnMap( [ [get_class($patch1)], [get_class($patch2)], ] ); $this->patchApllier->applyDataPatch($moduleName); } /** * @param $moduleName * @param $dataPatches * @param $moduleVersionInDb * * @dataProvider applyDataPatchDataNewModuleProvider() */ public function testApplyDataPatchForAlias($moduleName, $dataPatches, $moduleVersionInDb) { $this->dataPatchReaderMock->expects($this->once()) ->method('read') ->with($moduleName) ->willReturn($dataPatches); $this->moduleResourceMock->expects($this->any())->method('getDataVersion')->willReturnMap( [ [$moduleName, $moduleVersionInDb] ] ); $patch1 = $this->getMockForAbstractClass(DataPatchInterface::class); $patch1->expects($this->once())->method('getAliases')->willReturn(['PatchAlias']); $patchClass = get_class($patch1); $patchRegistryMock = $this->createAggregateIteratorMock(PatchRegistry::class, [$patchClass], ['registerPatch']); $patchRegistryMock->expects($this->any()) ->method('registerPatch'); $this->patchRegistryFactoryMock->expects($this->any()) ->method('create') ->willReturn($patchRegistryMock); $this->objectManagerMock->expects($this->any())->method('create')->willReturnMap( [ ['\\' . $patchClass, ['moduleDataSetup' => $this->moduleDataSetupMock], $patch1], ] ); $this->patchApllier->applyDataPatch($moduleName); } /** * @return array */ public function applyDataPatchDataNewModuleProvider() { return [ 'newly installed module' => [ 'moduleName' => 'Module1', 'dataPatches' => [ // phpstan:ignore \SomeDataPatch::class, // phpstan:ignore \OtherDataPatch::class ], 'moduleVersionInDb' => null, ], ]; } /** * @param $moduleName * @param $dataPatches * @param $moduleVersionInDb * * @dataProvider applyDataPatchDataInstalledModuleProvider() */ public function testApplyDataPatchForInstalledModule($moduleName, $dataPatches, $moduleVersionInDb) { $this->dataPatchReaderMock->expects($this->once()) ->method('read') ->with($moduleName) ->willReturn($dataPatches); $this->moduleResourceMock->expects($this->any())->method('getDataVersion')->willReturnMap( [ [$moduleName, $moduleVersionInDb] ] ); // phpstan:ignore $patches = [ \SomeDataPatch::class, // phpstan:ignore \OtherDataPatch::class ]; $patchRegistryMock = $this->createAggregateIteratorMock( PatchRegistry::class, $patches, ['registerPatch'] ); $patchRegistryMock->expects(self::exactly(2)) ->method('registerPatch'); $this->patchRegistryFactoryMock->expects(self::any()) ->method('create') ->willReturn($patchRegistryMock); // phpstan:ignore "Class SomeDataPatch not found." $patch1 = $this->createMock(\SomeDataPatch::class); $patch1->expects(self::never())->method('apply'); $patch1->expects(self::any())->method('getAliases')->willReturn([]); // phpstan:ignore "Class OtherDataPatch not found." $patch2 = $this->createMock(\OtherDataPatch::class); $patch2->expects(self::once())->method('apply'); $patch2->expects(self::any())->method('getAliases')->willReturn([]); $this->objectManagerMock->expects(self::any())->method('create')->willReturnMap( [ // phpstan:ignore ['\\' . \SomeDataPatch::class, ['moduleDataSetup' => $this->moduleDataSetupMock], $patch1], // phpstan:ignore ['\\' . \OtherDataPatch::class, ['moduleDataSetup' => $this->moduleDataSetupMock], $patch2], ] ); $this->connectionMock->expects(self::exactly(1))->method('beginTransaction'); $this->connectionMock->expects(self::exactly(1))->method('commit'); $this->patchHistoryMock->expects(self::exactly(2))->method('fixPatch'); $this->patchApllier->applyDataPatch($moduleName); } /** * @return array */ public function applyDataPatchDataInstalledModuleProvider() { return [ 'upgrade module iwth only OtherDataPatch' => [ 'moduleName' => 'Module1', 'dataPatches' => [ // phpstan:ignore \SomeDataPatch::class, // phpstan:ignore \OtherDataPatch::class ], 'moduleVersionInDb' => '2.0.0', ] ]; } /** * @param $moduleName * @param $dataPatches * @param $moduleVersionInDb * * * @dataProvider applyDataPatchDataInstalledModuleProvider() */ public function testApplyDataPatchRollback($moduleName, $dataPatches, $moduleVersionInDb) { $this->expectException('Exception'); $this->expectExceptionMessage('Patch Apply Error'); $this->dataPatchReaderMock->expects($this->once()) ->method('read') ->with($moduleName) ->willReturn($dataPatches); $this->moduleResourceMock->expects($this->any())->method('getDataVersion')->willReturnMap( [ [$moduleName, $moduleVersionInDb] ] ); // phpstan:ignore $patches = [ \SomeDataPatch::class, // phpstan:ignore \OtherDataPatch::class ]; $patchRegistryMock = $this->createAggregateIteratorMock(PatchRegistry::class, $patches, ['registerPatch']); $patchRegistryMock->expects($this->exactly(2)) ->method('registerPatch'); $this->patchRegistryFactoryMock->expects($this->any()) ->method('create') ->willReturn($patchRegistryMock); // phpstan:ignore "Class SomeDataPatch not found." $patch1 = $this->createMock(\SomeDataPatch::class); $patch1->expects($this->never())->method('apply'); // phpstan:ignore "Class OtherDataPatch not found." $patch2 = $this->createMock(\OtherDataPatch::class); $exception = new \Exception('Patch Apply Error'); $patch2->expects($this->once())->method('apply')->willThrowException($exception); $this->objectManagerMock->expects($this->any())->method('create')->willReturnMap( [ // phpstan:ignore ['\\' . \SomeDataPatch::class, ['moduleDataSetup' => $this->moduleDataSetupMock], $patch1], // phpstan:ignore ['\\' . \OtherDataPatch::class, ['moduleDataSetup' => $this->moduleDataSetupMock], $patch2], ] ); $this->connectionMock->expects($this->exactly(1))->method('beginTransaction'); $this->connectionMock->expects($this->never())->method('commit'); $this->connectionMock->expects($this->exactly(1))->method('rollback'); $this->patchHistoryMock->expects($this->exactly(1))->method('fixPatch'); $this->patchApllier->applyDataPatch($moduleName); } public function testNonDataPatchApply() { $this->expectException('Exception'); $this->expectExceptionMessageMatches('"Patch [a-zA-Z0-9\_]+ should implement DataPatchInterface"'); $this->dataPatchReaderMock->expects($this->once()) ->method('read') ->with('module1') ->willReturn([\stdClass::class]); $patchRegistryMock = $this->createAggregateIteratorMock( PatchRegistry::class, [\stdClass::class], ['registerPatch'] ); $patchRegistryMock->expects($this->exactly(1)) ->method('registerPatch'); $this->patchRegistryFactoryMock->expects($this->any()) ->method('create') ->willReturn($patchRegistryMock); $this->objectManagerMock->expects($this->any())->method('create')->willReturnMap( [ [ '\\' . \stdClass::class, ['moduleDataSetup' => $this->moduleDataSetupMock], $this->createMock(\stdClass::class) ], ] ); $this->patchApllier->applyDataPatch('module1'); } public function testNonTransactionablePatch() { // phpstan:ignore "Class NonTransactionableDataPatch not found." $patches = [\NonTransactionableDataPatch::class]; $this->dataPatchReaderMock->expects($this->once()) ->method('read') ->with('module1') ->willReturn($patches); $patchRegistryMock = $this->createAggregateIteratorMock( PatchRegistry::class, $patches, ['registerPatch'] ); $patchRegistryMock->expects($this->exactly(1)) ->method('registerPatch'); $this->patchRegistryFactoryMock->expects($this->any()) ->method('create') ->willReturn($patchRegistryMock); $patch1 = $this->createMock($patches[0]); $patch1->expects($this->exactly(1))->method('apply'); $this->connectionMock->expects($this->never())->method('beginTransaction'); $this->connectionMock->expects($this->never())->method('commit'); $this->connectionMock->expects($this->never())->method('rollback'); $this->patchHistoryMock->expects($this->once())->method('fixPatch')->with(get_class($patch1)); $this->objectManagerMock->expects($this->any())->method('create')->willReturnMap( [ [ '\\' . $patches[0], ['moduleDataSetup' => $this->moduleDataSetupMock], $patch1 ], ] ); $this->patchApllier->applyDataPatch('module1'); } /** * @param $moduleName * @param $schemaPatches * @param $moduleVersionInDb * * @dataProvider schemaPatchDataProvider() */ public function testSchemaPatchAplly($moduleName, $schemaPatches, $moduleVersionInDb) { $this->schemaPatchReaderMock->expects($this->once()) ->method('read') ->with($moduleName) ->willReturn($schemaPatches); $this->moduleResourceMock->expects($this->any())->method('getDbVersion')->willReturnMap( [ [$moduleName, $moduleVersionInDb] ] ); // phpstan:ignore $patches = [ \SomeSchemaPatch::class, // phpstan:ignore \OtherSchemaPatch::class ]; $patchRegistryMock = $this->createAggregateIteratorMock(PatchRegistry::class, $patches, ['registerPatch']); $patchRegistryMock->expects($this->exactly(2)) ->method('registerPatch'); $this->patchRegistryFactoryMock->expects($this->any()) ->method('create') ->willReturn($patchRegistryMock); // phpstan:ignore "Class SomeSchemaPatch not found." $patch1 = $this->createMock(\SomeSchemaPatch::class); $patch1->expects($this->never())->method('apply'); $patch1->expects($this->any())->method('getAliases')->willReturn([]); // phpstan:ignore "Class OtherSchemaPatch not found." $patch2 = $this->createMock(\OtherSchemaPatch::class); $patch2->expects($this->once())->method('apply'); $patch2->expects($this->any())->method('getAliases')->willReturn([]); $this->patchFactoryMock->expects($this->any())->method('create')->willReturnMap( [ // phpstan:ignore [\SomeSchemaPatch::class, ['schemaSetup' => $this->schemaSetupMock], $patch1], // phpstan:ignore [\OtherSchemaPatch::class, ['schemaSetup' => $this->schemaSetupMock], $patch2], ] ); $this->connectionMock->expects($this->never())->method('beginTransaction'); $this->connectionMock->expects($this->never())->method('commit'); $this->patchHistoryMock->expects($this->exactly(2))->method('fixPatch'); $this->patchApllier->applySchemaPatch($moduleName); } /** * @param $moduleName * @param $schemaPatches * @param $moduleVersionInDb * * @dataProvider schemaPatchDataProvider() */ public function testSchemaPatchApplyForPatchAlias($moduleName, $schemaPatches, $moduleVersionInDb) { $this->schemaPatchReaderMock->expects($this->once()) ->method('read') ->with($moduleName) ->willReturn($schemaPatches); $this->moduleResourceMock->expects($this->any())->method('getDbVersion')->willReturnMap( [ [$moduleName, $moduleVersionInDb] ] ); $patch1 = $this->getMockForAbstractClass(PatchInterface::class); $patch1->expects($this->once())->method('getAliases')->willReturn(['PatchAlias']); $patchClass = get_class($patch1); $patchRegistryMock = $this->createAggregateIteratorMock(PatchRegistry::class, [$patchClass], ['registerPatch']); $patchRegistryMock->expects($this->any()) ->method('registerPatch'); $this->patchRegistryFactoryMock->expects($this->any()) ->method('create') ->willReturn($patchRegistryMock); $this->patchFactoryMock->expects($this->any())->method('create')->willReturn($patch1); $this->patchApllier->applySchemaPatch($moduleName); } public function testRevertDataPatches() { // phpstan:ignore "Class RevertableDataPatch not found." $patches = [\RevertableDataPatch::class]; $this->dataPatchReaderMock->expects($this->once()) ->method('read') ->with('module1') ->willReturn($patches); $patchRegistryMock = $this->createAggregateIteratorMock( PatchRegistry::class, $patches, ['registerPatch', 'getReverseIterator'] ); $patchRegistryMock->expects($this->exactly(1)) ->method('registerPatch'); $patchRegistryMock->expects($this->once())->method('getReverseIterator') ->willReturn(array_reverse($patches)); $this->patchRegistryFactoryMock->expects($this->any()) ->method('create') ->willReturn($patchRegistryMock); $patch1 = $this->createMock($patches[0]); $patch1->expects($this->exactly(1))->method('revert'); $this->connectionMock->expects($this->once())->method('beginTransaction'); $this->connectionMock->expects($this->once())->method('commit'); $this->connectionMock->expects($this->never())->method('rollback'); $this->patchHistoryMock->expects($this->once())->method('revertPatchFromHistory')->with(get_class($patch1)); $this->objectManagerMock->expects($this->any())->method('create')->willReturnMap( [ [ '\\' . $patches[0], ['moduleDataSetup' => $this->moduleDataSetupMock], $patch1 ], ] ); $this->patchApllier->revertDataPatches('module1'); } /** * @return array */ public function schemaPatchDataProvider() { return [ 'upgrade module iwth only OtherSchemaPatch' => [ 'moduleName' => 'Module1', 'schemaPatches' => [ // phpstan:ignore \SomeSchemaPatch::class, // phpstan:ignore \OtherSchemaPatch::class ], 'moduleVersionInDb' => '2.0.0', ] ]; } /** * Create mock of class that implements IteratorAggregate * * @param string $className * @param array $items * @param array $methods * @return MockObject|\IteratorAggregate * @throws \Exception */ private function createAggregateIteratorMock($className, array $items = [], array $methods = []) { if (!in_array(ltrim(\IteratorAggregate::class, '\\'), class_implements($className))) { throw new \Exception('Mock possible only for classes that implement IteratorAggregate interface.'); } /** * PHPUnit\Framework\MockObject\MockObject */ $someIterator = $this->createMock(\ArrayIterator::class); $mockIteratorAggregate = $this->getMockBuilder($className) ->disableOriginalConstructor() ->setMethods(array_merge($methods, ['getIterator'])) ->getMock(); $mockIteratorAggregate->expects($this->any())->method('getIterator')->willReturn($someIterator); $iterator = new \ArrayIterator($items); $someIterator->expects($this->any()) ->method('rewind') ->willReturnCallback( function () use ($iterator) { $iterator->rewind(); } ); $someIterator->expects($this->any()) ->method('current') ->willReturnCallback( function () use ($iterator) { return $iterator->current(); } ); $someIterator->expects($this->any()) ->method('key') ->willReturnCallback( function () use ($iterator) { return $iterator->key(); } ); $someIterator->expects($this->any()) ->method('next') ->willReturnCallback( function () use ($iterator) { $iterator->next(); } ); $someIterator->expects($this->any()) ->method('valid') ->willReturnCallback( function () use ($iterator) { return $iterator->valid(); } ); return $mockIteratorAggregate; } }