diff --git a/Application/Controller/Admin/d3ActionWizard.php b/Application/Controller/Admin/d3ActionWizard.php index 3c4e5a4..c15ec84 100644 --- a/Application/Controller/Admin/d3ActionWizard.php +++ b/Application/Controller/Admin/d3ActionWizard.php @@ -23,6 +23,7 @@ use D3\ModCfg\Application\Model\Exception\d3_cfg_mod_exception; use D3\ModCfg\Application\Model\Exception\d3ShopCompatibilityAdapterException; use Doctrine\DBAL\DBALException; use OxidEsales\Eshop\Application\Controller\Admin\AdminDetailsController; +use OxidEsales\Eshop\Core\Config; use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; use OxidEsales\Eshop\Core\Exception\DatabaseErrorException; use OxidEsales\Eshop\Core\Exception\StandardException; @@ -79,7 +80,7 @@ class d3ActionWizard extends AdminDetailsController [ $queryString, $parameters ] = $action->getQuery(); - if (Registry::getConfig()->getConfigParam('d3datawizard_debug')) { + if ($this->d3GetConfig()->getConfigParam('d3datawizard_debug')) { throw oxNew( DebugException::class, d3database::getInstance()->getPreparedStatementQuery($queryString, $parameters) @@ -89,6 +90,14 @@ class d3ActionWizard extends AdminDetailsController $action->run(); } + /** + * @return Config + */ + public function d3GetConfig() + { + return Registry::getConfig(); + } + public function getUserMessages() { return null; diff --git a/Application/Controller/Admin/d3ExportWizard.php b/Application/Controller/Admin/d3ExportWizard.php index d38e678..049a24e 100644 --- a/Application/Controller/Admin/d3ExportWizard.php +++ b/Application/Controller/Admin/d3ExportWizard.php @@ -25,6 +25,7 @@ use D3\ModCfg\Application\Model\Exception\d3_cfg_mod_exception; use D3\ModCfg\Application\Model\Exception\d3ShopCompatibilityAdapterException; use Doctrine\DBAL\DBALException; use OxidEsales\Eshop\Application\Controller\Admin\AdminDetailsController; +use OxidEsales\Eshop\Core\Config; use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; use OxidEsales\Eshop\Core\Exception\DatabaseErrorException; use OxidEsales\Eshop\Core\Exception\StandardException; @@ -87,7 +88,7 @@ class d3ExportWizard extends AdminDetailsController [ $queryString, $parameters ] = $export->getQuery(); - if (Registry::getConfig()->getConfigParam('d3datawizard_debug')) { + if ($this->d3GetConfig()->getConfigParam('d3datawizard_debug')) { throw oxNew( DebugException::class, d3database::getInstance()->getPreparedStatementQuery($queryString, $parameters) @@ -97,6 +98,14 @@ class d3ExportWizard extends AdminDetailsController $export->run(Registry::getRequest()->getRequestEscapedParameter('format')); } + /** + * @return Config + */ + public function d3GetConfig() + { + return Registry::getConfig(); + } + public function getUserMessages() { return null; diff --git a/Application/Model/ActionBase.php b/Application/Model/ActionBase.php index 1285f34..5bb651c 100644 --- a/Application/Model/ActionBase.php +++ b/Application/Model/ActionBase.php @@ -19,6 +19,7 @@ use D3\DataWizard\Application\Model\Exceptions\InputUnvalidException; use FormManager\Inputs\Checkbox; use FormManager\Inputs\Input; use FormManager\Inputs\Radio; +use OxidEsales\Eshop\Core\Database\Adapter\DatabaseInterface; use OxidEsales\Eshop\Core\DatabaseProvider; use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; use OxidEsales\Eshop\Core\Exception\DatabaseErrorException; @@ -76,7 +77,7 @@ abstract class ActionBase implements QueryBase ); } - $affected = DatabaseProvider::getDb( DatabaseProvider::FETCH_MODE_ASSOC )->execute( $queryString, $parameters ); + $affected = $this->d3GetDb()->execute( $queryString, $parameters ); throw oxNew( Exceptions\TaskException::class, @@ -90,6 +91,15 @@ abstract class ActionBase implements QueryBase ); } + /** + * @return DatabaseInterface|null + * @throws DatabaseConnectionException + */ + public function d3GetDb() + { + return DatabaseProvider::getDb( DatabaseProvider::FETCH_MODE_ASSOC ); + } + /** * @return string */ @@ -103,15 +113,12 @@ abstract class ActionBase implements QueryBase */ public function registerFormElement(Input $input) { - switch (get_class($input)) { - case Radio::class: - case Checkbox::class: - $input->setTemplate('

{{ input }} {{ label }}

'); - $input->setAttribute('class', 'form-check-input'); - break; - default: - $input->setTemplate('

{{ label }} {{ input }}

'); - $input->setAttribute('class', 'form-control'); + if ($input instanceof Radio || $input instanceof Checkbox) { + $input->setTemplate('

{{ input }} {{ label }}

'); + $input->setAttribute('class', 'form-check-input'); + } else { + $input->setTemplate('

{{ label }} {{ input }}

'); + $input->setAttribute('class', 'form-control'); } $this->formElements[] = $input; } diff --git a/Application/Model/Configuration.php b/Application/Model/Configuration.php index 62caa45..90208c5 100644 --- a/Application/Model/Configuration.php +++ b/Application/Model/Configuration.php @@ -149,7 +149,13 @@ class Configuration */ public function getActionById($id) : ActionBase { - return $this->getAllActions()[$id]; + $allActions = $this->getAllActions(); + + if (false == $allActions[$id]) { + throw oxNew(DataWizardException::class, 'no action with id '.$id); + } + + return $allActions[$id]; } /** diff --git a/Application/Model/ExportBase.php b/Application/Model/ExportBase.php index 480bab2..02c0eab 100644 --- a/Application/Model/ExportBase.php +++ b/Application/Model/ExportBase.php @@ -69,23 +69,7 @@ abstract class ExportBase implements QueryBase } } - [ $rows, $fieldNames ] = $this->getExportData( $this->getQuery() ); - - $content = $this->renderContent($rows, $fieldNames, $format); - - /** @var $oFS d3filesystem */ - $oFS = oxNew( d3filesystem::class ); - if (is_null($path)) { - $oFS->startDirectDownload( $oFS->filterFilename( $this->getExportFileName( $format ) ), $content ); - } else { - $filePath = $oFS->trailingslashit($path).$oFS->filterFilename( $this->getExportFileName( $format ) ); - if (false === $oFS->createFile($filePath, $content,true)) { - throw oxNew(ExportFileException::class, $filePath); - } - return $filePath; - } - - return ''; + return $this->executeExport($format, $path); } /** @@ -180,7 +164,7 @@ abstract class ExportBase implements QueryBase ); } - $rows = DatabaseProvider::getDb( DatabaseProvider::FETCH_MODE_ASSOC )->getAll( $queryString, $parameters ); + $rows = $this->d3GetDb()->getAll( $queryString, $parameters ); if ( count( $rows ) <= 0 ) { throw oxNew( @@ -200,15 +184,12 @@ abstract class ExportBase implements QueryBase */ public function registerFormElement(Input $input) { - switch (get_class($input)) { - case Radio::class: - case Checkbox::class: - $input->setTemplate('

{{ input }} {{ label }}

'); - $input->setAttribute('class', 'form-check-input'); - break; - default: - $input->setTemplate('

{{ label }} {{ input }}

'); - $input->setAttribute('class', 'form-control'); + if ($input instanceof Radio || $input instanceof Checkbox) { + $input->setTemplate('

{{ input }} {{ label }}

'); + $input->setAttribute('class', 'form-check-input'); + } else { + $input->setTemplate('

{{ label }} {{ input }}

'); + $input->setAttribute('class', 'form-control'); } $this->formElements[] = $input; } @@ -228,4 +209,54 @@ abstract class ExportBase implements QueryBase { return $this->formElements; } + + /** + * @param string $format + * @param $path + * @return string + * @throws DBALException + * @throws DatabaseConnectionException + * @throws DatabaseErrorException + * @throws Exceptions\NoSuitableRendererException + * @throws StandardException + * @throws d3ShopCompatibilityAdapterException + * @throws d3_cfg_mod_exception + */ + protected function executeExport(string $format, $path): string + { + [$rows, $fieldNames] = $this->getExportData($this->getQuery()); + + $content = $this->renderContent($rows, $fieldNames, $format); + + /** @var $oFS d3filesystem */ + $oFS = $this->getFileSystem(); + if (is_null($path)) { + $oFS->startDirectDownload($oFS->filterFilename($this->getExportFileName($format)), $content); + } else { + $filePath = $oFS->trailingslashit($path) . $oFS->filterFilename($this->getExportFileName($format)); + if (false === $oFS->createFile($filePath, $content, true)) { + throw oxNew(ExportFileException::class, $filePath); + } + return $filePath; + } + + return ''; + } + + /** + * @return \OxidEsales\Eshop\Core\Database\Adapter\DatabaseInterface|null + * @throws DatabaseConnectionException + */ + protected function d3GetDb(): ?\OxidEsales\Eshop\Core\Database\Adapter\DatabaseInterface + { + return DatabaseProvider::getDb(DatabaseProvider::FETCH_MODE_ASSOC); + } + + /** + * @return d3filesystem|mixed + */ + protected function getFileSystem() + { + return oxNew(d3filesystem::class); + } } \ No newline at end of file diff --git a/Application/Model/ExportRenderer/Csv.php b/Application/Model/ExportRenderer/Csv.php index 549f6d8..38f38b6 100644 --- a/Application/Model/ExportRenderer/Csv.php +++ b/Application/Model/ExportRenderer/Csv.php @@ -19,6 +19,7 @@ use D3\DataWizard\Application\Model\Exceptions\RenderException; use League\Csv\EncloseField; use League\Csv\Exception; use League\Csv\Writer; +use OxidEsales\Eshop\Core\Config; use OxidEsales\Eshop\Core\Registry; class Csv implements RendererInterface @@ -59,13 +60,13 @@ class Csv implements RendererInterface EncloseField::addTo($csv, "\t\x1f"); - $sEncloser = Registry::getConfig()->getConfigParam('sGiCsvFieldEncloser'); + $sEncloser = $this->d3GetConfig()->getConfigParam('sGiCsvFieldEncloser'); if (false == $sEncloser) { $sEncloser = '"'; } $csv->setEnclosure($sEncloser); - $sDelimiter = Registry::getConfig()->getConfigParam('sCSVSign'); + $sDelimiter = $this->d3GetConfig()->getConfigParam('sCSVSign'); if (false == $sDelimiter) { $sDelimiter = ';'; } @@ -81,4 +82,12 @@ class Csv implements RendererInterface { return 'D3_DATAWIZARD_EXPORT_FORMAT_CSV'; } + + /** + * @return Config + */ + public function d3GetConfig() + { + return Registry::getConfig(); + } } \ No newline at end of file diff --git a/Application/Model/ExportRenderer/Pretty.php b/Application/Model/ExportRenderer/Pretty.php index 9029853..8700d2f 100644 --- a/Application/Model/ExportRenderer/Pretty.php +++ b/Application/Model/ExportRenderer/Pretty.php @@ -27,10 +27,19 @@ class Pretty implements RendererInterface */ public function getContent($rows, $fieldNames) : string { - $renderer = oxNew(ArrayToTextTable::class, $rows); + $renderer = $this->getArrayToTextTableInstance($rows); return $renderer->getTable(); } + /** + * @param $rows + * @return ArrayToTextTable + */ + public function getArrayToTextTableInstance($rows) + { + return oxNew(ArrayToTextTable::class, $rows); + } + /** * @return string */ diff --git a/composer.json b/composer.json index 188e9c2..78a9bc3 100644 --- a/composer.json +++ b/composer.json @@ -31,6 +31,9 @@ "mathieuviossat/arraytotexttable": "^1.0", "form-manager/form-manager": "^5.1 || ^6.1" }, + "require-dev": { + "oxid-esales/oxideshop-ce": "6.8 - 6.9" + }, "extra": { "oxideshop": { "blacklist-filter": [ diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..89bffeb --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,2 @@ +reports +.phpunit.result.cache diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..2e8a767 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,52 @@ +# D3 Datawizard Tests + +## Requirements + +Both unit and acceptance tests require OXID Testing Library installed. +See https://github.com/OXID-eSales/testing_library. + +### Configuration + +Please install the packages listed in the composer.json in "require-dev". Unfortunately Composer does not provide an automatic installation. + +Here is an example of Testing Library configuration file `oxideshop/test_config.yml` + +``` +# This file is auto-generated during the composer install +mandatory_parameters: + shop_path: /var/www/oxideshop/source + shop_tests_path: /var/www/oxideshop/tests + partial_module_paths: d3/datawizard +optional_parameters: + shop_url: null + shop_serial: '' + enable_varnish: false + is_subshop: false + install_shop: false + remote_server_dir: null + shop_setup_path: null + restore_shop_after_tests_suite: false + test_database_name: null + restore_after_acceptance_tests: false + restore_after_unit_tests: false + tmp_path: /tmp/oxid_test_library/ + database_restoration_class: DatabaseRestorer + activate_all_modules: false + run_tests_for_shop: false + run_tests_for_modules: true + screen_shots_path: null + screen_shots_url: null + browser_name: firefox + selenium_server_ip: 127.0.0.1 + selenium_server_port: '4444' + additional_test_paths: null +``` + +## Unit Tests + +To execute unit tests run the following: + +``` +cd /var/www/oxideshop/ +vendor/bin/runtests +``` diff --git a/tests/additional.inc.php b/tests/additional.inc.php new file mode 100755 index 0000000..84c8ac5 --- /dev/null +++ b/tests/additional.inc.php @@ -0,0 +1,39 @@ + + * @link https://www.oxidmodule.com + */ + +// Include datawizard test config +namespace D3\DataWizard\tests; + +use D3\ModCfg\Tests\additional_abstract; +use Exception; +use OxidEsales\Eshop\Core\Exception\StandardException; + +include(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'd3datawizard_config.php'); + +class additional extends additional_abstract +{ + /** + * additional constructor. + * @throws StandardException + */ + public function __construct() + { + if (D3DATAWIZARD_REQUIRE_MODCFG) { + $this->reactivateModCfg(); + } + } +} + +try { + d3GetModCfgDIC()->get(additional::class); +} catch (Exception $e) {} \ No newline at end of file diff --git a/tests/d3datawizard_config.php b/tests/d3datawizard_config.php new file mode 100755 index 0000000..e4e6aab --- /dev/null +++ b/tests/d3datawizard_config.php @@ -0,0 +1,15 @@ + + * @link https://www.oxidmodule.com + */ + +define('D3DATAWIZARD_REQUIRE_MODCFG', true); + diff --git a/tests/phpunit.xml b/tests/phpunit.xml new file mode 100644 index 0000000..6c09de6 --- /dev/null +++ b/tests/phpunit.xml @@ -0,0 +1,27 @@ + + + + ../Application + ../Modules + ../public + ../Setup + + + + + + diff --git a/tests/tools/d3TestAction.php b/tests/tools/d3TestAction.php new file mode 100644 index 0000000..746624e --- /dev/null +++ b/tests/tools/d3TestAction.php @@ -0,0 +1,18 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DataWizard\tests\unit\Application\Controller\Admin; + +use D3\DataWizard\Application\Controller\Admin\d3ActionWizard; +use D3\DataWizard\Application\Model\Configuration; +use D3\DataWizard\Application\Model\Exceptions\DebugException; +use D3\DataWizard\Application\Model\ExportRenderer\RendererBridge; +use D3\DataWizard\tests\tools\d3TestAction; +use OxidEsales\Eshop\Core\Config; +use OxidEsales\Eshop\Core\Registry; +use OxidEsales\Eshop\Core\Request; +use PHPUnit\Framework\MockObject\MockObject; +use ReflectionException; + +class d3ActionWizardTest extends d3AdminControllerTest +{ + /** @var d3ActionWizard */ + protected $_oController; + + protected $testClassName = d3ActionWizard::class; + + public function setUp() : void + { + parent::setUp(); + + $this->_oController = oxNew($this->testClassName); + } + + /** + * @covers \D3\DataWizard\Application\Controller\Admin\d3ActionWizard::getGroups() + * @test + * @throws ReflectionException + */ + public function canGetGroups() + { + $expected = ['expected' => 'array']; + /** @var Configuration|MockObject $configurationMock */ + $configurationMock = $this->getMockBuilder(Configuration::class) + ->onlyMethods(['getActionGroups']) + ->getMock(); + $configurationMock->expects($this->atLeastOnce())->method('getActionGroups')->willReturn($expected); + + $this->setValue($this->_oController, 'configuration', $configurationMock); + + $this->assertSame( + $expected, + $this->callMethod( + $this->_oController, + 'getGroups' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Controller\Admin\d3ActionWizard::getGroupTasks() + * @test + * @throws ReflectionException + * @dataProvider canGetGroupTasksDataProvider + */ + public function canGetGroupTasks($argument) + { + $expected = ['expected' => 'array']; + /** @var Configuration|MockObject $configurationMock */ + $configurationMock = $this->getMockBuilder(Configuration::class) + ->onlyMethods(['getActionsByGroup']) + ->getMock(); + $configurationMock->expects($this->atLeastOnce())->method('getActionsByGroup')->with($argument)->willReturn($expected); + + $this->setValue($this->_oController, 'configuration', $configurationMock); + + $this->assertSame( + $expected, + $this->callMethod( + $this->_oController, + 'getGroupTasks', + [$argument] + ) + ); + } + + /** + * @return array + */ + public function canGetGroupTasksDataProvider(): array + { + return [ + ['test1'], + ['test2'] + ]; + } + + /** + * @covers \D3\DataWizard\Application\Controller\Admin\d3ActionWizard::execute() + * @test + * @throws ReflectionException + * @dataProvider executePassDataProvider + */ + public function executePass($blDebug) + { + /** @var Request|MockObject $requestMock */ + $requestMock = $this->getMockBuilder(get_class(Registry::getRequest())) + ->onlyMethods(['getRequestEscapedParameter']) + ->getMock(); + $requestMock->expects($this->any())->method('getRequestEscapedParameter')->willReturnCallback([$this, 'executePassRequestCallback']); + Registry::set(Request::class, $requestMock); + + /** @var Config|MockObject $configMock */ + $configMock = $this->getMockBuilder(Config::class) + ->onlyMethods(['getConfigParam']) + ->getMock(); + $configMock->expects($this->atLeastOnce())->method('getConfigParam')->willReturnCallback( + function ($argName) use ($blDebug) { + switch ($argName) { + case 'd3datawizard_debug': + return $blDebug; + default: + return Registry::getConfig()->getConfigParam($argName); + } + } + ); + + /** @var d3ActionWizard|MockObject $controllerMock */ + $controllerMock = $this->getMockBuilder(d3ActionWizard::class) + ->onlyMethods(['d3GetConfig']) + ->getMock(); + $controllerMock->method('d3GetConfig')->willReturn($configMock); + $this->_oController = $controllerMock; + + /** @var d3TestAction|MockObject $actionMock */ + $actionMock = $this->getMockBuilder(d3TestAction::class) + ->onlyMethods([ + 'getQuery', + 'run' + ]) + ->getMock(); + $actionMock->expects($this->atLeastOnce())->method('getQuery')->willReturn(['SELECT 1', ['1']]); + $actionMock->expects($this->exactly((int) !$blDebug))->method('run')->willReturn(true); + + /** @var Configuration|MockObject $configurationMock */ + $configurationMock = $this->getMockBuilder(Configuration::class) + ->onlyMethods(['getActionById']) + ->disableOriginalConstructor() + ->getMock(); + $configurationMock->expects($this->atLeastOnce())->method('getActionById')->with('testTaskId')->willReturn($actionMock); + $this->setValue($this->_oController, 'configuration', $configurationMock); + + if ($blDebug) { + $this->expectException(DebugException::class); + } + + $this->callMethod( + $this->_oController, + 'execute' + ); + } + + public function executePassRequestCallback($varName) + { + switch ($varName) { + case 'taskid': + return 'testTaskId'; + case 'format': + return RendererBridge::FORMAT_CSV; + default: + return oxNew(Request::class)->getRequestEscapedParameter($varName); + } + } + + /** + * @return array + */ + public function executePassDataProvider(): array + { + return [ + 'no debug' => [false], + 'debug' => [true], + ]; + } +} \ No newline at end of file diff --git a/tests/unit/Application/Controller/Admin/d3AdminControllerTest.php b/tests/unit/Application/Controller/Admin/d3AdminControllerTest.php new file mode 100644 index 0000000..29aa527 --- /dev/null +++ b/tests/unit/Application/Controller/Admin/d3AdminControllerTest.php @@ -0,0 +1,262 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DataWizard\tests\unit\Application\Controller\Admin; + +use D3\DataWizard\Application\Controller\Admin\d3ActionWizard; +use D3\DataWizard\Application\Controller\Admin\d3ExportWizard; +use D3\DataWizard\Application\Model\Configuration; +use D3\DataWizard\Application\Model\Exceptions\DataWizardException; +use D3\DataWizard\Application\Model\Exceptions\DebugException; +use D3\DataWizard\tests\tools\d3TestAction; +use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase; +use Doctrine\DBAL\DBALException; +use OxidEsales\Eshop\Core\Config; +use OxidEsales\Eshop\Core\Exception\DatabaseErrorException; +use OxidEsales\Eshop\Core\Registry; +use OxidEsales\Eshop\Core\Request; +use OxidEsales\Eshop\Core\UtilsView; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use ReflectionException; + +abstract class d3AdminControllerTest extends d3ModCfgUnitTestCase +{ + /** @var d3ActionWizard|d3ExportWizard */ + protected $_oController; + + protected $testClassName; + + public function tearDown() : void + { + parent::tearDown(); + + unset($this->_oController); + } + + /** + * @covers \D3\DataWizard\Application\Controller\Admin\d3ActionWizard::__construct + * @covers \D3\DataWizard\Application\Controller\Admin\d3ExportWizard::__construct + * @test + * @throws ReflectionException + */ + public function testConstructor() + { + $this->setValue($this->_oController, 'configuration', null); + + $this->callMethod( + $this->_oController, + '__construct' + ); + + $this->assertInstanceOf( + Configuration::class, + $this->getValue( + $this->_oController, + 'configuration' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Controller\Admin\d3ActionWizard::runTask() + * @covers \D3\DataWizard\Application\Controller\Admin\d3ExportWizard::runTask() + * @test + * @throws ReflectionException + */ + public function runTaskPass() + { + /** @var d3ActionWizard|d3ExportWizard|MockObject $controllerMock */ + $controllerMock = $this->getMockBuilder($this->testClassName) + ->onlyMethods(['execute']) + ->getMock(); + $controllerMock->expects($this->once())->method('execute')->willReturn(true); + + $this->_oController = $controllerMock; + + $this->callMethod( + $this->_oController, + 'runTask' + ); + } + + /** + * @covers \D3\DataWizard\Application\Controller\Admin\d3ActionWizard::runTask() + * @covers \D3\DataWizard\Application\Controller\Admin\d3ExportWizard::runTask() + * @test + * @param $exceptionClass + * @throws ReflectionException + * @dataProvider runTaskFailedDataProvider + */ + public function runTaskFailed($exceptionClass) + { + /** @var DataWizardException|DBALException|DatabaseErrorException|MockObject $exceptionMock */ + $exceptionMock = $this->getMockBuilder($exceptionClass) + ->disableOriginalConstructor() + ->getMock(); + $this->setValue($exceptionMock, 'message', 'exc_msg'); + + /** @var d3ActionWizard|d3ExportWizard|MockObject $controllerMock */ + $controllerMock = $this->getMockBuilder($this->testClassName) + ->onlyMethods(['execute']) + ->getMock(); + $controllerMock->expects($this->once())->method('execute')->willThrowException($exceptionMock); + + /** @var LoggerInterface|MockObject $loggerMock */ + $loggerMock = $this->getMockBuilder(get_class(Registry::getLogger())) + ->onlyMethods(['error']) + ->disableOriginalConstructor() + ->getMock(); + $loggerMock->expects($this->atLeastOnce())->method('error')->with('exc_msg')->willReturn(true); + Registry::set('logger', $loggerMock); + + /** @var UtilsView|MockObject $utilsViewMock */ + $utilsViewMock = $this->getMockBuilder(get_class(Registry::getUtilsView())) + ->onlyMethods(['addErrorToDisplay']) + ->getMock(); + $utilsViewMock->expects($this->atLeastOnce())->method('addErrorToDisplay')->with($exceptionMock)->willReturn(true); + Registry::set(UtilsView::class, $utilsViewMock); + + $this->_oController = $controllerMock; + + $this->callMethod( + $this->_oController, + 'runTask' + ); + } + + /** + * @return \string[][] + */ + public function runTaskFailedDataProvider(): array + { + return [ + [DataWizardException::class], + [DBALException::class], + [DatabaseErrorException::class], + ]; + } + + /** + * @covers \D3\DataWizard\Application\Controller\Admin\d3ActionWizard::execute() + * @covers \D3\DataWizard\Application\Controller\Admin\d3ExportWizard::execute() + * @test + * @throws ReflectionException + * @dataProvider executePassDataProvider + */ + public function executePass($blDebug) + { + /** @var Request|MockObject $requestMock */ + $requestMock = $this->getMockBuilder(get_class(Registry::getRequest())) + ->onlyMethods(['getRequestEscapedParameter']) + ->getMock(); + $requestMock->expects($this->atLeastOnce())->method('getRequestEscapedParameter')->with('taskid')->willReturn('testTaskId'); + Registry::set(Request::class, $requestMock); + + /** @var d3TestAction|MockObject $actionMock */ + $actionMock = $this->getMockBuilder(d3TestAction::class) + ->onlyMethods([ + 'getQuery', + 'run' + ]) + ->getMock(); + $actionMock->expects($this->atLeastOnce())->method('getQuery')->willReturn(['SELECT 1', ['1']]); + $actionMock->expects($this->exactly((int) !$blDebug))->method('run')->willReturn(true); + + /** @var Configuration|MockObject $configurationMock */ + $configurationMock = $this->getMockBuilder(Configuration::class) + ->onlyMethods(['getActionById']) + ->disableOriginalConstructor() + ->getMock(); + $configurationMock->expects($this->atLeastOnce())->method('getActionById')->with('testTaskId')->willReturn($actionMock); + $this->setValue($this->_oController, 'configuration', $configurationMock); + + /** @var Config|MockObject $configMock */ + $configMock = $this->getMockBuilder(Config::class) + ->onlyMethods(['getConfigParam']) + ->getMock(); + $configMock->expects($this->atLeastOnce())->method('getConfigParam')->willReturn($blDebug); + Registry::set(Config::class, $configMock); + + if ($blDebug) { + $this->expectException(DebugException::class); + } + + $this->callMethod( + $this->_oController, + 'execute' + ); + } + + /** + * @return array + */ + public function executePassDataProvider(): array + { + return [ + 'no debug' => [false], + 'debug' => [true], + ]; + } + + /** + * @covers \D3\DataWizard\Application\Controller\Admin\d3ActionWizard::getUserMessages() + * @covers \D3\DataWizard\Application\Controller\Admin\d3ExportWizard::getUserMessages() + * @test + * @throws ReflectionException + */ + public function canGetUserMessages() + { + $this->assertNull( + $this->callMethod( + $this->_oController, + 'getUserMessages' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Controller\Admin\d3ActionWizard::getHelpURL() + * @covers \D3\DataWizard\Application\Controller\Admin\d3ExportWizard::getHelpURL() + * @test + * @throws ReflectionException + */ + public function canGetHelpUrl() + { + $this->assertNull( + $this->callMethod( + $this->_oController, + 'getHelpURL' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Controller\Admin\d3ExportWizard::d3GetConfig + * @covers \D3\DataWizard\Application\Controller\Admin\d3ActionWizard::d3GetConfig + * @test + * @throws ReflectionException + */ + public function canGetConfig() + { + $this->assertInstanceOf( + Config::class, + $this->callMethod( + $this->_oController, + 'd3GetConfig' + ) + ); + } +} \ No newline at end of file diff --git a/tests/unit/Application/Controller/Admin/d3ExportWizardTest.php b/tests/unit/Application/Controller/Admin/d3ExportWizardTest.php new file mode 100644 index 0000000..e5cccf3 --- /dev/null +++ b/tests/unit/Application/Controller/Admin/d3ExportWizardTest.php @@ -0,0 +1,195 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DataWizard\tests\unit\Application\Controller\Admin; + +use D3\DataWizard\Application\Controller\Admin\d3ExportWizard; +use D3\DataWizard\Application\Model\Configuration; +use D3\DataWizard\Application\Model\Exceptions\DebugException; +use D3\DataWizard\Application\Model\ExportRenderer\RendererBridge; +use D3\DataWizard\tests\tools\d3TestAction; +use D3\DataWizard\tests\tools\d3TestExport; +use OxidEsales\Eshop\Core\Config; +use OxidEsales\Eshop\Core\Registry; +use OxidEsales\Eshop\Core\Request; +use PHPUnit\Framework\MockObject\MockObject; +use ReflectionException; + +class d3ExportWizardTest extends d3AdminControllerTest +{ + /** @var d3ExportWizard */ + protected $_oController; + + protected $testClassName = d3ExportWizard::class; + + public function setUp() : void + { + parent::setUp(); + + $this->_oController = oxNew($this->testClassName); + } + + /** + * @covers \D3\DataWizard\Application\Controller\Admin\d3ExportWizard::getGroups() + * @test + * @throws ReflectionException + */ + public function canGetGroups() + { + $expected = ['expected' => 'array']; + /** @var Configuration|MockObject $configurationMock */ + $configurationMock = $this->getMockBuilder(Configuration::class) + ->onlyMethods(['getExportGroups']) + ->getMock(); + $configurationMock->expects($this->atLeastOnce())->method('getExportGroups')->willReturn($expected); + + $this->setValue($this->_oController, 'configuration', $configurationMock); + + $this->assertSame( + $expected, + $this->callMethod( + $this->_oController, + 'getGroups' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Controller\Admin\d3ExportWizard::getGroupTasks() + * @test + * @throws ReflectionException + * @dataProvider canGetGroupTasksDataProvider + */ + public function canGetGroupTasks($argument) + { + $expected = ['expected' => 'array']; + /** @var Configuration|MockObject $configurationMock */ + $configurationMock = $this->getMockBuilder(Configuration::class) + ->onlyMethods(['getExportsByGroup']) + ->getMock(); + $configurationMock->expects($this->atLeastOnce())->method('getExportsByGroup')->with($argument)->willReturn($expected); + + $this->setValue($this->_oController, 'configuration', $configurationMock); + + $this->assertSame( + $expected, + $this->callMethod( + $this->_oController, + 'getGroupTasks', + [$argument] + ) + ); + } + + /** + * @return array + */ + public function canGetGroupTasksDataProvider(): array + { + return [ + ['test1'], + ['test2'] + ]; + } + + /** + * @covers \D3\DataWizard\Application\Controller\Admin\d3ExportWizard::execute() + * @test + * @throws ReflectionException + * @dataProvider executePassDataProvider + */ + public function executePass($blDebug) + { + /** @var Request|MockObject $requestMock */ + $requestMock = $this->getMockBuilder(get_class(Registry::getRequest())) + ->onlyMethods(['getRequestEscapedParameter']) + ->getMock(); + $requestMock->expects($this->any())->method('getRequestEscapedParameter')->willReturnCallback([$this, 'executePassRequestCallback']); + //OnConsecutiveCalls('testTaskId', 'CSV'); + Registry::set(Request::class, $requestMock); + + /** @var Config|MockObject $configMock */ + $configMock = $this->getMockBuilder(Config::class) + ->onlyMethods(['getConfigParam']) + ->getMock(); + $configMock->expects($this->atLeastOnce())->method('getConfigParam')->willReturnCallback( + function ($argName) use ($blDebug) { + switch ($argName) { + case 'd3datawizard_debug': + return $blDebug; + default: + return Registry::getConfig()->getConfigParam($argName); + } + } + ); + + /** @var d3ExportWizard|MockObject $controllerMock */ + $controllerMock = $this->getMockBuilder(d3ExportWizard::class) + ->onlyMethods(['d3GetConfig']) + ->getMock(); + $controllerMock->method('d3GetConfig')->willReturn($configMock); + $this->_oController = $controllerMock; + + /** @var d3TestAction|MockObject $exportMock */ + $exportMock = $this->getMockBuilder(d3TestExport::class) + ->onlyMethods([ + 'getQuery', + 'run' + ]) + ->getMock(); + $exportMock->expects($this->atLeastOnce())->method('getQuery')->willReturn(['SELECT 1', ['1']]); + $exportMock->expects($this->exactly((int) !$blDebug))->method('run')->willReturn(''); + + /** @var Configuration|MockObject $configurationMock */ + $configurationMock = $this->getMockBuilder(Configuration::class) + ->onlyMethods(['getExportById']) + ->disableOriginalConstructor() + ->getMock(); + $configurationMock->expects($this->atLeastOnce())->method('getExportById')->with('testTaskId')->willReturn($exportMock); + $this->setValue($this->_oController, 'configuration', $configurationMock); + + if ($blDebug) { + $this->expectException(DebugException::class); + } + + $this->callMethod( + $this->_oController, + 'execute' + ); + } + + public function executePassRequestCallback($varName) + { + switch ($varName) { + case 'taskid': + return 'testTaskId'; + case 'format': + return RendererBridge::FORMAT_CSV; + default: + return oxNew(Request::class)->getRequestEscapedParameter($varName); + } + } + + /** + * @return array + */ + public function executePassDataProvider(): array + { + return [ + 'no debug' => [false], + 'debug' => [true], + ]; + } +} \ No newline at end of file diff --git a/tests/unit/Application/Model/ActionBaseTest.php b/tests/unit/Application/Model/ActionBaseTest.php new file mode 100644 index 0000000..cedc94b --- /dev/null +++ b/tests/unit/Application/Model/ActionBaseTest.php @@ -0,0 +1,316 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DataWizard\tests\unit\Application\Model; + +use D3\DataWizard\Application\Model\Exceptions\TaskException; +use D3\DataWizard\tests\tools\d3TestAction; +use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase; +use FormManager\Inputs\Hidden; +use FormManager\Inputs\Number; +use FormManager\Inputs\Radio; +use OxidEsales\Eshop\Core\Database\Adapter\Doctrine\Database; +use OxidEsales\Eshop\Core\Exception\StandardException; +use PHPUnit\Framework\MockObject\MockObject; +use ReflectionException; + +class ActionBaseTest extends d3ModCfgUnitTestCase +{ + /** @var d3TestAction */ + protected $_oModel; + + public function setUp() : void + { + parent::setUp(); + + $this->_oModel = oxNew(d3TestAction::class); + } + + public function tearDown() : void + { + parent::tearDown(); + + unset($this->_oModel); + } + + /** + * @covers \D3\DataWizard\Application\Model\ActionBase::getDescription + * @test + * @throws ReflectionException + */ + public function canGetDescription() + { + $this->assertIsString( + $this->callMethod( + $this->_oModel, + 'getDescription' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ActionBase::getButtonText + * @test + * @throws ReflectionException + */ + public function canGetButtonText() + { + $this->assertIsString( + $this->callMethod( + $this->_oModel, + 'getButtonText' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ActionBase::hasFormElements + * @test + * @throws ReflectionException + * @dataProvider canGetHasFormElementsDataProvider + */ + public function canGetHasFormElements($formElements, $expected) + { + $this->setValue($this->_oModel, 'formElements', $formElements); + + $this->assertSame( + $expected, + $this->callMethod( + $this->_oModel, + 'hasFormElements' + ) + ); + } + + public function canGetHasFormElementsDataProvider() + { + return [ + 'hasFormElements' => [['abc', 'def'], true], + 'hasNoFormElements' => [[], false], + ]; + } + + /** + * @covers \D3\DataWizard\Application\Model\ActionBase::getFormElements + * @test + * @throws ReflectionException + * @dataProvider canGetHasFormElementsDataProvider + */ + public function canGetFormElements($formElements) + { + $this->setValue($this->_oModel, 'formElements', $formElements); + + $this->assertSame( + $formElements, + $this->callMethod( + $this->_oModel, + 'getFormElements' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ActionBase::registerFormElement + * @test + * @throws ReflectionException + * @dataProvider canRegisterFormElementDataProvider + */ + public function canRegisterFormElement($inputClass) + { + $oldCount = count($this->getValue($this->_oModel, 'formElements')); + + /** @var Radio|MockObject $inputMock */ + $inputMock = $this->getMockBuilder($inputClass) + ->onlyMethods([ + 'setTemplate', + 'setAttribute' + ]) + ->getMock(); + $inputMock->expects($this->atLeastOnce())->method('setTemplate'); + $inputMock->expects($this->atLeastOnce())->method('setAttribute'); + + $this->callMethod( + $this->_oModel, + 'registerFormElement', + [$inputMock] + ); + + $newCount = count($this->getValue($this->_oModel, 'formElements')); + + $this->assertGreaterThan($oldCount, $newCount); + } + + /** + * @return \string[][] + */ + public function canRegisterFormElementDataProvider(): array + { + return [ + 'Radio' => [Radio::class], + 'Checkbox' => [Radio::class], + 'Hidden' => [Hidden::class] + ]; + } + + /** + * @covers \D3\DataWizard\Application\Model\ActionBase::run + * @test + * @throws ReflectionException + */ + public function canRunWithoutFormElements() + { + $modelMock = $this->getMockBuilder(d3TestAction::class) + ->onlyMethods([ + 'hasFormElements', + 'executeAction', + 'getQuery' + ]) + ->getMock(); + $modelMock->expects($this->atLeastOnce())->method('hasFormElements')->willReturn(false); + $modelMock->expects($this->atLeastOnce())->method('executeAction')->willReturn(1); + $modelMock->expects($this->atLeastOnce())->method('getQuery')->willReturn([]); + $this->_oModel = $modelMock; + + $this->callMethod( + $this->_oModel, + 'run' + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ActionBase::run + * @test + * @throws ReflectionException + * @dataProvider canRunWithFormElementsDataProvider + */ + public function canRunWithFormElements($elements, $blThrowException) + { + $expectedException = oxNew(StandardException::class); + + $modelMock = $this->getMockBuilder(d3TestAction::class) + ->onlyMethods([ + 'hasFormElements', + 'executeAction', + 'getQuery', + 'getFormElements' + ]) + ->getMock(); + $modelMock->expects($this->atLeastOnce())->method('hasFormElements')->willReturn(true); + $modelMock->expects($this->exactly((int) !$blThrowException))->method('executeAction')->willReturn(1); + $modelMock->expects($this->exactly((int) !$blThrowException))->method('getQuery')->willReturn([]); + $modelMock->expects($this->atLeastOnce())->method('getFormElements')->willReturn($elements); + $this->_oModel = $modelMock; + + if ($blThrowException) { + $this->expectException(get_class($expectedException)); + } + + $this->callMethod( + $this->_oModel, + 'run' + ); + } + + /** + * @return array[] + */ + public function canRunWithFormElementsDataProvider(): array + { + /** @var Radio|MockObject $validMock */ + $validMock = $this->getMockBuilder(Radio::class) + ->onlyMethods(['isValid']) + ->getMock(); + $validMock->expects($this->atLeastOnce())->method('isValid')->willReturn(true); + + $invalidField = new Number(null, [ + 'required' => true, + 'min' => 1, + 'max' => 10, + 'step' => 5, + ]); + $invalidField + ->setValue(20) + ->setErrorMessages(['errorMsgs']); + + return [ + 'validElements' => [[$validMock, $validMock], false], + 'invalidElements' => [[$validMock, $invalidField], true] + ]; + } + + /** + * @covers \D3\DataWizard\Application\Model\ActionBase::executeAction + * @test + * @throws ReflectionException + * @dataProvider canExecuteActionDataProvider + */ + public function canExecuteAction($query, $throwsException) + { + /** @var Database|MockObject $dbMock */ + $dbMock = $this->getMockBuilder(Database::class) + ->onlyMethods(['execute']) + ->getMock(); + $dbMock->expects($this->exactly((int) !$throwsException))->method('execute')->willReturn(true); + + /** @var d3TestAction|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(d3TestAction::class) + ->onlyMethods(['d3GetDb']) + ->getMock(); + $modelMock->expects($this->exactly((int) !$throwsException))->method('d3GetDb')->willReturn($dbMock); + + $this->_oModel = $modelMock; + + try { + $this->callMethod( + $this->_oModel, + 'executeAction', + [[$query, ['parameters']]] + ); + } catch (TaskException $e) { + if ($throwsException) { + $this->assertStringContainsString('ACTIONSELECT', $e->getMessage()); + } else { + $this->assertStringContainsString('ACTIONRESULT', $e->getMessage()); + } + } + } + + /** + * @return array[] + */ + public function canExecuteActionDataProvider(): array + { + return [ + 'Select throws exception' => ['SELECT 1', true], + 'Update dont throws exception' => ['UPDATE 1', false], + ]; + } + + /** + * @covers \D3\DataWizard\Application\Model\ActionBase::d3GetDb + * @test + * @throws ReflectionException + */ + public function canGetDb() + { + $this->assertInstanceOf( + Database::class, + $this->callMethod( + $this->_oModel, + 'd3GetDb' + ) + ); + } +} \ No newline at end of file diff --git a/tests/unit/Application/Model/ConfigurationTest.php b/tests/unit/Application/Model/ConfigurationTest.php new file mode 100644 index 0000000..1d8d163 --- /dev/null +++ b/tests/unit/Application/Model/ConfigurationTest.php @@ -0,0 +1,454 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DataWizard\tests\unit\Application\Model; + +use D3\DataWizard\Application\Model\Configuration; +use D3\DataWizard\Application\Model\Exceptions\DataWizardException; +use D3\DataWizard\tests\tools\d3TestAction; +use D3\DataWizard\tests\tools\d3TestExport; +use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase; +use PHPUnit\Framework\MockObject\MockObject; + +class ConfigurationTest extends d3ModCfgUnitTestCase +{ + /** @var Configuration */ + protected $_oModel; + + public function setUp() : void + { + parent::setUp(); + + $this->_oModel = oxNew(Configuration::class); + } + + public function tearDown() : void + { + parent::tearDown(); + + unset($this->_oModel); + } + + /** + * @covers \D3\DataWizard\Application\Model\Configuration::__construct + * @test + * @throws \ReflectionException + */ + public function canConstruct() + { + /** @var Configuration|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(Configuration::class) + ->disableOriginalConstructor() + ->onlyMethods(['configure']) + ->getMock(); + $modelMock->expects($this->once())->method('configure'); + $this->_oModel = $modelMock; + + $this->callMethod( + $this->_oModel, + '__construct' + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\Configuration::configure() + * @test + * @throws \ReflectionException + */ + public function canConfigure() + { + $this->assertEmpty( + $this->callMethod( + $this->_oModel, + 'configure' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\Configuration::registerAction + * @test + * @throws \ReflectionException + */ + public function canRegisterAction() + { + $action = oxNew(d3TestAction::class); + + $oldCount = count($this->getValue( + $this->_oModel, + 'actions' + )); + + $this->callMethod( + $this->_oModel, + 'registerAction', + ['testGroup', $action] + ); + + $actions = $this->getValue( + $this->_oModel, + 'actions' + ); + + $newCount = count($actions); + + $flattedActions = $this->array_flatten($actions); + + $this->assertGreaterThan($oldCount, $newCount); + $this->assertContains($action, $flattedActions); + } + + /** + * @covers \D3\DataWizard\Application\Model\Configuration::registerExport + * @test + * @throws \ReflectionException + */ + public function canRegisterExport() + { + $export = oxNew(d3TestExport::class); + + $oldCount = count($this->getValue( + $this->_oModel, + 'exports' + )); + + $this->callMethod( + $this->_oModel, + 'registerExport', + ['testGroup', $export] + ); + + $exports = $this->getValue( + $this->_oModel, + 'exports' + ); + + $newCount = count($exports); + + $flattedExports = $this->array_flatten($exports); + + $this->assertGreaterThan($oldCount, $newCount); + $this->assertContains($export, $flattedExports); + } + + /** + * @param $array + * @return array|false + */ + public function array_flatten($array) { + if (!is_array($array)) { + return false; + } + $result = array(); + foreach ($array as $key => $value) { + if (is_array($value)) { + $result = array_merge($result, $this->array_flatten($value)); + } + else { + $result[$key] = $value; + } + } + return $result; + } + + /** + * @covers \D3\DataWizard\Application\Model\Configuration::getGroupedActions() + * @test + * @throws \ReflectionException + */ + public function canGetGroupedActions() + { + $actionList = ['abc', 'def']; + + $this->setValue( + $this->_oModel, + 'actions', + $actionList + ); + + $this->assertSame( + $actionList, + $this->callMethod( + $this->_oModel, + 'getGroupedActions' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\Configuration::getGroupedExports() + * @test + * @throws \ReflectionException + */ + public function canGetGroupedExports() + { + $exportList = ['abc', 'def']; + + $this->setValue( + $this->_oModel, + 'exports', + $exportList + ); + + $this->assertSame( + $exportList, + $this->callMethod( + $this->_oModel, + 'getGroupedExports' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\Configuration::getActionGroups() + * @test + * @throws \ReflectionException + */ + public function canGetActionGroups() + { + $actionList = ['abc' => '123', 'def' => '456']; + + $this->setValue( + $this->_oModel, + 'actions', + $actionList + ); + + $this->assertSame( + ['abc', 'def'], + $this->callMethod( + $this->_oModel, + 'getActionGroups' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\Configuration::getExportGroups() + * @test + * @throws \ReflectionException + */ + public function canGetExportGroups() + { + $exportList = ['abc' => '123', 'def' => '456']; + + $this->setValue( + $this->_oModel, + 'exports', + $exportList + ); + + $this->assertSame( + ['abc', 'def'], + $this->callMethod( + $this->_oModel, + 'getExportGroups' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\Configuration::getActionsByGroup() + * @test + * @throws \ReflectionException + */ + public function canGetActionsByGroup() + { + $actionList = ['abc' => '123', 'def' => '456']; + + $this->setValue( + $this->_oModel, + 'actions', + $actionList + ); + + $this->assertSame( + '456', + $this->callMethod( + $this->_oModel, + 'getActionsByGroup', + ['def'] + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\Configuration::getExportsByGroup() + * @test + * @throws \ReflectionException + */ + public function canGetExportsByGroup() + { + $exportList = ['abc' => '123', 'def' => '456']; + + $this->setValue( + $this->_oModel, + 'exports', + $exportList + ); + + $this->assertSame( + '456', + $this->callMethod( + $this->_oModel, + 'getExportsByGroup', + ['def'] + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\Configuration::getAllActions() + * @test + * @throws \ReflectionException + */ + public function canGetAllActions() + { + /** @var Configuration|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(Configuration::class) + ->onlyMethods([ + 'getActionGroups', + 'getActionsByGroup' + ]) + ->getMock(); + $modelMock->expects($this->once())->method('getActionGroups')->willReturn(['123', '456']); + $modelMock->expects($this->exactly(2))->method('getActionsByGroup')->willReturnOnConsecutiveCalls( + ['123', '456'], + ['789', '012'] + ); + + $this->_oModel = $modelMock; + + $this->assertSame( + ['123','456', '789', '012'], + $this->callMethod( + $this->_oModel, + 'getAllActions' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\Configuration::getAllExports() + * @test + * @throws \ReflectionException + */ + public function canGetAllExports() + { + /** @var Configuration|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(Configuration::class) + ->onlyMethods([ + 'getExportGroups', + 'getExportsByGroup' + ]) + ->getMock(); + $modelMock->expects($this->once())->method('getExportGroups')->willReturn(['123', '456']); + $modelMock->expects($this->exactly(2))->method('getExportsByGroup')->willReturnOnConsecutiveCalls( + ['123', '456'], + ['789', '012'] + ); + + $this->_oModel = $modelMock; + + $this->assertSame( + ['123', '456', '789', '012'], + $this->callMethod( + $this->_oModel, + 'getAllExports' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\Configuration::getActionById() + * @test + * @throws \ReflectionException + * @dataProvider canGetActionByIdDataProvider + */ + public function canGetActionById($id, $throwException) + { + $expected = oxNew(d3TestAction::class); + + /** @var Configuration|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(Configuration::class) + ->onlyMethods(['getAllActions']) + ->getMock(); + $modelMock->expects($this->once())->method('getAllActions')->willReturn(['123' => oxNew(d3TestAction::class), '456' => $expected]); + $this->_oModel = $modelMock; + + if ($throwException) { + $this->expectException(DataWizardException::class); + } + + $return = $this->callMethod( + $this->_oModel, + 'getActionById', + [$id] + ); + + if (!$throwException) { + $this->assertSame( + $expected, + $return + ); + } + } + + /** + * @covers \D3\DataWizard\Application\Model\Configuration::getExportById() + * @test + * @throws \ReflectionException + * @dataProvider canGetActionByIdDataProvider + */ + public function canGetExportById($id, $throwException) + { + $expected = oxNew(d3TestExport::class); + + /** @var Configuration|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(Configuration::class) + ->onlyMethods(['getAllExports']) + ->getMock(); + $modelMock->expects($this->once())->method('getAllExports')->willReturn(['123' => oxNew(d3TestExport::class), '456' => $expected]); + $this->_oModel = $modelMock; + + if ($throwException) { + $this->expectException(DataWizardException::class); + } + + $return = $this->callMethod( + $this->_oModel, + 'getExportById', + [$id] + ); + + if (!$throwException) { + $this->assertSame( + $expected, + $return + ); + } + } + + /** + * @return array[] + */ + public function canGetActionByIdDataProvider(): array + { + return [ + 'unset id' => ['987', true], + 'set id' => ['456', false], + ]; + } +} \ No newline at end of file diff --git a/tests/unit/Application/Model/Exceptions/DebugExceptionTest.php b/tests/unit/Application/Model/Exceptions/DebugExceptionTest.php new file mode 100644 index 0000000..779db2f --- /dev/null +++ b/tests/unit/Application/Model/Exceptions/DebugExceptionTest.php @@ -0,0 +1,76 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DataWizard\tests\unit\Application\Model\Exceptions; + +use D3\DataWizard\Application\Model\Exceptions\DebugException; +use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase; +use PHPUnit\Framework\MockObject\MockObject; + +class DebugExceptionTest extends d3ModCfgUnitTestCase +{ + /** @var DebugException */ + protected $_oModel; + + /** + * @covers \D3\DataWizard\Application\Model\Exceptions\DebugException::__construct + * @test + * @throws \ReflectionException + */ + public function canConstruct() + { + $code = '500'; + + $exception = oxNew(\Exception::class); + + /** @var DebugException|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(DebugException::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->_oModel = $modelMock; + + $this->callMethod( + $this->_oModel, + '__construct', + ['testMessage', $code, $exception] + ); + + $this->assertStringContainsString( + 'DEBUG', + $this->callMethod( + $this->_oModel, + 'getMessage' + ) + ); + + $this->assertEquals( + $code, + $this->callMethod( + $this->_oModel, + 'getCode' + ) + ); + + $this->assertSame( + $exception, + $this->callMethod( + $this->_oModel, + 'getPrevious' + ) + ); + } + +} \ No newline at end of file diff --git a/tests/unit/Application/Model/Exceptions/ExportFileExceptionTest.php b/tests/unit/Application/Model/Exceptions/ExportFileExceptionTest.php new file mode 100644 index 0000000..be6d83e --- /dev/null +++ b/tests/unit/Application/Model/Exceptions/ExportFileExceptionTest.php @@ -0,0 +1,76 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DataWizard\tests\unit\Application\Model\Exceptions; + +use D3\DataWizard\Application\Model\Exceptions\ExportFileException; +use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase; +use PHPUnit\Framework\MockObject\MockObject; + +class ExportFileExceptionTest extends d3ModCfgUnitTestCase +{ + /** @var ExportFileException */ + protected $_oModel; + + /** + * @covers \D3\DataWizard\Application\Model\Exceptions\ExportFileException::__construct + * @test + * @throws \ReflectionException + */ + public function canConstruct() + { + $code = '500'; + + $exception = oxNew(\Exception::class); + + /** @var ExportFileException|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(ExportFileException::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->_oModel = $modelMock; + + $this->callMethod( + $this->_oModel, + '__construct', + ['testMessage', $code, $exception] + ); + + $this->assertStringContainsString( + 'EXPORTFILEERROR', + $this->callMethod( + $this->_oModel, + 'getMessage' + ) + ); + + $this->assertEquals( + $code, + $this->callMethod( + $this->_oModel, + 'getCode' + ) + ); + + $this->assertSame( + $exception, + $this->callMethod( + $this->_oModel, + 'getPrevious' + ) + ); + } + +} \ No newline at end of file diff --git a/tests/unit/Application/Model/Exceptions/InputUnvalidExceptionTest.php b/tests/unit/Application/Model/Exceptions/InputUnvalidExceptionTest.php new file mode 100644 index 0000000..2dfe869 --- /dev/null +++ b/tests/unit/Application/Model/Exceptions/InputUnvalidExceptionTest.php @@ -0,0 +1,96 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DataWizard\tests\unit\Application\Model\Exceptions; + +use D3\DataWizard\Application\Model\Exceptions\InputUnvalidException; +use D3\DataWizard\Application\Model\ExportBase; +use D3\DataWizard\tests\tools\d3TestExport; +use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase; +use FormManager\Inputs\Number; +use PHPUnit\Framework\MockObject\MockObject; + +class InputUnvalidExceptionTest extends d3ModCfgUnitTestCase +{ + /** @var InputUnvalidException */ + protected $_oModel; + + /** + * @covers \D3\DataWizard\Application\Model\Exceptions\InputUnvalidException::__construct + * @test + * @throws \ReflectionException + */ + public function canConstruct() + { + $code = '500'; + + $exception = oxNew(\Exception::class); + + /** @var Number $invalidField */ + $invalidField = new Number(null, [ + 'required' => true, + 'min' => 1, + 'max' => 10, + 'step' => 5, + ]); + $invalidField + ->setValue(20) + ->setErrorMessages(['errorMsgs']); + + /** @var ExportBase|MockObject $taskMock */ + $taskMock = $this->getMockBuilder(d3TestExport::class) + ->onlyMethods(['getTitle']) + ->getMock(); + $taskMock->expects($this->atLeastOnce())->method('getTitle')->willReturn('testTitle'); + + /** @var InputUnvalidException|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(InputUnvalidException::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->_oModel = $modelMock; + + $this->callMethod( + $this->_oModel, + '__construct', + [$taskMock, $invalidField, $code, $exception] + ); + + $this->assertRegExp( + '/^testTitle\s-\s*->\s.*\sless\s/m', + $this->callMethod( + $this->_oModel, + 'getMessage' + ) + ); + + $this->assertEquals( + $code, + $this->callMethod( + $this->_oModel, + 'getCode' + ) + ); + + $this->assertSame( + $exception, + $this->callMethod( + $this->_oModel, + 'getPrevious' + ) + ); + } + +} \ No newline at end of file diff --git a/tests/unit/Application/Model/Exceptions/NoSuitableRendererExceptionTest.php b/tests/unit/Application/Model/Exceptions/NoSuitableRendererExceptionTest.php new file mode 100644 index 0000000..49e34b0 --- /dev/null +++ b/tests/unit/Application/Model/Exceptions/NoSuitableRendererExceptionTest.php @@ -0,0 +1,76 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DataWizard\tests\unit\Application\Model\Exceptions; + +use D3\DataWizard\Application\Model\Exceptions\NoSuitableRendererException; +use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase; +use PHPUnit\Framework\MockObject\MockObject; + +class NoSuitableRendererExceptionTest extends d3ModCfgUnitTestCase +{ + /** @var NoSuitableRendererException */ + protected $_oModel; + + /** + * @covers \D3\DataWizard\Application\Model\Exceptions\NoSuitableRendererException::__construct + * @test + * @throws \ReflectionException + */ + public function canConstruct() + { + $code = '500'; + + $exception = oxNew(\Exception::class); + + /** @var NoSuitableRendererException|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(NoSuitableRendererException::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->_oModel = $modelMock; + + $this->callMethod( + $this->_oModel, + '__construct', + ['testMessage', $code, $exception] + ); + + $this->assertStringContainsString( + 'NOSUITABLERENDERER', + $this->callMethod( + $this->_oModel, + 'getMessage' + ) + ); + + $this->assertEquals( + $code, + $this->callMethod( + $this->_oModel, + 'getCode' + ) + ); + + $this->assertSame( + $exception, + $this->callMethod( + $this->_oModel, + 'getPrevious' + ) + ); + } + +} \ No newline at end of file diff --git a/tests/unit/Application/Model/Exceptions/TaskExceptionTest.php b/tests/unit/Application/Model/Exceptions/TaskExceptionTest.php new file mode 100644 index 0000000..f9d5cd4 --- /dev/null +++ b/tests/unit/Application/Model/Exceptions/TaskExceptionTest.php @@ -0,0 +1,84 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DataWizard\tests\unit\Application\Model\Exceptions; + +use D3\DataWizard\Application\Model\Exceptions\TaskException; +use D3\DataWizard\Application\Model\ExportBase; +use D3\DataWizard\tests\tools\d3TestExport; +use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase; +use PHPUnit\Framework\MockObject\MockObject; + +class TaskExceptionTest extends d3ModCfgUnitTestCase +{ + /** @var TaskException */ + protected $_oModel; + + /** + * @covers \D3\DataWizard\Application\Model\Exceptions\TaskException::__construct + * @test + * @throws \ReflectionException + */ + public function canConstruct() + { + $code = '500'; + + $exception = oxNew(\Exception::class); + + /** @var ExportBase|MockObject $taskMock */ + $taskMock = $this->getMockBuilder(d3TestExport::class) + ->onlyMethods(['getTitle']) + ->getMock(); + $taskMock->expects($this->atLeastOnce())->method('getTitle')->willReturn('testTitle'); + + /** @var TaskException|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(TaskException::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->_oModel = $modelMock; + + $this->callMethod( + $this->_oModel, + '__construct', + [$taskMock, 'testMessage', $code, $exception] + ); + + $this->assertSame( + 'testTitle - testMessage', + $this->callMethod( + $this->_oModel, + 'getMessage' + ) + ); + + $this->assertEquals( + $code, + $this->callMethod( + $this->_oModel, + 'getCode' + ) + ); + + $this->assertSame( + $exception, + $this->callMethod( + $this->_oModel, + 'getPrevious' + ) + ); + } + +} \ No newline at end of file diff --git a/tests/unit/Application/Model/ExportBaseTest.php b/tests/unit/Application/Model/ExportBaseTest.php new file mode 100644 index 0000000..e486956 --- /dev/null +++ b/tests/unit/Application/Model/ExportBaseTest.php @@ -0,0 +1,607 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DataWizard\tests\unit\Application\Model; + +use D3\DataWizard\Application\Model\Exceptions\ExportFileException; +use D3\DataWizard\Application\Model\Exceptions\TaskException; +use D3\DataWizard\Application\Model\ExportRenderer\Csv; +use D3\DataWizard\Application\Model\ExportRenderer\RendererBridge; +use D3\DataWizard\Application\Model\ExportRenderer\RendererInterface; +use D3\DataWizard\tests\tools\d3TestExport; +use D3\ModCfg\Application\Model\d3filesystem; +use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase; +use FormManager\Inputs\Hidden; +use FormManager\Inputs\Number; +use FormManager\Inputs\Radio; +use OxidEsales\Eshop\Core\Database\Adapter\Doctrine\Database; +use OxidEsales\Eshop\Core\Exception\StandardException; +use PHPUnit\Framework\MockObject\MockObject; +use ReflectionException; + +class ExportBaseTest extends d3ModCfgUnitTestCase +{ + /** @var d3TestExport */ + protected $_oModel; + + public function setUp() : void + { + parent::setUp(); + + $this->_oModel = oxNew(d3TestExport::class); + } + + public function tearDown() : void + { + parent::tearDown(); + + unset($this->_oModel); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::getDescription + * @test + * @throws ReflectionException + */ + public function canGetDescription() + { + $this->assertIsString( + $this->callMethod( + $this->_oModel, + 'getDescription' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::getButtonText + * @test + * @throws ReflectionException + */ + public function canGetButtonText() + { + $this->assertIsString( + $this->callMethod( + $this->_oModel, + 'getButtonText' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::hasFormElements + * @test + * @throws ReflectionException + * @dataProvider canGetHasFormElementsDataProvider + */ + public function canGetHasFormElements($formElements, $expected) + { + $this->setValue($this->_oModel, 'formElements', $formElements); + + $this->assertSame( + $expected, + $this->callMethod( + $this->_oModel, + 'hasFormElements' + ) + ); + } + + public function canGetHasFormElementsDataProvider() + { + return [ + 'hasFormElements' => [['abc', 'def'], true], + 'hasNoFormElements' => [[], false], + ]; + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::getFormElements + * @test + * @throws ReflectionException + * @dataProvider canGetHasFormElementsDataProvider + */ + public function canGetFormElements($formElements) + { + $this->setValue($this->_oModel, 'formElements', $formElements); + + $this->assertSame( + $formElements, + $this->callMethod( + $this->_oModel, + 'getFormElements' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::registerFormElement + * @test + * @throws ReflectionException + * @dataProvider canRegisterFormElementDataProvider + */ + public function canRegisterFormElement($inputClass) + { + $oldCount = count($this->getValue($this->_oModel, 'formElements')); + + /** @var Radio|MockObject $inputMock */ + $inputMock = $this->getMockBuilder($inputClass) + ->onlyMethods([ + 'setTemplate', + 'setAttribute' + ]) + ->getMock(); + $inputMock->expects($this->atLeastOnce())->method('setTemplate'); + $inputMock->expects($this->atLeastOnce())->method('setAttribute'); + + $this->callMethod( + $this->_oModel, + 'registerFormElement', + [$inputMock] + ); + + $newCount = count($this->getValue($this->_oModel, 'formElements')); + + $this->assertGreaterThan($oldCount, $newCount); + } + + /** + * @return \string[][] + */ + public function canRegisterFormElementDataProvider(): array + { + return [ + 'Radio' => [Radio::class], + 'Checkbox' => [Radio::class], + 'Hidden' => [Hidden::class] + ]; + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::run + * @test + * @throws ReflectionException + */ + public function canRunWithoutFormElements() + { + $format = 'myFormat'; + $path = 'myPath'; + + /** @var d3TestExport|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(d3TestExport::class) + ->onlyMethods([ + 'hasFormElements', + 'executeExport' + ]) + ->getMock(); + $modelMock->expects($this->atLeastOnce())->method('hasFormElements')->willReturn(false); + $modelMock->expects($this->atLeastOnce())->method('executeExport')->with($format, $path)->willReturn('some content'); + $this->_oModel = $modelMock; + + $this->callMethod( + $this->_oModel, + 'run', + [$format, $path] + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::run + * @test + * @throws ReflectionException + * @dataProvider canRunWithFormElementsDataProvider + */ + public function canRunWithFormElements($elements, $blThrowException) + { + $format = 'myFormat'; + $path = 'myPath'; + + $expectedException = oxNew(StandardException::class); + + $modelMock = $this->getMockBuilder(d3TestExport::class) + ->onlyMethods([ + 'hasFormElements', + 'executeExport', + 'getFormElements' + ]) + ->getMock(); + $modelMock->expects($this->atLeastOnce())->method('hasFormElements')->willReturn(true); + $modelMock->expects($this->exactly((int) !$blThrowException))->method('executeExport')->with($format, $path)->willReturn('some content'); + $modelMock->expects($this->atLeastOnce())->method('getFormElements')->willReturn($elements); + $this->_oModel = $modelMock; + + if ($blThrowException) { + $this->expectException(get_class($expectedException)); + } + + $this->callMethod( + $this->_oModel, + 'run', + [$format, $path] + ); + } + + /** + * @return array[] + */ + public function canRunWithFormElementsDataProvider(): array + { + /** @var Radio|MockObject $validMock */ + $validMock = $this->getMockBuilder(Radio::class) + ->onlyMethods(['isValid']) + ->getMock(); + $validMock->expects($this->atLeastOnce())->method('isValid')->willReturn(true); + + $invalidField = new Number(null, [ + 'required' => true, + 'min' => 1, + 'max' => 10, + 'step' => 5, + ]); + $invalidField + ->setValue(20) + ->setErrorMessages(['errorMsgs']); + + return [ + 'validElements' => [[$validMock, $validMock], false], + 'invalidElements' => [[$validMock, $invalidField], true] + ]; + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::executeExport + * @test + * @throws ReflectionException + * @dataProvider canExecuteExportDataProvider + */ + public function canExecuteExport($path, $throwsException) + { + /** @var d3filesystem|MockObject $fsMock */ + $fsMock = $this->getMockBuilder(d3filesystem::class) + ->onlyMethods([ + 'startDirectDownload', + 'filterFilename', + 'trailingslashit', + 'createFile' + ]) + ->getMock(); + $fsMock->expects($this->exactly((int) !isset($path)))->method('startDirectDownload')->willReturn(true); + $fsMock->method('filterFilename')->willReturnArgument(0); + $fsMock->expects($this->exactly((int) isset($path)))->method('trailingslashit')->willReturnArgument(0); + $fsMock->expects($this->exactly((int) isset($path)))->method('createFile')->willReturn((bool) !$throwsException); + + /** @var d3TestExport|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(d3TestExport::class) + ->onlyMethods([ + 'getQuery', + 'getExportData', + 'renderContent', + 'getFileSystem', + 'getExportFileName' + ]) + ->getMock(); + $modelMock->expects($this->atLeastOnce())->method('getQuery')->willReturn(['SELECT 1', ['arg1', 'arg2']]); + $modelMock->expects($this->atLeastOnce())->method('getExportData')->willReturn([[1, 2], ['field1', 'field2']]); + $modelMock->expects($this->atLeastOnce())->method('renderContent')->willReturn('some content'); + $modelMock->expects($this->atLeastOnce())->method('getFileSystem')->willReturn($fsMock); + $modelMock->expects($this->atLeastOnce())->method('getExportFileName')->willReturn('exportFileName'); + + $this->_oModel = $modelMock; + + if ($path && $throwsException) { + $this->expectException(ExportFileException::class); + } + + $this->callMethod( + $this->_oModel, + 'executeExport', + ['CSV', $path] + ); + } + + /** + * @return array[] + */ + public function canExecuteExportDataProvider(): array + { + return [ + 'unable to create path for saving file' => ['myPath', true], + 'can create path for saving file' => ['myPath', false], + 'no path for download' => [null, false], + ]; + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::d3GetDb + * @test + * @throws ReflectionException + */ + public function canGetDb() + { + $this->assertInstanceOf( + Database::class, + $this->callMethod( + $this->_oModel, + 'd3GetDb' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::getFileSystem + * @test + * @throws ReflectionException + */ + public function canGetFileSystem() + { + $this->assertInstanceOf( + d3filesystem::class, + $this->callMethod( + $this->_oModel, + 'getFileSystem' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::getRenderer + * @test + * @throws ReflectionException + */ + public function canGetRenderer() + { + /** @var RendererInterface|MockObject $rendererMock */ + $rendererMock = $this->getMockBuilder(Csv::class) + ->getMock(); + + /** @var RendererBridge|MockObject $rendererBridgeMock */ + $rendererBridgeMock = $this->getMockBuilder(RendererBridge::class) + ->onlyMethods(['getRenderer']) + ->getMock(); + $rendererBridgeMock->expects($this->atLeastOnce())->method('getRenderer')->willReturn($rendererMock); + + /** @var d3TestExport|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(d3TestExport::class) + ->onlyMethods([ + 'getRendererBridge' + ]) + ->getMock(); + $modelMock->expects($this->atLeastOnce())->method('getRendererBridge')->willReturn($rendererBridgeMock); + + $this->_oModel = $modelMock; + + $this->assertSame( + $rendererMock, + $this->callMethod( + $this->_oModel, + 'getRenderer', + [RendererBridge::FORMAT_CSV] + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::getRendererBridge + * @test + * @throws ReflectionException + */ + public function canGetRendererBridge() + { + $this->assertInstanceOf( + RendererBridge::class, + $this->callMethod( + $this->_oModel, + 'getRendererBridge' + ) + ); + } + + + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::getFileExtension + * @test + * @throws ReflectionException + */ + public function canGetFileExtension() + { + $format = RendererBridge::FORMAT_CSV; + $expected = 'myFileExtension'; + + /** @var RendererInterface|MockObject $rendererMock */ + $rendererMock = $this->getMockBuilder(Csv::class) + ->onlyMethods(['getFileExtension']) + ->getMock(); + $rendererMock->expects($this->atLeastOnce())->method('getFileExtension')->willReturn($expected); + + /** @var d3TestExport|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(d3TestExport::class) + ->onlyMethods([ + 'getRenderer' + ]) + ->getMock(); + $modelMock->expects($this->atLeastOnce())->method('getRenderer')->with($format)->willReturn($rendererMock); + + $this->_oModel = $modelMock; + + $this->assertSame( + $expected, + $this->callMethod( + $this->_oModel, + 'getFileExtension', + [$format] + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::renderContent + * @test + * @throws ReflectionException + */ + public function canRenderContent() + { + $rows = ['row1', 'row2']; + $fieldnames = ['fieldname1', 'fieldname2']; + $format = RendererBridge::FORMAT_CSV; + $expected = 'myContent'; + + /** @var RendererInterface|MockObject $rendererMock */ + $rendererMock = $this->getMockBuilder(Csv::class) + ->onlyMethods(['getContent']) + ->getMock(); + $rendererMock->expects($this->atLeastOnce())->method('getContent')->with($rows, $fieldnames)->willReturn($expected); + + /** @var d3TestExport|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(d3TestExport::class) + ->onlyMethods([ + 'getRenderer' + ]) + ->getMock(); + $modelMock->expects($this->atLeastOnce())->method('getRenderer')->with($format)->willReturn($rendererMock); + + $this->_oModel = $modelMock; + + $this->assertSame( + $expected, + $this->callMethod( + $this->_oModel, + 'renderContent', + [$rows, $fieldnames, $format] + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::getExportFilenameBase + * @test + * @throws ReflectionException + */ + public function canGetExportFilenameBase() + { + /** @var d3TestExport|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(d3TestExport::class) + ->onlyMethods([ + 'getTitle' + ]) + ->getMock(); + $modelMock->expects($this->atLeastOnce())->method('getTitle')->willReturn('someTitle'); + + $this->_oModel = $modelMock; + + $this->callMethod( + $this->_oModel, + 'getExportFilenameBase' + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::getExportFileName + * @test + * @throws ReflectionException + */ + public function canGetExportFileName() + { + $format = RendererBridge::FORMAT_CSV; + + /** @var d3TestExport|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(d3TestExport::class) + ->onlyMethods([ + 'getExportFilenameBase', + 'getFileExtension' + ]) + ->getMock(); + $modelMock->expects($this->atLeastOnce())->method('getExportFilenameBase')->willReturn('base'); + $modelMock->expects($this->atLeastOnce())->method('getFileExtension')->with($format)->willReturn('extension'); + + $this->_oModel = $modelMock; + + $this->assertRegExp( + '/^base_(\d{4})-(\d{2})-(\d{2})_(\d{2})-(\d{2})-(\d{2})\.extension$/m', + $this->callMethod( + $this->_oModel, + 'getExportFileName', + [$format] + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportBase::getExportData + * @test + * @throws ReflectionException + * @dataProvider canGetExportDataDataProvider + */ + public function canGetExportData($query, $throwsException, $dbResult) + { + /** @var Database|MockObject $dbMock */ + $dbMock = $this->getMockBuilder(Database::class) + ->onlyMethods(['getAll']) + ->getMock(); + $dbMock->expects($this->exactly((int) !$throwsException))->method('getAll')->willReturn($dbResult); + + /** @var d3TestExport|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(d3TestExport::class) + ->onlyMethods([ + 'd3GetDb' + ]) + ->getMock(); + $modelMock->expects($this->exactly((int) !$throwsException))->method('d3GetDb')->willReturn($dbMock); + + $this->_oModel = $modelMock; + + try { + $result = $this->callMethod( + $this->_oModel, + 'getExportData', + [[$query], ['param1', 'param2']] + ); + + $this->assertSame( + [ + [ + [ + 'field1' => 'content1', + 'field2' => 'content2' + ] + ], + [ + 'field1', + 'field2' + ] + ], + $result + ); + } catch (TaskException $e) { + if ($throwsException) { + $this->assertStringContainsString('NOEXPORTSELECT', $e->getMessage()); + } elseif (!count($dbResult)) { + $this->assertStringContainsString('kein Inhalt', $e->getMessage()); + } + } + } + + /** + * @return array[] + */ + public function canGetExportDataDataProvider(): array + { + return [ + 'not SELECT throws exception' => [' UPDATE 1', true, []], + 'empty SELECT' => [' SELECT 1', false, []], + 'fulfilled SELECT' => [' SELECT 1', false, [['field1' => 'content1', 'field2' => 'content2']]], + ]; + } +} \ No newline at end of file diff --git a/tests/unit/Application/Model/ExportRenderer/CsvTest.php b/tests/unit/Application/Model/ExportRenderer/CsvTest.php new file mode 100644 index 0000000..2968a1b --- /dev/null +++ b/tests/unit/Application/Model/ExportRenderer/CsvTest.php @@ -0,0 +1,180 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DataWizard\tests\unit\Application\Model\ExportRenderer; + +use D3\DataWizard\Application\Model\Exceptions\RenderException; +use D3\DataWizard\Application\Model\ExportRenderer\Csv; +use League\Csv\Exception; +use League\Csv\Writer; +use OxidEsales\Eshop\Core\Config; +use OxidEsales\Eshop\Core\Registry; +use PHPUnit\Framework\MockObject\MockObject; + +class CsvTest extends ExportRendererTest +{ + /** @var Csv */ + protected $_oModel; + + public function setUp() : void + { + parent::setUp(); + + $this->_oModel = oxNew(Csv::class); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportRenderer\Csv::getContent + * @test + * @throws \ReflectionException + * @dataProvider canGetContentDataProvider + */ + public function canGetContent($blThrowException) + { + $expected = 'expectedReturn'; + $fieldList = ['field1', 'field2']; + $valueList = ['value1', 'value2']; + + /** @var Writer|MockObject $csvMock */ + $csvMockBuilder = $this->getMockBuilder(Writer::class); + $csvMockBuilder->disableOriginalConstructor(); + $onlyMethods = ['insertOne', 'insertAll']; + if (method_exists($csvMockBuilder->getMock(), 'getContent')) { + $onlyMethods[] = 'getContent'; + } else { + $csvMockBuilder->addMethods(['getContent']); + } + $csvMockBuilder->onlyMethods($onlyMethods); + $csvMock = $csvMockBuilder->getMock(); + + if ($blThrowException) { + $csvMock->expects($this->atLeastOnce())->method('getContent')->willThrowException(oxNew(Exception::class)); + $this->expectException(RenderException::class); + } else { + $csvMock->expects($this->atLeastOnce())->method('getContent')->willReturn($expected); + } + $csvMock->expects($this->atLeastOnce())->method('insertOne')->with($fieldList)->willReturn(1); + $csvMock->expects($this->atLeastOnce())->method('insertAll')->with($valueList)->willReturn(1); + + /** @var Csv|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(Csv::class) + ->onlyMethods(['getCsv']) + ->getMock(); + $modelMock->expects($this->atLeastOnce())->method('getCsv')->willReturn($csvMock); + $this->_oModel = $modelMock; + + $this->assertSame( + $expected, + $this->callMethod( + $this->_oModel, + 'getContent', + [$valueList, $fieldList] + ) + ); + } + + /** + * @return array + */ + public function canGetContentDataProvider(): array + { + return [ + 'exception' => [true], + 'no exception' => [false] + ]; + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportRenderer\Csv::getCsv + * @test + * @throws \ReflectionException + */ + public function canGetCsv() + { + $this->assertInstanceOf( + Writer::class, + $this->callMethod( + $this->_oModel, + 'getCsv' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportRenderer\Csv::getCsv + * @test + * @throws \ReflectionException + */ + public function canGetCsvNoSettings() + { + /** @var Config|MockObject $configMock */ + $configMock = $this->getMockBuilder(Config::class) + ->onlyMethods(['getConfigParam']) + ->getMock(); + $configMock->expects($this->atLeastOnce())->method('getConfigParam')->willReturnCallback( + function ($argName) { + switch ($argName) { + case 'sGiCsvFieldEncloser': + case 'sCSVSign': + return false; + default: + return Registry::getConfig()->getConfigParam($argName); + } + } + ); + + $modelMock = $this->getMockBuilder(Csv::class) + ->onlyMethods(['d3GetConfig']) + ->getMock(); + $modelMock->method('d3GetConfig')->willReturn($configMock); + $this->_oModel = $modelMock; + + $csv = $this->callMethod( + $this->_oModel, + 'getCsv' + ); + + $this->assertInstanceOf( + Writer::class, + $csv + ); + + $this->assertSame( + '"', + $csv->getEnclosure() + ); + + $this->assertSame( + ';', + $csv->getDelimiter() + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportRenderer\Csv::d3GetConfig + * @test + * @throws \ReflectionException + */ + public function canGetConfig() + { + $this->assertInstanceOf( + Config::class, + $this->callMethod( + $this->_oModel, + 'd3GetConfig' + ) + ); + } +} \ No newline at end of file diff --git a/tests/unit/Application/Model/ExportRenderer/ExportRendererTest.php b/tests/unit/Application/Model/ExportRenderer/ExportRendererTest.php new file mode 100644 index 0000000..f89dede --- /dev/null +++ b/tests/unit/Application/Model/ExportRenderer/ExportRendererTest.php @@ -0,0 +1,65 @@ + + * @link https://www.oxidmodule.com + */ + +namespace D3\DataWizard\tests\unit\Application\Model\ExportRenderer; + +use D3\DataWizard\Application\Model\ExportRenderer\RendererInterface; +use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase; + +abstract class ExportRendererTest extends d3ModCfgUnitTestCase +{ + /** @var RendererInterface */ + protected $_oModel; + + public function tearDown() : void + { + parent::tearDown(); + + unset($this->_oModel); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportRenderer\Csv::getFileExtension + * @covers \D3\DataWizard\Application\Model\ExportRenderer\Json::getFileExtension + * @covers \D3\DataWizard\Application\Model\ExportRenderer\Pretty::getFileExtension + * @test + * @throws \ReflectionException + */ + public function canGetFileExtension() + { + $this->assertRegExp( + "/^[a-z0-9._-]*$/i", + $this->callMethod( + $this->_oModel, + 'getFileExtension' + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportRenderer\Csv::getTitleTranslationId + * @covers \D3\DataWizard\Application\Model\ExportRenderer\Json::getTitleTranslationId + * @covers \D3\DataWizard\Application\Model\ExportRenderer\Pretty::getTitleTranslationId + * @test + * @throws \ReflectionException + */ + public function canGetTitleTranslationId() + { + $this->assertIsString( + $this->callMethod( + $this->_oModel, + 'getTitleTranslationId' + ) + ); + } +} \ No newline at end of file diff --git a/tests/unit/Application/Model/ExportRenderer/JsonTest.php b/tests/unit/Application/Model/ExportRenderer/JsonTest.php new file mode 100644 index 0000000..413aa80 --- /dev/null +++ b/tests/unit/Application/Model/ExportRenderer/JsonTest.php @@ -0,0 +1,66 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DataWizard\tests\unit\Application\Model\ExportRenderer; + +use D3\DataWizard\Application\Model\Exceptions\RenderException; +use D3\DataWizard\Application\Model\ExportRenderer\Json; + +class JsonTest extends ExportRendererTest +{ + /** @var Json */ + protected $_oModel; + + public function setUp() : void + { + parent::setUp(); + + $this->_oModel = oxNew(Json::class); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportRenderer\Json::getContent + * @test + * @throws \ReflectionException + * @dataProvider canGetContentDataProvider + */ + public function canGetContent($valueList, $expectException) + { + $fieldList = ['field1', 'field2']; + + if ($expectException) { + $this->expectException(RenderException::class); + } + + $this->assertJson( + $this->callMethod( + $this->_oModel, + 'getContent', + [$valueList, $fieldList] + ) + ); + } + + /** + * @return \string[][] + */ + public function canGetContentDataProvider(): array + { + return [ + 'valid' => [['value1', 'value2'], false], + 'invalid' => [["text" => "\xB1\x31"], true] // malformed UTF8 chars + ]; + } +} \ No newline at end of file diff --git a/tests/unit/Application/Model/ExportRenderer/PrettyTest.php b/tests/unit/Application/Model/ExportRenderer/PrettyTest.php new file mode 100644 index 0000000..481d9ec --- /dev/null +++ b/tests/unit/Application/Model/ExportRenderer/PrettyTest.php @@ -0,0 +1,85 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DataWizard\tests\unit\Application\Model\ExportRenderer; + +use D3\DataWizard\Application\Model\ExportRenderer\Pretty; +use MathieuViossat\Util\ArrayToTextTable; +use PHPUnit\Framework\MockObject\MockObject; + +class PrettyTest extends ExportRendererTest +{ + /** @var Pretty */ + protected $_oModel; + + public function setUp() : void + { + parent::setUp(); + + $this->_oModel = oxNew(Pretty::class); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportRenderer\Pretty::getContent + * @test + * @throws \ReflectionException + */ + public function canGetContent() + { + $expected = 'expectedReturn'; + $fieldList = ['field1', 'field2']; + $valueList = ['value1', 'value2']; + + /** @var ArrayToTextTable|MockObject $csvMock */ + $arrayToTextTableMock = $this->getMockBuilder(ArrayToTextTable::class) + ->disableOriginalConstructor() + ->onlyMethods(['getTable']) + ->getMock(); + $arrayToTextTableMock->expects($this->atLeastOnce())->method('getTable')->willReturn($expected); + + /** @var Pretty|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(Pretty::class) + ->onlyMethods(['getArrayToTextTableInstance']) + ->getMock(); + $modelMock->expects($this->atLeastOnce())->method('getArrayToTextTableInstance')->willReturn($arrayToTextTableMock); + $this->_oModel = $modelMock; + + $this->assertSame( + $expected, + $this->callMethod( + $this->_oModel, + 'getContent', + [$valueList, $fieldList] + ) + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportRenderer\Pretty::getArrayToTextTableInstance + * @test + * @throws \ReflectionException + */ + public function canGetArrayToTextTableInstance() + { + $this->assertInstanceOf( + ArrayToTextTable::class, + $this->callMethod( + $this->_oModel, + 'getArrayToTextTableInstance', + [['field1', 'field2']] + ) + ); + } +} \ No newline at end of file diff --git a/tests/unit/Application/Model/ExportRenderer/RendererBridgeTest.php b/tests/unit/Application/Model/ExportRenderer/RendererBridgeTest.php new file mode 100644 index 0000000..c7c5c07 --- /dev/null +++ b/tests/unit/Application/Model/ExportRenderer/RendererBridgeTest.php @@ -0,0 +1,151 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DataWizard\tests\unit\Application\Model\ExportRenderer; + +use D3\DataWizard\Application\Model\Exceptions\NoSuitableRendererException; +use D3\DataWizard\Application\Model\ExportRenderer\Csv; +use D3\DataWizard\Application\Model\ExportRenderer\Json; +use D3\DataWizard\Application\Model\ExportRenderer\Pretty; +use D3\DataWizard\Application\Model\ExportRenderer\RendererBridge; +use D3\DataWizard\Application\Model\ExportRenderer\RendererInterface; +use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase; +use PHPUnit\Framework\MockObject\MockObject; + +class RendererBridgeTest extends d3ModCfgUnitTestCase +{ + /** @var RendererBridge */ + protected $_oModel; + + public function setUp() : void + { + parent::setUp(); + + $this->_oModel = oxNew(RendererBridge::class); + } + + public function tearDown(): void + { + parent::tearDown(); + + unset($this->_oModel); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportRenderer\RendererBridge::getRendererList + * @test + * @throws \ReflectionException + */ + public function canGetRendererList() + { + $list = $this->callMethod( + $this->_oModel, + 'getRendererList' + ); + + $this->assertIsArray($list); + $this->assertTrue((bool) count($list)); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportRenderer\RendererBridge::getTranslatedRendererIdList + * @test + * @throws \ReflectionException + */ + public function canGetTranslatedRendererIdList() + { + $utlist = $this->callMethod( + $this->_oModel, + 'getRendererList' + ); + + $list = $this->callMethod( + $this->_oModel, + 'getTranslatedRendererIdList' + ); + + $this->assertIsArray($list); + $this->assertTrue((bool) count($list)); + $this->assertSame(count($utlist), count($list)); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportRenderer\RendererBridge::translateRendererId + * @test + * @throws \ReflectionException + */ + public function canTranslateRendererId() + { + $expected = "expectedTranslation"; + + /** @var RendererInterface|MockObject $renderMock */ + $renderMock = $this->getMockBuilder(Pretty::class) + ->onlyMethods(['getTitleTranslationId']) + ->getMock(); + $renderMock->expects($this->atLeastOnce())->method('getTitleTranslationId')->willReturn($expected); + + $this->callMethod( + $this->_oModel, + 'translateRendererId', + [$renderMock] + ); + } + + /** + * @covers \D3\DataWizard\Application\Model\ExportRenderer\RendererBridge::getRenderer + * @test + * @param $format + * @param $blThrowException + * @throws \ReflectionException + * @dataProvider canGetRendererDataProvider + */ + public function canGetRenderer($format, $blThrowException) + { + /** @var RendererBridge|MockObject $modelMock */ + $modelMock = $this->getMockBuilder(RendererBridge::class) + ->onlyMethods(['getRendererList']) + ->getMock(); + $modelMock->expects($this->atLeastOnce())->method('getRendererList')->willReturn( + [ + 'CSV' => $this->getMockBuilder(Csv::class)->getMock(), + 'Pretty' => $this->getMockBuilder(Pretty::class)->getMock(), + 'JSON' => $this->getMockBuilder(Json::class)->getMock() + ] + ); + + $this->_oModel = $modelMock; + + if ($blThrowException) { + $this->expectException(NoSuitableRendererException::class); + } + + $this->callMethod( + $this->_oModel, + 'getRenderer', + [$format] + ); + } + + /** + * @return array + */ + public function canGetRendererDataProvider(): array + { + return [ + 'existing renderer'=> [RendererBridge::FORMAT_JSON, false], + 'unknown renderer'=> ['unknownRenderer', true] + ]; + } +} \ No newline at end of file