N°8760 - setupaudit and dry removal API review with Romain - avoid file deletion

N°8760 - be able to simulate extension removal by oerriding GetExtensionMap

be able to simulate SetupAudit errors in Setups for integration tests

fix rebase
This commit is contained in:
odain
2025-11-03 17:33:17 +01:00
parent 9cdc707bc5
commit a2b01b3ed4
11 changed files with 289 additions and 217 deletions

View File

@@ -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])) {

View File

@@ -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)) {

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace Combodo\iTop\Setup\FeatureRemoval;
use iTopExtensionsMap;
use RunTimeEnvironment;
use SetupUtils;
class DryRemovalRuntimeEnvironment extends RunTimeEnvironment
{
const DRY_REMOVAL_AUDIT_ENV = "extension-removal";
protected array $aExtensionsByCode;
private bool $bExtensionMapModified = false;
/**
* Toolset for building a run-time environment
*
* @param string $sEnvironment (e.g. 'test')
* @param bool $bAutoCommit (make the target environment directly, or build a temporary one)
*/
public function __construct($sEnvironment = self::DRY_REMOVAL_AUDIT_ENV, $bAutoCommit = true)
{
parent::__construct($sEnvironment, $bAutoCommit);
$this->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();
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Combodo\iTop\Setup\FeatureRemoval;
use CoreException;
use Exception;
class ModelReflectionSerializer
{
private static ModelReflectionSerializer $oInstance;
protected function __construct()
{
}
final public static function GetInstance(): ModelReflectionSerializer
{
if (!isset(self::$oInstance)) {
self::$oInstance = new ModelReflectionSerializer();
}
return self::$oInstance;
}
final public static function SetInstance(?ModelReflectionSerializer $oInstance): void
{
self::$oInstance = $oInstance;
}
public function GetModelFromEnvironment(string $sEnv): array
{
\IssueLog::Info(__METHOD__, null, ['env' => $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;
}
}

View File

@@ -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);
}

View File

@@ -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(),

View File

@@ -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/<target-env>-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/<target-env>-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);

View File

@@ -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)
{

View File

@@ -0,0 +1,29 @@
<?php
namespace Combodo\iTop\Test\UnitTest\Setup\FeatureRemoval;
use Combodo\iTop\Setup\FeatureRemoval\ModelReflectionSerializer;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use MetaModel;
class ModelSerializationTest extends ItopDataTestCase
{
protected function setUp(): void
{
parent::setUp();
$this->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');
}
}

View File

@@ -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);
}
}