diff --git a/src/Config/linkmobility4oxid.yaml b/src/Config/linkmobility4oxid.yaml index 6c401eb..9179466 100644 --- a/src/Config/linkmobility4oxid.yaml +++ b/src/Config/linkmobility4oxid.yaml @@ -93,4 +93,11 @@ services: factory: 'oxNew' shared: false arguments: - - D3\Linkmobility4OXID\Application\Model\Exceptions\noRecipientFoundException \ No newline at end of file + - D3\Linkmobility4OXID\Application\Model\Exceptions\noRecipientFoundException + + D3\Linkmobility4OXID\Setup\Actions: + class: D3\Linkmobility4OXID\Setup\Actions + factory: 'oxNew' + shared: false + arguments: + - D3\Linkmobility4OXID\Setup\Actions \ No newline at end of file diff --git a/src/Config/oxid.yaml b/src/Config/oxid.yaml index 66996d0..49e4697 100644 --- a/src/Config/oxid.yaml +++ b/src/Config/oxid.yaml @@ -40,6 +40,14 @@ services: - 'getUtilsView' shared: true + # Email + d3ox.linkmobility.OxidEsales\Eshop\Core\DbMetaDataHandler: + class: 'OxidEsales\Eshop\Core\DbMetaDataHandler' + factory: 'oxNew' + arguments: + - 'OxidEsales\Eshop\Core\DbMetaDataHandler' + shared: false + # Email d3ox.linkmobility.OxidEsales\Eshop\Core\Email: class: 'OxidEsales\Eshop\Core\Email' @@ -85,4 +93,14 @@ services: factory: - 'OxidEsales\Eshop\Core\Registry' - 'getLogger' + shared: true + + # DB_assoc + d3ox.linkmobility.OxidEsales\Eshop\Core\Database\Adapter\DatabaseInterface.assoc: + class: 'OxidEsales\Eshop\Core\Database\Adapter\DatabaseInterface' + factory: + - 'OxidEsales\Eshop\Core\DatabaseProvider' + - 'getDb' + arguments: + - 2 shared: true \ No newline at end of file diff --git a/src/Setup/Actions.php b/src/Setup/Actions.php index f565eea..9655c8f 100644 --- a/src/Setup/Actions.php +++ b/src/Setup/Actions.php @@ -19,16 +19,22 @@ use D3\Linkmobility4OXID\Application\Model\MessageTypes\AbstractMessage; use Doctrine\DBAL\Driver\Exception as DoctrineDriverException; use Doctrine\DBAL\Exception as DoctrineException; use Doctrine\DBAL\Query\QueryBuilder; +use Monolog\Logger; +use OxidEsales\Eshop\Core\Database\Adapter\DatabaseInterface; +use OxidEsales\Eshop\Core\Database\Adapter\Doctrine\Database; use OxidEsales\Eshop\Core\DatabaseProvider; use OxidEsales\Eshop\Core\DbMetaDataHandler; use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; use OxidEsales\Eshop\Core\Exception\DatabaseErrorException; -use OxidEsales\Eshop\Core\Exception\DatabaseException; +use OxidEsales\Eshop\Core\Exception\StandardException; use OxidEsales\Eshop\Core\Registry; +use OxidEsales\Eshop\Core\UtilsView; use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory; use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface; use Psr\Container\ContainerExceptionInterface; +use Psr\Container\ContainerInterface; use Psr\Container\NotFoundExceptionInterface; +use Psr\Log\LoggerInterface; class Actions { @@ -43,9 +49,13 @@ class Actions if (!$this->hasRemarkTypeEnumValue()) { $this->addRemarkTypeEnumValue(); } - } catch (DatabaseException|DoctrineDriverException|DoctrineException $e) { - Registry::getLogger()->error($e->getMessage()); - Registry::getUtilsView()->addErrorToDisplay($e); + } catch (StandardException|DoctrineDriverException|DoctrineException $e) { + /** @var Logger $logger */ + $logger = d3GetOxidDIC()->get('d3ox.linkmobility.'.LoggerInterface::class); + $logger->error($e->getMessage()); + /** @var UtilsView $utilsView */ + $utilsView = d3GetOxidDIC()->get('d3ox.linkmobility.'.UtilsView::class); + $utilsView->addErrorToDisplay($e); } } @@ -56,7 +66,8 @@ class Actions */ public function regenerateViews() { - $oDbMetaDataHandler = oxNew(DbMetaDataHandler::class); + /** @var DbMetaDataHandler $oDbMetaDataHandler */ + $oDbMetaDataHandler = d3GetOxidDIC()->get('d3ox.linkmobility.'.DbMetaDataHandler::class); $oDbMetaDataHandler->updateViews(); } @@ -73,7 +84,7 @@ class Actions $patternEnumCheck = '/^\b(enum)\b/mi'; if (!preg_match($patternEnumCheck, $fieldType)) { - throw oxNew(DatabaseException::class, 'remark type field has not the expected enum type'); + throw oxNew(StandardException::class, 'remark type field has not the expected enum type'); } $patternValueCheck = '/\b('.preg_quote(AbstractMessage::REMARK_IDENT).')\b/mi'; @@ -91,7 +102,7 @@ class Actions protected function getRemarkTypeFieldType(): string { /** @var QueryBuilder $qb */ - $qb = ContainerFactory::getInstance()->getContainer()->get(QueryBuilderFactoryInterface::class)->create(); + $qb = $this->getContainer()->get(QueryBuilderFactoryInterface::class)->create(); $qb->select('column_type') ->from('INFORMATION_SCHEMA.COLUMNS') ->where( @@ -125,17 +136,10 @@ class Actions */ protected function addRemarkTypeEnumValue() { - $valuePattern = '/(?<=enum\().*(?=\))/i'; - preg_match($valuePattern, $this->getRemarkTypeFieldType(), $matches); + $items = $this->getUniqueFieldTypes(); - $items = array_unique( - array_merge( - str_getcsv($matches[0], ',', "'"), - [AbstractMessage::REMARK_IDENT] - ) - ); - - $db = DatabaseProvider::getDb(); + /** @var Database $db */ + $db = d3GetOxidDIC()->get('d3ox.linkmobility.'.DatabaseInterface::class.'.assoc'); $query = 'ALTER TABLE '.$db->quoteIdentifier('oxremark'). ' CHANGE '.$db->quoteIdentifier('OXTYPE'). ' '.$db->quoteIdentifier('OXTYPE') . @@ -144,4 +148,30 @@ class Actions $db->execute($query); } + + /** + * @return array + * @throws DoctrineDriverException + * @throws DoctrineException + */ + protected function getUniqueFieldTypes(): array + { + $valuePattern = '/(?<=enum\().*(?=\))/i'; + preg_match($valuePattern, $this->getRemarkTypeFieldType(), $matches); + + return array_unique( + array_merge( + str_getcsv($matches[0], ',', "'"), + [AbstractMessage::REMARK_IDENT] + ) + ); + } + + /** + * @return ContainerInterface + */ + public function getContainer(): ContainerInterface + { + return ContainerFactory::getInstance()->getContainer(); + } } \ No newline at end of file diff --git a/src/Setup/Events.php b/src/Setup/Events.php index 9cc4892..7e32872 100644 --- a/src/Setup/Events.php +++ b/src/Setup/Events.php @@ -36,7 +36,7 @@ class Events public static function onActivate() { /** @var Actions $actions */ - $actions = oxNew(Actions::class); + $actions = d3GetOxidDIC()->get(Actions::class); $actions->setupDatabase(); $actions->regenerateViews(); } diff --git a/src/tests/unit/Setup/ActionsTest.php b/src/tests/unit/Setup/ActionsTest.php new file mode 100644 index 0000000..95ad975 --- /dev/null +++ b/src/tests/unit/Setup/ActionsTest.php @@ -0,0 +1,299 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\Linkmobility4OXID\tests\unit\Setup; + +use D3\Linkmobility4OXID\Setup\Actions; +use D3\Linkmobility4OXID\tests\unit\LMUnitTestCase; +use D3\TestingTools\Development\CanAccessRestricted; +use Doctrine\DBAL\Driver\Result; +use Doctrine\DBAL\Query\Expression\ExpressionBuilder; +use Doctrine\DBAL\Query\QueryBuilder; +use Doctrine\DBAL\Statement; +use Monolog\Logger; +use OxidEsales\Eshop\Application\Model\Article; +use OxidEsales\Eshop\Core\Database\Adapter\DatabaseInterface; +use OxidEsales\Eshop\Core\Database\Adapter\Doctrine\Database; +use OxidEsales\Eshop\Core\DbMetaDataHandler; +use OxidEsales\Eshop\Core\Exception\StandardException; +use OxidEsales\Eshop\Core\UtilsView; +use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactory; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use ReflectionException; +use Symfony\Component\DependencyInjection\Container; + +class ActionsTest extends LMUnitTestCase +{ + use CanAccessRestricted; + + /** + * @test + * @param $hasEnumValue + * @param $invocationCount + * @param $throwException + * @return void + * @throws ReflectionException + * @dataProvider canSetupDatabaseDataProvider + * @covers \D3\Linkmobility4OXID\Setup\Actions::setupDatabase + */ + public function canSetupDatabase($hasEnumValue, $invocationCount, $throwException) + { + /** @var Logger|MockObject $loggerMock */ + $loggerMock = $this->getMockBuilder(Logger::class) + ->onlyMethods(['error']) + ->disableOriginalConstructor() + ->getMock(); + $loggerMock->expects($this->exactly((int) $throwException))->method('error'); + d3GetOxidDIC()->set('d3ox.linkmobility.'.LoggerInterface::class, $loggerMock); + + /** @var UtilsView|MockObject $utilsViewMock */ + $utilsViewMock = $this->getMockBuilder(UtilsView::class) + ->onlyMethods(['addErrorToDisplay']) + ->getMock(); + $utilsViewMock->expects($this->exactly((int) $throwException))->method('addErrorToDisplay'); + d3GetOxidDIC()->set('d3ox.linkmobility.'.UtilsView::class, $utilsViewMock); + + /** @var StandardException|MockObject $standardExceptionMock */ + $standardExceptionMock = $this->getMockBuilder(StandardException::class) + ->disableOriginalConstructor() + ->getMock(); + + /** @var Actions|MockObject $sut */ + $sut = $this->getMockBuilder(Actions::class) + ->onlyMethods(['hasRemarkTypeEnumValue', 'addRemarkTypeEnumValue']) + ->getMock(); + $sut->method('hasRemarkTypeEnumValue')->will( + $throwException ? + $this->throwException($standardExceptionMock) : + $this->returnValue($hasEnumValue) + ); + $sut->expects($invocationCount)->method('addRemarkTypeEnumValue'); + + $this->callMethod( + $sut, + 'setupDatabase' + ); + } + + /** + * @return array[] + */ + public function canSetupDatabaseDataProvider(): array + { + return [ + 'has enum value' => [true, $this->never(), false], + 'has no enum value' => [false, $this->once(), false], + 'throws exception' => [false, $this->never(), true], + ]; + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Linkmobility4OXID\Setup\Actions::regenerateViews + */ + public function canRegenerateViews() + { + /** @var DbMetaDataHandler|MockObject $dbMetaDataHandlerMock */ + $dbMetaDataHandlerMock = $this->getMockBuilder(DbMetaDataHandler::class) + ->onlyMethods(['updateViews']) + ->getMock(); + $dbMetaDataHandlerMock->expects($this->once())->method('updateViews'); + d3GetOxidDIC()->set('d3ox.linkmobility.'.DbMetaDataHandler::class, $dbMetaDataHandlerMock); + + /** @var Actions $sut */ + $sut = d3GetOxidDIC()->get(Actions::class); + + $this->callMethod( + $sut, + 'regenerateViews' + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @dataProvider canHasRemarkTypeEnumValueDataProvider + * @covers \D3\Linkmobility4OXID\Setup\Actions::hasRemarkTypeEnumValue + */ + public function canHasRemarkTypeEnumValue($fieldType, $expectException, $expected) + { + /** @var Actions|MockObject $sut */ + $sut = $this->getMockBuilder(Actions::class) + ->onlyMethods(['getRemarkTypeFieldType']) + ->getMock(); + $sut->method('getRemarkTypeFieldType')->willReturn($fieldType); + + if ($expectException) { + $this->expectException(StandardException::class); + } + + $this->assertSame( + $expected, + $this->callMethod( + $sut, + 'hasRemarkTypeEnumValue' + ) + ); + } + + /** + * @return array[] + */ + public function canHasRemarkTypeEnumValueDataProvider(): array + { + return [ + 'no enum' => ['varchar(25)', true, false], + 'is enum, LM missing' => ['enum(foobar,barfoo)', false, false], + 'is enum, LM exists' => ['enum(foobar,LINKMOB,barfoo)', false, true] + ]; + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Linkmobility4OXID\Setup\Actions::getRemarkTypeFieldType() + */ + public function canGetRemarkTypeFieldType() + { + /** @var Statement|MockObject $resultStatementMock */ + $resultStatementMock = $this->getMockBuilder(Statement::class) + ->disableOriginalConstructor() + ->onlyMethods(['fetchOne']) + ->getMock(); + $resultStatementMock->method('fetchOne')->willReturn('returnFixture'); + + /** @var ExpressionBuilder|MockObject $expressionBuilderMock */ + $expressionBuilderMock = $this->getMockBuilder(ExpressionBuilder::class) + ->disableOriginalConstructor() + ->getMock(); + + /** @var QueryBuilder|MockObject $queryBuilderMock */ + $queryBuilderMock = $this->getMockBuilder(QueryBuilder::class) + ->disableOriginalConstructor() + ->onlyMethods(['execute', 'expr']) + ->getMock(); + $queryBuilderMock->method('execute')->willReturn($resultStatementMock); + $queryBuilderMock->method('expr')->willReturn($expressionBuilderMock); + + /** @var QueryBuilderFactory|MockObject $queryBuilderFactoryMock */ + $queryBuilderFactoryMock = $this->getMockBuilder(QueryBuilderFactory::class) + ->disableOriginalConstructor() + ->onlyMethods(['create']) + ->getMock(); + $queryBuilderFactoryMock->method('create')->willReturn($queryBuilderMock); + + /** @var Container|MockObject $containerMock */ + $containerMock = $this->getMockBuilder(Container::class) + ->onlyMethods(['get']) + ->getMock(); + $containerMock->method('get')->willReturn($queryBuilderFactoryMock); + + /** @var Actions|MockObject $sut */ + $sut = $this->getMockBuilder(Actions::class) + ->onlyMethods(['getContainer']) + ->getMock(); + $sut->method('getContainer')->willReturn($containerMock); + + $this->assertSame( + 'returnFixture', + $this->callMethod( + $sut, + 'getRemarkTypeFieldType' + ) + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Linkmobility4OXID\Setup\Actions::addRemarkTypeEnumValue() + */ + public function acnAddRemarkTypeEnumValue() + { + /** @var Database|MockObject $databaseMock */ + $databaseMock = $this->getMockBuilder(Database::class) + ->onlyMethods(['execute', 'quoteIdentifier', 'quoteArray', 'quote']) + ->getMock(); + $databaseMock->expects($this->once())->method('execute')->willReturn(1); + $databaseMock->method('quoteIdentifier')->willReturn('foo'); + $databaseMock->method('quoteArray')->willReturn('foo'); + $databaseMock->method('quote')->willReturn('foo'); + d3GetOxidDIC()->set('d3ox.linkmobility.'.DatabaseInterface::class.'.assoc', $databaseMock); + + /** @var Actions|MockObject $sut */ + $sut = $this->getMockBuilder(Actions::class) + ->onlyMethods(['getUniqueFieldTypes']) + ->getMock(); + $sut->method('getUniqueFieldTypes')->willReturn(['foobar', 'LINKMOB', 'barfoo']); + + $this->callMethod( + $sut, + 'addRemarkTypeEnumValue' + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Linkmobility4OXID\Setup\Actions::getUniqueFieldTypes() + */ + public function canGetUniqueFieldTypes() + { + /** @var Actions|MockObject $sut */ + $sut = $this->getMockBuilder(Actions::class) + ->onlyMethods(['getRemarkTypeFieldType']) + ->getMock(); + $sut->method('getRemarkTypeFieldType')->willReturn("enum('val1', 'val2')"); + + $this->assertSame( + [ + 'val1', + 'val2', + 'LINKMOB' + ], + $this->callMethod( + $sut, + 'getUniqueFieldTypes' + ) + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Linkmobility4OXID\Setup\Actions::getContainer() + */ + public function canGetContainer() + { + /** @var Actions $sut */ + $sut = oxNew(Actions::class); + + $this->assertInstanceOf( + Container::class, + $this->callMethod( + $sut, + 'getContainer' + ) + ); + } +} \ No newline at end of file