From a2b01b3ed4271df62ca337cd3db330be1665c61c Mon Sep 17 00:00:00 2001 From: odain Date: Mon, 3 Nov 2025 17:33:17 +0100 Subject: [PATCH] =?UTF-8?q?N=C2=B08760=20-=20setupaudit=20and=20dry=20remo?= =?UTF-8?q?val=20API=20review=20with=20Romain=20-=20avoid=20file=20deletio?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit N°8760 - be able to simulate extension removal by oerriding GetExtensionMap be able to simulate SetupAudit errors in Setups for integration tests fix rebase --- .../Service/RunTimeEnvironmentCoreUpdater.php | 2 +- setup/compiler.class.inc.php | 2 +- setup/extensionsmap.class.inc.php | 35 +++- .../DryRemovalRuntimeEnvironment.php | 66 ++++++++ .../ModelReflectionSerializer.php | 55 +++++++ setup/feature_removal/SetupAudit.php | 154 +++++------------- .../feature_removal/get_model_reflection.php | 4 +- setup/runtimeenv.class.inc.php | 42 +++-- setup/wizardsteps.class.inc.php | 7 +- .../ModelSerializationTest.php | 29 ++++ .../setup/feature_removal/SetupAuditTest.php | 110 ++++--------- 11 files changed, 289 insertions(+), 217 deletions(-) create mode 100644 setup/feature_removal/DryRemovalRuntimeEnvironment.php create mode 100644 setup/feature_removal/ModelReflectionSerializer.php create mode 100644 tests/php-unit-tests/unitary-tests/setup/feature_removal/ModelSerializationTest.php diff --git a/datamodels/2.x/itop-core-update/src/Service/RunTimeEnvironmentCoreUpdater.php b/datamodels/2.x/itop-core-update/src/Service/RunTimeEnvironmentCoreUpdater.php index dd5a6de40..8fd0b6d6a 100644 --- a/datamodels/2.x/itop-core-update/src/Service/RunTimeEnvironmentCoreUpdater.php +++ b/datamodels/2.x/itop-core-update/src/Service/RunTimeEnvironmentCoreUpdater.php @@ -135,7 +135,7 @@ class RunTimeEnvironmentCoreUpdater extends RunTimeEnvironment $aAvailableModules[$oModule->GetName()] = $oModule; } // TODO check the auto-selected modules here - foreach ($this->oExtensionsMap->GetAllExtensions() as $oExtension) { + foreach ($this->GetExtensionMap()->GetAllExtensions() as $oExtension) { if ($oExtension->bMarkedAsChosen) { foreach ($oExtension->aModules as $sModuleName) { if (!isset($aRet[$sModuleName]) && isset($aAvailableModules[$sModuleName])) { diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php index af7777266..38a86d45c 100644 --- a/setup/compiler.class.inc.php +++ b/setup/compiler.class.inc.php @@ -477,7 +477,7 @@ class MFCompiler $sClass = $oClass->getAttribute("id"); $aAllClasses[] = $sClass; try { - $sCompiledCode .= $this->CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir); + $sCompiledCode .= $this->CompileClass($oClass, $sModuleName, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir); } catch (DOMFormatException $e) { $sMessage = "Failed to process class '$sClass', "; if (!empty($sModuleRootDir)) { diff --git a/setup/extensionsmap.class.inc.php b/setup/extensionsmap.class.inc.php index 6b1a3e66e..8e35c86b9 100644 --- a/setup/extensionsmap.class.inc.php +++ b/setup/extensionsmap.class.inc.php @@ -148,14 +148,12 @@ class iTopExtensionsMap { /** * The list of all discovered extensions - * @param string $sFromEnvironment The environment to scan - * @param bool $bNormailizeOldExtension true to "magically" convert some well-known old extensions (i.e. a set of modules) to the new iTopExtension format - * @return void + * @var array $aExtensions */ protected $aExtensions; /** * The list of all currently installed extensions - * @var array + * @var array $aInstalledExtensions */ protected array $aInstalledExtensions; @@ -166,6 +164,12 @@ class iTopExtensionsMap */ protected $aScannedDirs; + /** + * The list of all discovered extensions + * @param string $sFromEnvironment The environment to scan + * @param bool $bNormailizeOldExtension true to "magically" convert some well-known old extensions (i.e. a set of modules) to the new iTopExtension format + * @return void + */ public function __construct($sFromEnvironment = 'production', $aExtraDirs = []) { $this->aExtensions = []; @@ -276,6 +280,21 @@ class iTopExtensionsMap $this->aExtensionsByCode[$oNewExtension->sCode] = $oNewExtension; } + public function RemoveExtension(string $sCode): void + { + $oExtension = $this->Get($sCode); + if (is_null($oExtension)) { + \IssueLog::Error(__METHOD__.": cannot find extension to remove", null, [$sCode]); + + return; + } + + \IssueLog::Debug(__METHOD__.": remove extension from map", null, [$oExtension->sCode => $oExtension->sSourceDir]); + + unset($this->aExtensions[$oExtension->sCode.'/'.$oExtension->sVersion]); + unset($this->aExtensionsByCode[$sCode]); + } + /** * @since 3.3.0 * @param string $sExtensionCode @@ -287,7 +306,7 @@ class iTopExtensionsMap return $this->aExtensionsByCode[$sExtensionCode] ?? null; } - public function GetMissingExtensions(array $aSelectedExtensions) + /*public function GetMissingExtensions(array $aSelectedExtensions) { \SetupLog::Info(__METHOD__, null, ['selected' => $aSelectedExtensions]); $aExtensionsFromDb = array_keys($this->aExtensionsByCode); @@ -308,8 +327,7 @@ class iTopExtensionsMap \SetupLog::Info(__METHOD__, null, $aRes); return $aRes; - } - + }*/ /** * Read (recursively) a directory to find if it contains extensions (or modules) @@ -494,7 +512,6 @@ class iTopExtensionsMap } } - public function MarkAsUninstallable($sExtensionCode, $bMark = true) { $oExtension = $this->Get($sExtensionCode); @@ -624,4 +641,4 @@ class iTopExtensionsMap return false; } -} \ No newline at end of file +} diff --git a/setup/feature_removal/DryRemovalRuntimeEnvironment.php b/setup/feature_removal/DryRemovalRuntimeEnvironment.php new file mode 100644 index 000000000..1b3bda65d --- /dev/null +++ b/setup/feature_removal/DryRemovalRuntimeEnvironment.php @@ -0,0 +1,66 @@ +aExtensionsByCode = []; + } + + /** + * @param string $sSourceEnv + * @param array $aExtensionCodesToRemove + * + * @return void + * @throws \Exception + */ + public function Prepare(string $sSourceEnv, array $aExtensionCodesToRemove) + { + + $sEnv = $this->sFinalEnv; + $this->aExtensionsByCode = $aExtensionCodesToRemove; + SetupUtils::rrmdir(APPROOT."/data/$sEnv-modules"); + SetupUtils::copydir(APPROOT."/data/$sSourceEnv-modules", APPROOT."/data/".$this->sFinalEnv."-modules"); + + /*$oDryRemovalConfig = clone(MetaModel::GetConfig()); + $oDryRemovalConfig->ChangeModulesPath($sSourceEnv, $this->sFinalEnv); + $this->WriteConfigFileSafe($oDryRemovalConfig);*/ + } + + /** + * @return \iTopExtensionsMap|null + */ + protected function GetExtensionMap(): ?iTopExtensionsMap + { + if (is_null(parent::GetExtensionMap())) { + return null; + } + + if (!$this->bExtensionMapModified) { + $this->bExtensionMapModified = true; + foreach ($this->aExtensionsByCode as $sCode) { + parent::GetExtensionMap()->RemoveExtension($sCode); + } + } + + return parent::GetExtensionMap(); + } +} \ No newline at end of file diff --git a/setup/feature_removal/ModelReflectionSerializer.php b/setup/feature_removal/ModelReflectionSerializer.php new file mode 100644 index 000000000..422b3acc4 --- /dev/null +++ b/setup/feature_removal/ModelReflectionSerializer.php @@ -0,0 +1,55 @@ + $sEnv]); + $sPHPExec = trim(\MetaModel::GetConfig()->Get('php_path')); + $sOutput = ""; + $iRes = 0; + exec(sprintf("$sPHPExec %s/get_model_reflection.php --env='%s'", __DIR__, $sEnv), $sOutput, $iRes); + if ($iRes != 0) { + \IssueLog::Error("Cannot get classes", null, ['code' => $iRes, "output" => $sOutput]); + throw new CoreException("Cannot get classes"); + } + + $aClasses = json_decode($sOutput[0] ?? null, true); + if (false === $aClasses) { + \IssueLog::Error("Invalid JSON", null, ["output" => $sOutput]); + throw new Exception("cannot get classes"); + } + + if (!is_array($aClasses)) { + \IssueLog::Error("not an array", null, ["classes" => $aClasses]); + throw new Exception("cannot get classes"); + } + + return $aClasses; + } +} \ No newline at end of file diff --git a/setup/feature_removal/SetupAudit.php b/setup/feature_removal/SetupAudit.php index 4104e94cb..10734313b 100644 --- a/setup/feature_removal/SetupAudit.php +++ b/setup/feature_removal/SetupAudit.php @@ -2,35 +2,48 @@ namespace Combodo\iTop\Setup\FeatureRemoval; -use Config; -use CoreException; use DBObjectSearch; use DBObjectSet; -use Exception; use MetaModel; -use RunTimeEnvironment; -use SetupUtils; + +require_once APPROOT.'setup/feature_removal/ModelReflectionSerializer.php'; class SetupAudit { - const DRY_REMOVAL_AUDIT_ENV = "extension-removal"; + //file used when present to trigger audit exception when testing specific setups + const GETISSUE_ERROR_MSG_FILE_FORTESTONLY = '.setup_audit_error_msg.txt'; + + private string $sEnvBeforeExtensionRemoval; + private string $sEnvAfterExtensionRemoval; private array $aClassesBeforeRemoval; private array $aClassesAfterRemoval; - private array $aExtensionToRemove; private array $aRemovedClasses; private array $aFinalClassesRemoved; - public function __construct() + public function __construct(string $sEnvBeforeExtensionRemoval, string $sEnvAfterExtensionRemoval = DryRemovalRuntimeEnvironment::DRY_REMOVAL_AUDIT_ENV) { - $this->aExtensionToRemove = []; - $this->aClassesBeforeRemoval = []; - $this->aClassesAfterRemoval = []; + $this->sEnvBeforeExtensionRemoval = $sEnvBeforeExtensionRemoval; + $this->sEnvAfterExtensionRemoval = $sEnvAfterExtensionRemoval; + + $sCurrentEnvt = MetaModel::GetEnvironment(); + if ($sCurrentEnvt === $this->sEnvBeforeExtensionRemoval) { + $this->aClassesBeforeRemoval = MetaModel::GetClasses(); + } else { + $this->aClassesBeforeRemoval = ModelReflectionSerializer::GetInstance()->GetModelFromEnvironment($this->sEnvBeforeExtensionRemoval); + } + + if ($sCurrentEnvt === $this->sEnvAfterExtensionRemoval) { + $this->aClassesAfterRemoval = MetaModel::GetClasses(); + } else { + $this->aClassesAfterRemoval = ModelReflectionSerializer::GetInstance()->GetModelFromEnvironment($this->sEnvAfterExtensionRemoval); + } + $this->aRemovedClasses = []; $this->aFinalClassesRemoved = []; } - public function SetSelectedExtensions(Config $oConfig, array $aSelectedExtensions) + /*public function SetSelectedExtensions(Config $oConfig, array $aSelectedExtensions) { $oExtensionsMap = new \iTopExtensionsMap(); $oExtensionsMap->LoadChoicesFromDatabase($oConfig); @@ -39,103 +52,7 @@ class SetupAudit $this->aExtensionToRemove = $oExtensionsMap->GetMissingExtensions($aSelectedExtensions); sort($this->aExtensionToRemove); \SetupLog::Info(__METHOD__, null, ['aExtensionToRemove' => $this->aExtensionToRemove]); - } - - public function ComputeClassesBeforeRemoval(string $sTargetEnv) - { - $this->aClassesBeforeRemoval = $this->GetModelFromEnvironment($sTargetEnv); - } - - public function SetClassesAfterRemovalFromCurrentEnv() - { - $this->aClassesAfterRemoval = MetaModel::GetClasses(); - } - - public function SetClassesBeforeRemovalFromCurrentEnv() - { - $this->aClassesBeforeRemoval = MetaModel::GetClasses(); - } - - public function ComputeDryExtensionRemoval(array $aExtensionToRemove): void - { - $this->aExtensionToRemove = $aExtensionToRemove; - - if (count($this->aExtensionToRemove) == 0) { - //avoid time consuming setup audit when no extension removed - return; - } - - $sDryRemovalEnv = self::DRY_REMOVAL_AUDIT_ENV; - self::Cleanup($sDryRemovalEnv); - - $sSourceEnvt = MetaModel::GetEnvironment(); - - $oDryRemovalRuntimeEnvt = new RunTimeEnvironment($sDryRemovalEnv); - $oDryRemovalConfig = clone(MetaModel::GetConfig()); - $oDryRemovalConfig->ChangeModulesPath($sSourceEnvt, $sDryRemovalEnv); - - $oDryRemovalRuntimeEnvt->WriteConfigFileSafe($oDryRemovalConfig); - SetupUtils::copydir(APPROOT."/data/$sSourceEnvt-modules", APPROOT."/data/$sDryRemovalEnv-modules"); - $this->RemoveExtensionsLocally($sDryRemovalEnv, $this->aExtensionToRemove); - - $oDryRemovalRuntimeEnvt->CompileFrom($sSourceEnvt); - - $this->aClassesAfterRemoval = $this->GetModelFromEnvironment($sDryRemovalEnv); - - $oDryRemovalRuntimeEnvt->Rollback(); - self::Cleanup($sDryRemovalEnv); - } - - public static function Cleanup(string $sEnv) - { - SetupUtils::rrmdir(APPROOT."/data/$sEnv-modules"); - SetupUtils::rrmdir(APPROOT."/data/cache-$sEnv"); - SetupUtils::rrmdir(APPROOT."/env-$sEnv"); - SetupUtils::rrmdir(APPROOT."/conf/$sEnv"); - @unlink(APPROOT."/data/datamodel-$sEnv.xml"); - } - - public function GetModelFromEnvironment(string $sEnv): array - { - $sPHPExec = trim(\MetaModel::GetConfig()->Get('php_path')); - $sOutput = ""; - $iRes = 0; - exec(sprintf("$sPHPExec %s/get_model_reflection.php --env='%s'", __DIR__, $sEnv), $sOutput, $iRes); - if ($iRes != 0) { - \IssueLog::Error("Cannot get classes", null, ['code' => $iRes, "output" => $sOutput]); - throw new CoreException("Cannot get classes"); - } - - $aClasses = json_decode($sOutput[0] ?? null, true); - if (false === $aClasses) { - \IssueLog::Error("Invalid JSON", null, ["output" => $sOutput]); - throw new Exception("cannot get classes"); - } - - if (!is_array($aClasses)) { - \IssueLog::Error("not an array", null, ["classes" => $aClasses]); - throw new Exception("cannot get classes"); - } - - return $aClasses; - } - - private function RemoveExtensionsLocally(string $sTargetEnv, array $aExtensionCodes): void - { - $oExtensionsMap = new \iTopExtensionsMap($sTargetEnv); - - foreach ($aExtensionCodes as $sCode) { - /** @var \iTopExtension $oExtension */ - $oExtension = $oExtensionsMap->Get($sCode); - if (!is_null($oExtension)) { - $sDir = $oExtension->sSourceDir; - \IssueLog::Info(__METHOD__.": remove extension locally", null, [$oExtension->sCode => $sDir]); - SetupUtils::rrmdir($sDir); - } else { - \IssueLog::Warning(__METHOD__." cannot find extensions", null, ['env' => $sTargetEnv, 'code' => $sCode]); - } - } - } + }*/ public function GetRemovedClasses(): array { @@ -161,8 +78,23 @@ class SetupAudit return $this->aRemovedClasses; } - public function AuditExtensionsCleanupRules(bool $bStopAtFirstIssue = false): array + /** test only: return file path that force audit error being raised + * + * @return string + */ + public static function GetErrorMessageFilePathForTestOnly(): string { + return APPROOT."/data/".self::GETISSUE_ERROR_MSG_FILE_FORTESTONLY; + } + + public function GetIssues(bool $bThrowExceptionAtFirstIssue = false): array + { + $sErrorMessageFilePath = self::GetErrorMessageFilePathForTestOnly(); + if ($bThrowExceptionAtFirstIssue && is_file($sErrorMessageFilePath)) { + $sMsg = file_get_contents($sErrorMessageFilePath); + throw new \Exception($sMsg); + } + $this->aFinalClassesRemoved = []; foreach ($this->GetRemovedClasses() as $sClass) { @@ -173,7 +105,7 @@ class SetupAudit if (!MetaModel::IsStandaloneClass($sClass)) { $iCount = $this->Count($sClass); $this->aFinalClassesRemoved[$sClass] = $iCount; - if ($bStopAtFirstIssue && $iCount > 0) { + if ($bThrowExceptionAtFirstIssue && $iCount > 0) { //setup envt: should raise issue ASAP throw new \Exception($sClass); } diff --git a/setup/feature_removal/get_model_reflection.php b/setup/feature_removal/get_model_reflection.php index 051b7b61e..073553519 100644 --- a/setup/feature_removal/get_model_reflection.php +++ b/setup/feature_removal/get_model_reflection.php @@ -24,7 +24,9 @@ try { MetaModel::Startup($sConfFile, false /* $bModelOnly */, true /* $bAllowCache */, false /* $bTraceSourceFiles */, $sEnv); } catch (\Throwable $e) { - \IssueLog::Error("Cannot read model from provided environment", null, + echo $e->getMessage(); + echo $e->getTraceAsString(); + \SetupLog::Error("Cannot read model from provided environment", null, [ 'env' => $sEnv, 'error' => $e->getMessage(), diff --git a/setup/runtimeenv.class.inc.php b/setup/runtimeenv.class.inc.php index 200e5faa3..658db72d7 100644 --- a/setup/runtimeenv.class.inc.php +++ b/setup/runtimeenv.class.inc.php @@ -62,7 +62,23 @@ class RunTimeEnvironment * Extensions map of the source environment * @var iTopExtensionsMap */ - protected $oExtensionsMap; + protected ?iTopExtensionsMap $oExtensionsMap; + + protected function GetExtensionMap(): ?iTopExtensionsMap + { + return $this->oExtensionsMap; + } + + public function InitExtensionMap($aExtraDirs, $oSourceConfig) + { + // Actually read the modules available for the target environment, + // but get the selection from the source environment and finally + // mark as (automatically) chosen alll the "remote" modules present in the + // target environment (data/-modules) + // The actual choices will be recorded by RecordInstallation below + $this->oExtensionsMap = new iTopExtensionsMap($this->sTargetEnv, true, $aExtraDirs); + $this->oExtensionsMap->LoadChoicesFromDatabase($oSourceConfig); + } /** * Toolset for building a run-time environment @@ -241,20 +257,22 @@ class RunTimeEnvironment // Actually read the modules available for the target environment, // but get the selection from the source environment and finally - // mark as (automatically) chosen alll the "remote" modules present in the + // mark as (automatically) chosen all the "remote" modules present in the // target environment (data/-modules) // The actual choices will be recorded by RecordInstallation below - $this->oExtensionsMap = new iTopExtensionsMap($this->sTargetEnv, $aExtraDirs); - $this->oExtensionsMap->LoadChoicesFromDatabase($oSourceConfig); - foreach ($this->oExtensionsMap->GetAllExtensions() as $oExtension) { + $this->InitExtensionMap($aExtraDirs, $oSourceConfig); + $this->GetExtensionMap()->LoadChoicesFromDatabase($oSourceConfig); + foreach ($this->GetExtensionMap()->GetAllExtensions() as $oExtension) { if ($this->IsExtensionSelected($oExtension)) { - $this->oExtensionsMap->MarkAsChosen($oExtension->sCode); + $this->GetExtensionMap()->MarkAsChosen($oExtension->sCode); } } // Do load the required modules // $oDictModule = new MFDictModule('dictionaries', 'iTop Dictionaries', APPROOT.'dictionaries'); + + $aRet = []; $aRet[$oDictModule->GetName()] = $oDictModule; $oFactory = new ModelFactory($aDirsToCompile); @@ -273,7 +291,7 @@ class RunTimeEnvironment foreach ($aModules as $oModule) { $sModule = $oModule->GetName(); $sModuleRootDir = $oModule->GetRootDir(); - $bIsExtra = $this->oExtensionsMap->ModuleIsChosenAsPartOfAnExtension($sModule, iTopExtension::SOURCE_REMOTE); + $bIsExtra = $this->GetExtensionMap()->ModuleIsChosenAsPartOfAnExtension($sModule, iTopExtension::SOURCE_REMOTE); if (array_key_exists($sModule, $aAvailableModules)) { if (($aAvailableModules[$sModule]['installed_version'] != '') || $bIsExtra && !$oModule->IsAutoSelect()) { //Extra modules are always unless they are 'AutoSelect' $aRet[$oModule->GetName()] = $oModule; @@ -532,7 +550,6 @@ class RunTimeEnvironment // Record installed modules and extensions // - $aAvailableExtensions = []; $aAvailableModules = $this->AnalyzeInstallation($oConfig, $this->GetBuildDir()); foreach ($aSelectedModuleCodes as $sModuleId) { if (!array_key_exists($sModuleId, $aAvailableModules)) { @@ -573,16 +590,17 @@ class RunTimeEnvironment $oInstallRec->DBInsertNoReload(); } - if ($this->oExtensionsMap) { + if ($this->GetExtensionMap()) { // Mark as chosen the selected extensions code passed to us + // Note: some other extensions may already be marked as chosen - foreach ($this->oExtensionsMap->GetAllExtensions() as $oExtension) { + foreach ($this->GetExtensionMap()->GetAllExtensions() as $oExtension) { if (in_array($oExtension->sCode, $aSelectedExtensionCodes)) { - $this->oExtensionsMap->MarkAsChosen($oExtension->sCode); + $this->GetExtensionMap()->MarkAsChosen($oExtension->sCode); } } - foreach ($this->oExtensionsMap->GetChoices() as $oExtension) { + foreach ($this->GetExtensionMap()->GetChoices() as $oExtension) { $oInstallRec = new ExtensionInstallation(); $oInstallRec->Set('code', $oExtension->sCode); $oInstallRec->Set('label', $oExtension->sLabel); diff --git a/setup/wizardsteps.class.inc.php b/setup/wizardsteps.class.inc.php index 9e016b6f9..021953811 100644 --- a/setup/wizardsteps.class.inc.php +++ b/setup/wizardsteps.class.inc.php @@ -42,7 +42,6 @@ use Combodo\iTop\Application\WebPage\WebPage; use Combodo\iTop\PhpParser\Evaluation\PhpExpressionEvaluator; -use Combodo\iTop\Setup\FeatureRemoval\SetupAudit; use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReaderException; require_once(APPROOT.'setup/setuputils.class.inc.php'); @@ -2117,8 +2116,8 @@ class WizStepSummary extends WizardStep try { SetupUtils::AnalyzeInstallation($this->oWizard, true, $aSelectedModules); - $sInstallMode = utils::ReadParam('install_mode'); - \SetupLog::Info(__METHOD__, null, ['install_mode' => $sInstallMode]); + /*$sInstallMode = utils::ReadParam('install_mode'); + \SetupLog::Info(__METHOD__, null, ['$sInstallMode' => $sInstallMode]); //if ($sInstallMode === "upgrade") { $aExtensions = json_decode($this->oWizard->GetParameter('selected_extensions'), true); $oSetupAudit = new SetupAudit([]); @@ -2127,7 +2126,7 @@ class WizStepSummary extends WizardStep $oSetupAudit->SetSelectedExtensions($oConfig, $aExtensions); //$oSetupAudit->AuditExtensionsCleanupRules(true); //} - + */ } catch(MissingDependencyException $e) { diff --git a/tests/php-unit-tests/unitary-tests/setup/feature_removal/ModelSerializationTest.php b/tests/php-unit-tests/unitary-tests/setup/feature_removal/ModelSerializationTest.php new file mode 100644 index 000000000..a9926fea6 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/setup/feature_removal/ModelSerializationTest.php @@ -0,0 +1,29 @@ +RequireOnceItopFile('/setup/feature_removal/ModelReflectionSerializer.php'); + } + + public function testGetModelFromEnvironment() + { + $aModel = ModelReflectionSerializer::GetInstance()->GetModelFromEnvironment($this->GetTestEnvironment()); + $this->assertEqualsCanonicalizing(MetaModel::GetClasses(), $aModel); + } + + public function testGetModelFromEnvironmentFailure() + { + $this->expectException(\CoreException::class); + $this->expectExceptionMessage("Cannot get classes"); + ModelReflectionSerializer::GetInstance()->GetModelFromEnvironment('gabuzomeu'); + } +} \ No newline at end of file diff --git a/tests/php-unit-tests/unitary-tests/setup/feature_removal/SetupAuditTest.php b/tests/php-unit-tests/unitary-tests/setup/feature_removal/SetupAuditTest.php index e0863f242..4e77eafbb 100644 --- a/tests/php-unit-tests/unitary-tests/setup/feature_removal/SetupAuditTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/feature_removal/SetupAuditTest.php @@ -2,6 +2,7 @@ namespace Combodo\iTop\Test\UnitTest\Setup\FeatureRemoval; +use Combodo\iTop\Setup\FeatureRemoval\DryRemovalRuntimeEnvironment; use Combodo\iTop\Setup\FeatureRemoval\SetupAudit; use Combodo\iTop\Test\UnitTest\ItopCustomDatamodelTestCase; use Combodo\iTop\Test\UnitTest\Service\UnitTestRunTimeEnvironment; @@ -11,48 +12,6 @@ class SetupAuditTest extends ItopCustomDatamodelTestCase { const ENVT = 'php-unit-extensionremoval-tests'; - protected function setUp(): void - { - static::LoadRequiredItopFiles(); - $this->oEnvironment = new UnitTestRunTimeEnvironment(self::ENVT); - $this->oEnvironment->bUseDelta = false; - $this->oEnvironment->bUseAdditionalFeatures = true; - parent::setUp(); - - $this->RequireOnceItopFile('/setup/feature_removal/SetupAudit.php'); - } - - public function GetTestEnvironment(): string - { - return self::ENVT; - } - - public function testGetModelFromEnvironment() - { - $oSetupAudit = new SetupAudit([]); - - $aExpected = \MetaModel::GetClasses(); - sort($aExpected); - - $aModel = $oSetupAudit->GetModelFromEnvironment($this->GetTestEnvironment()); - sort($aModel); - $this->assertEquals($aExpected, $aModel); - } - - public function testGetModelFromEnvironmentFailure() - { - $oSetupAudit = new SetupAudit([]); - - $aExpected = \MetaModel::GetClasses(); - sort($aExpected); - - $this->expectException(\CoreException::class); - $this->expectExceptionMessage("Cannot get classes"); - $aModel = $oSetupAudit->GetModelFromEnvironment('gabuzomeu'); - sort($aModel); - $this->assertEquals($aExpected, $aModel); - } - public function GetDatamodelDeltaAbsPath(): string { //no delta: empty path provided @@ -70,51 +29,51 @@ class SetupAuditTest extends ItopCustomDatamodelTestCase return $aFeaturePaths; } + protected function setUp(): void + { + static::LoadRequiredItopFiles(); + $this->oEnvironment = new UnitTestRunTimeEnvironment(self::ENVT); + $this->oEnvironment->bUseDelta = false; + $this->oEnvironment->bUseAdditionalFeatures = true; + parent::setUp(); + + $this->RequireOnceItopFile('/setup/feature_removal/SetupAudit.php'); + $this->RequireOnceItopFile('/setup/feature_removal/DryRemovalRuntimeEnvironment.php'); + } + + public function GetTestEnvironment(): string + { + return self::ENVT; + } + public function testComputeDryRemoval() { - $oSetupAudit = new SetupAudit(); - $oSetupAudit->SetClassesBeforeRemovalFromCurrentEnv(); - $oSetupAudit->ComputeDryExtensionRemoval(['nominal_ext1', 'finalclass_ext2']); - $aRemovedClasses = $oSetupAudit->GetRemovedClasses(); - sort($aRemovedClasses); + $oDryRemovalRuntimeEnvt = new DryRemovalRuntimeEnvironment(); + $oDryRemovalRuntimeEnvt->Prepare($this->GetTestEnvironment(), ['nominal_ext1', 'finalclass_ext2']); + $oDryRemovalRuntimeEnvt->CompileFrom($this->GetTestEnvironment()); + + $oSetupAudit = new SetupAudit(\MetaModel::GetEnvironment()); + $expected = [ "Feature1Module1MyClass", "FinalClassFeature2Module1MyClass", "FinalClassFeature2Module1MyFinalClassFromLocation", ]; + $this->assertEqualsCanonicalizing($expected, $oSetupAudit->GetRemovedClasses()); - sort($expected); - $this->assertEquals($expected, $aRemovedClasses); - } - - public function testComputeMTPWay() - { - $oSetupAudit = new SetupAudit(); - $oSetupAudit->ComputeClassesBeforeRemoval('production'); - $oSetupAudit->SetClassesAfterRemovalFromCurrentEnv(); - $oSetupAudit->AuditExtensionsCleanupRules(true); - - $oSetupAudit->SetClassesBeforeRemovalFromCurrentEnv(); - $oSetupAudit->ComputeDryExtensionRemoval(['nominal_ext1', 'finalclass_ext2']); - $aRemovedClasses = $oSetupAudit->GetRemovedClasses(); - sort($aRemovedClasses); $expected = [ - "Feature1Module1MyClass", - "FinalClassFeature2Module1MyClass", - "FinalClassFeature2Module1MyFinalClassFromLocation", + "FinalClassFeature2Module1MyFinalClassFromLocation" => 0, ]; - - sort($expected); - $this->assertEquals($expected, $aRemovedClasses); + $this->assertEqualsCanonicalizing($expected, $oSetupAudit->GetIssues()); } - public function testAuditExtensionsCleanupRules() + public function testGetIssues() { $sUID = "AuditExtensionsCleanupRules_".uniqid(); $oOrg = $this->CreateOrganization($sUID); $this->createObject('FinalClassFeature1Module1MyFinalClassFromLocation', ['org_id' => $oOrg->GetKey(), 'name' => $sUID, 'name2' => uniqid()]); - $oSetupAudit = new SetupAudit(); + $oSetupAudit = new SetupAudit(\MetaModel::GetEnvironment()); $aRemovedClasses = [ "Feature1Module1MyClass", "FinalClassFeature1Module1MyClass", @@ -126,16 +85,11 @@ class SetupAuditTest extends ItopCustomDatamodelTestCase //avoid setup dry computation $this->SetNonPublicProperty($oSetupAudit, 'aRemovedClasses', $aRemovedClasses); - $oRules = $oSetupAudit->AuditExtensionsCleanupRules(); - asort($oRules); - $expected = [ "FinalClassFeature1Module1MyFinalClassFromLocation" => 1, "FinalClassFeature2Module1MyFinalClassFromLocation" => 0, ]; - - asort($expected); - $this->assertEquals($expected, $oRules); + $this->assertEqualsCanonicalizing($expected, $oSetupAudit->GetIssues()); } public function testAuditExtensionsCleanupRulesFailASAP() @@ -145,7 +99,7 @@ class SetupAuditTest extends ItopCustomDatamodelTestCase $this->createObject('FinalClassFeature1Module1MyFinalClassFromLocation', ['org_id' => $oOrg->GetKey(), 'name' => $sUID, 'name2' => uniqid()]); $this->createObject('FinalClassFeature2Module1MyFinalClassFromLocation', ['org_id' => $oOrg->GetKey(), 'name' => $sUID, 'name2' => uniqid()]); - $oSetupAudit = new SetupAudit(['nominal_ext1', 'finalclass_ext1', 'finalclass_ext2']); + $oSetupAudit = new SetupAudit(\MetaModel::GetEnvironment()); $aRemovedClasses = [ "Feature1Module1MyClass", "FinalClassFeature1Module1MyClass", @@ -159,6 +113,6 @@ class SetupAuditTest extends ItopCustomDatamodelTestCase $this->expectException(Exception::class); $this->expectExceptionMessage('FinalClassFeature1Module1MyFinalClassFromLocation'); - $oSetupAudit->AuditExtensionsCleanupRules(true); + $oSetupAudit->GetIssues(true); } } \ No newline at end of file