From 1d6fd2c03d2598d5aa4a0371727f0fedfc888ed1 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Tue, 20 Apr 2021 23:27:11 -0700 Subject: [PATCH] Extensions - Add test-case for invoking upgrader classes This is essentially a copy of CRM_Extension_Manager_ModuleTest, adapted to use the upgrader class instead of lifecycle-hooks. --- .../CRM/Moduleupgtest/Upgrader.php | 40 +++ .../info.xml | 11 + .../moduleupgtest.php | 1 + .../CRM/Extension/Manager/ModuleUpgTest.php | 328 ++++++++++++++++++ 4 files changed, 380 insertions(+) create mode 100644 tests/extensions/test.extension.manager.moduleupgtest/CRM/Moduleupgtest/Upgrader.php create mode 100644 tests/extensions/test.extension.manager.moduleupgtest/info.xml create mode 100644 tests/extensions/test.extension.manager.moduleupgtest/moduleupgtest.php create mode 100644 tests/phpunit/CRM/Extension/Manager/ModuleUpgTest.php diff --git a/tests/extensions/test.extension.manager.moduleupgtest/CRM/Moduleupgtest/Upgrader.php b/tests/extensions/test.extension.manager.moduleupgtest/CRM/Moduleupgtest/Upgrader.php new file mode 100644 index 0000000000..6c280ea778 --- /dev/null +++ b/tests/extensions/test.extension.manager.moduleupgtest/CRM/Moduleupgtest/Upgrader.php @@ -0,0 +1,40 @@ + + moduleupgtest + test_extension_manager_moduleupgtest + + mock + + + + + CRM_Moduleupgtest_Upgrader + diff --git a/tests/extensions/test.extension.manager.moduleupgtest/moduleupgtest.php b/tests/extensions/test.extension.manager.moduleupgtest/moduleupgtest.php new file mode 100644 index 0000000000..b3d9bbc7f3 --- /dev/null +++ b/tests/extensions/test.extension.manager.moduleupgtest/moduleupgtest.php @@ -0,0 +1 @@ +basedir = $this->createTempDir('ext-'); + $this->system = new CRM_Extension_System([ + 'extensionsDir' => $this->basedir, + 'extensionsURL' => 'http://testbase/', + ]); + $this->setExtensionSystem($this->system); + } + + public function tearDown(): void { + parent::tearDown(); + $this->system = NULL; + } + + /** + * Install an extension with a valid type name. + */ + public function testInstallDisableUninstall() { + $manager = $this->system->getManager(); + $this->assertModuleActiveByName(FALSE, 'moduleupgtest'); + + $manager->install(['test.extension.manager.moduleupgtest']); + $this->assertHookCounts('moduleupgtest', [ + 'install' => 1, + 'postInstall' => 1, + 'enable' => 1, + 'disable' => 0, + 'uninstall' => 0, + ]); + $this->assertModuleActiveByName(TRUE, 'moduleupgtest'); + $this->assertModuleActiveByKey(TRUE, 'test.extension.manager.moduleupgtest'); + + $manager->disable(['test.extension.manager.moduleupgtest']); + $this->assertHookCounts('moduleupgtest', [ + 'install' => 1, + 'postInstall' => 1, + 'enable' => 1, + 'disable' => 1, + 'uninstall' => 0, + ]); + $this->assertModuleActiveByName(FALSE, 'moduleupgtest'); + $this->assertModuleActiveByKey(FALSE, 'test.extension.manager.moduleupgtest'); + + $manager->uninstall(['test.extension.manager.moduleupgtest']); + $this->assertHookCounts('moduleupgtest', [ + 'install' => 1, + 'postInstall' => 1, + 'enable' => 1, + 'disable' => 1, + 'uninstall' => 1, + ]); + $this->assertModuleActiveByName(FALSE, 'moduleupgtest'); + $this->assertModuleActiveByKey(FALSE, 'test.extension.manager.moduleupgtest'); + } + + /** + * Install an extension with a valid type name. + */ + public function testInstallDisableEnable() { + $manager = $this->system->getManager(); + $this->assertModuleActiveByName(FALSE, 'moduleupgtest'); + $this->assertModuleActiveByKey(FALSE, 'test.extension.manager.moduleupgtest'); + + $manager->install(['test.extension.manager.moduleupgtest']); + $this->assertHookCounts('moduleupgtest', [ + 'install' => 1, + 'enable' => 1, + 'disable' => 0, + 'uninstall' => 0, + ]); + $this->assertModuleActiveByName(TRUE, 'moduleupgtest'); + $this->assertModuleActiveByKey(TRUE, 'test.extension.manager.moduleupgtest'); + + $manager->disable(['test.extension.manager.moduleupgtest']); + $this->assertHookCounts('moduleupgtest', [ + 'install' => 1, + 'enable' => 1, + 'disable' => 1, + 'uninstall' => 0, + ]); + $this->assertModuleActiveByName(FALSE, 'moduleupgtest'); + $this->assertModuleActiveByKey(FALSE, 'test.extension.manager.moduleupgtest'); + + $manager->enable(['test.extension.manager.moduleupgtest']); + $this->assertHookCounts('moduleupgtest', [ + 'install' => 1, + 'enable' => 2, + 'disable' => 1, + 'uninstall' => 0, + ]); + $this->assertModuleActiveByName(TRUE, 'moduleupgtest'); + $this->assertModuleActiveByKey(TRUE, 'test.extension.manager.moduleupgtest'); + } + + /** + * Install an extension then forcibly remove the code and cleanup DB afterwards. + */ + public function testInstall_DirtyRemove_Disable_Uninstall() { + // create temporary extension (which can dirtily remove later) + $this->_createExtension('test.extension.manager.moduleupg.auto1', 'module', 'test_extension_manager_moduleupg_auto1'); + $mainfile = $this->basedir . '/test.extension.manager.moduleupg.auto1/test_extension_manager_moduleupg_auto1.php'; + $this->assertTrue(file_exists($mainfile)); + $manager = $this->system->getManager(); + $this->assertModuleActiveByName(FALSE, 'test_extension_manager_moduleupg_auto1'); + $this->assertModuleActiveByKey(FALSE, 'test.extension.manager.moduleupg.auto1'); + + // install it + $manager->install(['test.extension.manager.moduleupg.auto1']); + $this->assertEquals('installed', $manager->getStatus('test.extension.manager.moduleupg.auto1')); + $this->assertHookCounts('test_extension_manager_moduleupg_auto1', [ + 'install' => 1, + 'enable' => 1, + 'disable' => 0, + 'uninstall' => 0, + ]); + $this->assertModuleActiveByName(TRUE, 'test_extension_manager_moduleupg_auto1'); + $this->assertModuleActiveByKey(TRUE, 'test.extension.manager.moduleupg.auto1'); + + // dirty removal + CRM_Utils_File::cleanDir($this->basedir . '/test.extension.manager.moduleupg.auto1', TRUE, FALSE); + $manager->refresh(); + $this->assertEquals('installed-missing', $manager->getStatus('test.extension.manager.moduleupg.auto1')); + + // disable while missing + $manager->disable(['test.extension.manager.moduleupg.auto1']); + $this->assertEquals('disabled-missing', $manager->getStatus('test.extension.manager.moduleupg.auto1')); + $this->assertHookCounts('test_extension_manager_moduleupg_auto1', [ + 'install' => 1, + 'enable' => 1, + // normally called -- but not for missing modules! + 'disable' => 0, + 'uninstall' => 0, + ]); + $this->assertModuleActiveByName(FALSE, 'test_extension_manager_moduleupg_auto1'); + $this->assertModuleActiveByKey(FALSE, 'test.extension.manager.moduleupgtest'); + + $manager->uninstall(['test.extension.manager.moduleupg.auto1']); + $this->assertHookCounts('test_extension_manager_moduleupg_auto1', [ + 'install' => 1, + 'enable' => 1, + // normally called -- but not for missing modules! + 'disable' => 0, + // normally called -- but not for missing modules! + 'uninstall' => 0, + ]); + $this->assertEquals('unknown', $manager->getStatus('test.extension.manager.moduleupg.auto1')); + $this->assertModuleActiveByName(FALSE, 'test_extension_manager_moduleupg_auto1'); + $this->assertModuleActiveByKey(FALSE, 'test.extension.manager.moduleupg.auto1'); + } + + /** + * Install an extension then forcibly remove the code and cleanup DB afterwards. + */ + public function testInstall_DirtyRemove_Disable_Restore() { + // create temporary extension (which can dirtily remove later) + $this->_createExtension('test.extension.manager.moduleupg.auto2', 'module', 'test_extension_manager_moduleupg_auto2'); + $mainfile = $this->basedir . '/test.extension.manager.moduleupg.auto2/test_extension_manager_moduleupg_auto2.php'; + $this->assertTrue(file_exists($mainfile)); + $manager = $this->system->getManager(); + $this->assertModuleActiveByName(FALSE, 'test_extension_manager_moduleupg_auto2'); + $this->assertModuleActiveByKey(FALSE, 'test.extension.manager.moduleupg.auto2'); + + // install it + $manager->install(['test.extension.manager.moduleupg.auto2']); + $this->assertEquals('installed', $manager->getStatus('test.extension.manager.moduleupg.auto2')); + $this->assertHookCounts('test_extension_manager_moduleupg_auto2', [ + 'install' => 1, + 'enable' => 1, + 'disable' => 0, + 'uninstall' => 0, + ]); + $this->assertModuleActiveByName(TRUE, 'test_extension_manager_moduleupg_auto2'); + $this->assertModuleActiveByKey(TRUE, 'test.extension.manager.moduleupg.auto2'); + + // dirty removal + CRM_Utils_File::cleanDir($this->basedir . '/test.extension.manager.moduleupg.auto2', TRUE, FALSE); + $manager->refresh(); + $this->assertEquals('installed-missing', $manager->getStatus('test.extension.manager.moduleupg.auto2')); + + // disable while missing + $manager->disable(['test.extension.manager.moduleupg.auto2']); + $this->assertEquals('disabled-missing', $manager->getStatus('test.extension.manager.moduleupg.auto2')); + $this->assertHookCounts('test_extension_manager_moduleupg_auto2', [ + 'install' => 1, + 'enable' => 1, + // normally called -- but not for missing modules! + 'disable' => 0, + 'uninstall' => 0, + ]); + $this->assertModuleActiveByName(FALSE, 'test_extension_manager_moduleupg_auto2'); + $this->assertModuleActiveByKey(FALSE, 'test.extension.manager.moduleupgtest'); + + // restore the code + $this->_createExtension('test.extension.manager.moduleupg.auto2', 'module', 'test_extension_manager_moduleupg_auto2'); + $manager->refresh(); + $this->assertHookCounts('test_extension_manager_moduleupg_auto2', [ + 'install' => 1, + 'enable' => 1, + 'disable' => 0, + 'uninstall' => 0, + ]); + $this->assertEquals('disabled', $manager->getStatus('test.extension.manager.moduleupg.auto2')); + $this->assertModuleActiveByName(FALSE, 'test_extension_manager_moduleupg_auto2'); + $this->assertModuleActiveByKey(FALSE, 'test.extension.manager.moduleupg.auto2'); + } + + /** + * @param $module + * @param array $counts + * Expected hook invocation counts ($hookName => $count). + */ + public function assertHookCounts($module, $counts) { + global $_test_extension_manager_moduleupgtest_counts; + foreach ($counts as $key => $expected) { + $actual = @$_test_extension_manager_moduleupgtest_counts[$module][$key]; + $this->assertEquals($expected, $actual, + sprintf('Expected %d call(s) to hook_civicrm_%s -- found %d', $expected, $key, $actual) + ); + } + } + + /** + * @param $expectedIsActive + * @param $prefix + */ + public function assertModuleActiveByName($expectedIsActive, $prefix) { + // FIXME + $activeModules = CRM_Core_PseudoConstant::getModuleExtensions(TRUE); + foreach ($activeModules as $activeModule) { + if ($activeModule['prefix'] == $prefix) { + $this->assertEquals($expectedIsActive, TRUE); + return; + } + } + $this->assertEquals($expectedIsActive, FALSE); + } + + /** + * @param $expectedIsActive + * @param $key + */ + public function assertModuleActiveByKey($expectedIsActive, $key) { + foreach (CRM_Core_Module::getAll() as $module) { + if ($module->name == $key) { + $this->assertEquals((bool) $expectedIsActive, (bool) $module->is_active); + return; + } + } + $this->assertEquals($expectedIsActive, FALSE); + } + + /** + * @param $key + * @param $type + * @param $file + * @param string $template + */ + public function _createExtension($key, $type, $file, $template = self::MODULE_TEMPLATE) { + $basedir = $this->basedir; + mkdir("$basedir/$key"); + $upgClass = 'CRM_' . preg_replace('/[^a-zA-Z0-9]/', '', $key) . '_Upgrader'; + $infoXmlStr = "$file$upgClass"; + $modulePhpStr = strtr($template, ['_FILE_' => $file, '_TEST_' => __CLASS__, '_UPGRADER_' => $upgClass]); + file_put_contents("$basedir/$key/info.xml", $infoXmlStr); + file_put_contents("$basedir/$key/$file.php", $modulePhpStr); + $system = CRM_Extension_System::singleton(); + $system->getCache()->flush(); + $system->getManager()->refresh(); + $this->system->getCache()->flush(); + $this->system->getManager()->refresh(); + CRM_Extension_System::setSingleton($this->system); + } + + /** + * @param $module + * @param string $name + */ + public static function incHookCount($module, $name) { + global $_test_extension_manager_moduleupgtest_counts; + if (!isset($_test_extension_manager_moduleupgtest_counts[$module][$name])) { + $_test_extension_manager_moduleupgtest_counts[$module][$name] = 0; + } + $_test_extension_manager_moduleupgtest_counts[$module][$name] = 1 + (int) $_test_extension_manager_moduleupgtest_counts[$module][$name]; + } + + const MODULE_TEMPLATE = "