Files
iTop/tests/php-unit-tests/unitary-tests/setup/ExtensionsMapTest.php
odain-cbd 95cb21cff7 N°9949 - Cannot uninstall IPAM from setup wizard screen (#1015)
* N°9949 - Cannot uninstall IPAM from setup wizard screen

* N°9949 - add test coverage

* N°9949 - handle list in compilation
2026-08-20 10:03:25 +02:00

353 lines
12 KiB
PHP

<?php
namespace Combodo\iTop\Test\UnitTest\Integration;
use Combodo\iTop\Test\UnitTest\ItopTestCase;
use CoreException;
use iTopExtension;
use ItopExtensionsMap;
use ModuleDiscovery;
class ExtensionsMapTest extends ItopTestCase
{
protected function setUp(): void
{
parent::setUp(); // TODO: Change the autogenerated stub
$this->RequireOnceItopFile('/setup/unattended-install/InstallationFileService.php');
ModuleDiscovery::ResetCache();
}
public function testGetAllExtensionsWithPreviouslyInstalledDoesNotCrash()
{
$oExtensionsMap = iTopExtensionsMap::GetExtensionsMap();
$aExtensions = $oExtensionsMap->GetAllExtensionsWithPreviouslyInstalled();
$this->assertGreaterThan(0, count($aExtensions));
}
public function testGetAllExtensionsToDisplayInSetup()
{
$oExtensionsMap = $this->GiveExtensionMapWithAllTypeOfExtensions();
$aExtensions = $oExtensionsMap->GetAllExtensionsToDisplayInSetup();
$expected = [
'installed_ext1',
'ext1',
];
$this->assertEquals($expected, array_keys($aExtensions));
}
public function testDeclareExtensionAsRemoved_WithDictPassed()
{
try {
$oExtensionsMap = $this->GiveExtensionMapWithAllTypeOfExtensions();
//non existing extension
$aExtensionCodes = ['unexisting_extension_code' => 'label_unexisting_extension_code'];
//extension ok
$aExtensionCodes ['installed_ext1'] = $oExtensionsMap->GetFromExtensionCode('installed_ext1')->sLabel;
//search extension by label ko
$sLabel = $oExtensionsMap->GetFromExtensionCode('installed_ext_in_package')->sLabel;
$aExtensionCodes [$sLabel] = $sLabel;
$oExtensionsMap->DeclareExtensionAsRemoved($aExtensionCodes);
$this->assertEquals(["installed_ext1"], ModuleDiscovery::GetRemovedExtensionCodes());
} finally {
ModuleDiscovery::DeclareRemovedExtensions([]);
}
}
public function testDeclareExtensionAsRemoved_WithListPassed()
{
try {
$oExtensionsMap = $this->GiveExtensionMapWithAllTypeOfExtensions();
$aExtensionCodes = ['unexisting_extension_code', 'installed_ext1'];
$aExtensionCodes [] = $oExtensionsMap->GetFromExtensionCode('installed_ext_in_package')->sLabel;
$oExtensionsMap->DeclareExtensionAsRemoved($aExtensionCodes);
$this->assertEquals(["installed_ext1"], ModuleDiscovery::GetRemovedExtensionCodes());
} finally {
ModuleDiscovery::DeclareRemovedExtensions([]);
}
}
public function testGetAllExtensionsToDisplayInSetup_WithExtensionsHavingDependencyIssues()
{
$oExtensionsMap = $this->GiveExtensionMapWithAllTypeOfExtensions();
$aExtensions = $oExtensionsMap->GetAllExtensionsToDisplayInSetup(true);
$expected = [
'installed_ext1',
'installed_ext_with_deps_issues',
'ext1',
'ext_with_deps_issues',
];
$this->assertEquals($expected, array_keys($aExtensions));
}
public function testGetAllExtensionsToDisplayInSetup_LegacyPackage()
{
$oExtensionsMap = $this->GiveExtensionMapWithAllTypeOfExtensions();
$this->SetNonPublicProperty($oExtensionsMap, 'bHasXmlInstallationFile', false);
$aExtensions = $oExtensionsMap->GetAllExtensionsToDisplayInSetup();
$expected = [
'installed_ext1',
'installed_ext_in_package',
'ext1',
'ext_in_package',
];
$this->assertEquals($expected, array_keys($aExtensions));
}
public function testGetAllExtensionsToDisplayInSetup_LegacyPackage_WithExtensionsHavingDependencyIssues()
{
$oExtensionsMap = $this->GiveExtensionMapWithAllTypeOfExtensions();
$this->SetNonPublicProperty($oExtensionsMap, 'bHasXmlInstallationFile', false);
$aExtensions = $oExtensionsMap->GetAllExtensionsToDisplayInSetup(true);
$expected = [
'installed_ext1',
'installed_ext_in_package',
'installed_ext_with_deps_issues',
'ext1',
'ext_in_package',
'ext_with_deps_issues',
];
$this->assertEquals($expected, array_keys($aExtensions));
}
private function GiveExtensionMapWithAllTypeOfExtensions(): iTopExtensionsMap
{
$oExtensionsMap = iTopExtensionsMap::GetExtensionsMap();
$this->SetNonPublicProperty($oExtensionsMap, 'aInstalledExtensions', []);
$this->SetNonPublicProperty($oExtensionsMap, 'aExtensions', []);
$this->AddExtension(
$oExtensionsMap,
$this->GivenExtension("installed_ext1", "123", true, iTopExtension::SOURCE_REMOTE, true, []),
'aInstalledExtensions'
);
$this->AddExtension(
$oExtensionsMap,
$this->GivenExtension("installed_notvisible", "123", false, iTopExtension::SOURCE_REMOTE, true, []),
'aInstalledExtensions'
);
$this->AddExtension(
$oExtensionsMap,
$this->GivenExtension("installed_ext_in_package", "123", true, iTopExtension::SOURCE_WIZARD, true, []),
'aInstalledExtensions'
);
$this->AddExtension(
$oExtensionsMap,
$this->GivenExtension("installed_ext_with_deps_issues", "123", true, iTopExtension::SOURCE_REMOTE, true, ["aa"]),
'aInstalledExtensions'
);
$this->AddExtension(
$oExtensionsMap,
$this->GivenExtension("ext1", "123", true, iTopExtension::SOURCE_REMOTE, true, []),
'aExtensions'
);
$this->AddExtension(
$oExtensionsMap,
$this->GivenExtension("notvisible", "123", false, iTopExtension::SOURCE_REMOTE, true, []),
'aExtensions'
);
$this->AddExtension(
$oExtensionsMap,
$this->GivenExtension("ext_in_package", "123", true, iTopExtension::SOURCE_WIZARD, true, []),
'aExtensions'
);
$this->AddExtension(
$oExtensionsMap,
$this->GivenExtension("ext_with_deps_issues", "123", true, iTopExtension::SOURCE_REMOTE, true, ["aa"]),
'aExtensions'
);
return $oExtensionsMap;
}
private function GivenExtension(string $sCode, string $sVersion, bool $bVisible, string $sSource, bool $bMandatory, array $aMissingDependencies = []): iTopExtension
{
$oExt = new iTopExtension();
$oExt->sCode = $sCode;
$oExt->sLabel = "LABEL_".$sCode;
$oExt->sVersion = $sVersion;
$oExt->bVisible = $bVisible;
$oExt->sSource = $sSource;
$oExt->aMissingDependencies = $aMissingDependencies;
$oExt->bMandatory = $bMandatory;
return $oExt;
}
private function AddExtension(iTopExtensionsMap $oExtensionsMap, iTopExtension $oExt, string $mapKeyInItopExtensionMap)
{
$this->InvokeNonPublicMethod(iTopExtensionsMap::class, 'AddExtension', $oExtensionsMap, [$oExt]);
$aMap = $this->GetNonPublicProperty($oExtensionsMap, $mapKeyInItopExtensionMap);
$aMap[$oExt->sCode.'/'.$oExt->sVersion] = $oExt;
$this->SetNonPublicProperty($oExtensionsMap, $mapKeyInItopExtensionMap, $aMap);
}
public function testiTopExtensionsMapInit()
{
$oiTopExtensionsMap = iTopExtensionsMap::GetExtensionsMap(sAppRootForTests: __DIR__."/ressources/");
$sExpected = file_get_contents(__DIR__.'/ressources/all_extensions_from_datamodels.json');
$sExpected = str_replace('"sVersion": "ITOP_VERSION"', '"sVersion": "'.ITOP_VERSION.'"', $sExpected);
$sExpected = preg_replace('/"module_file_path": .*/', '"module_file_path": ANYPATH', $sExpected);
$actual = json_encode($this->SerializeExtensionMap($oiTopExtensionsMap), JSON_PRETTY_PRINT);
$actual = preg_replace('/"module_file_path": .*/', '"module_file_path": ANYPATH', $actual);
$this->assertEquals($sExpected, $actual);
}
public function testGetScannedModulesRootDirs()
{
$sAppRootForTest = __DIR__."/resources2/";
$oiTopExtensionsMap = iTopExtensionsMap::GetExtensionsMap(sAppRootForTests: $sAppRootForTest);
$aExpectedDirs = [
"{$sAppRootForTest}datamodels/2.x",
"{$sAppRootForTest}extensions",
"{$sAppRootForTest}data/production-modules",
];
self::assertEquals($aExpectedDirs, $oiTopExtensionsMap->GetScannedModulesRootDirs());
}
public function testCheckExtensionsValidityReturnsFalseWhenSelectedExtensionCodeAndLabelAreMissing(): void
{
[$sEnvironment, $sExtensionsDirRelative] = $this->CreateFixtureContext('env-missing-label-');
$sInvalidExtensionXml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<extension format="1.0">
<description>Test extension without code and label</description>
<version>1.0.0</version>
<mandatory>false</mandatory>
<more_info_url/>
</extension>
XML;
$sExtensionsDirAbsolute = APPROOT.$sExtensionsDirRelative;
file_put_contents($sExtensionsDirAbsolute.'/extension.xml', $sInvalidExtensionXml);
$oExtensionMap = ItopExtensionsMap::GetExtensionsMap($sEnvironment);
//echo json_encode(array_keys($oExtensionMap->GetAllExtensions()), JSON_PRETTY_PRINT);
// $this->expectException(CoreException::class);
// $this->expectExceptionMessage("Selected extension(s) cannot be installed: Missing extension code ($sExtensionsDirAbsolute)");
self::assertFalse($oExtensionMap->CheckExtensionsValidity(), 'Check extensions validity should have detected an extension without code.');
}
public function testCheckExtensionsValidityReturnsFalseWhenSelectedExtensionCodeIsMissing(): void
{
[$sEnvironment, $sExtensionsDirRelative] = $this->CreateFixtureContext('env-missing-label-');
$sInvalidExtensionXml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<extension format="1.0">
<label>Broken extension</label>
<description>Test extension without code and label</description>
<version>1.0.0</version>
<mandatory>false</mandatory>
<more_info_url/>
</extension>
XML;
$sExtensionsDirAbsolute = APPROOT.$sExtensionsDirRelative;
file_put_contents($sExtensionsDirAbsolute.'/extension.xml', $sInvalidExtensionXml);
$oExtensionMap = ItopExtensionsMap::GetExtensionsMap($sEnvironment);
//echo json_encode(array_keys($oExtensionMap->GetAllExtensions()), JSON_PRETTY_PRINT);
// $this->expectException(CoreException::class);
// $this->expectExceptionMessage("Selected extension(s) cannot be installed: Missing extension code ($sExtensionsDirAbsolute)");
self::assertFalse($oExtensionMap->CheckExtensionsValidity(), 'Check extensions validity should have detected an extension without code.');
}
public function testCheckExtensionsValidityThrowExceptionWhenSelectedExtensionCodeAndLabelAreMissingAndExtensionIsChosen(): void
{
[$sEnvironment, $sExtensionsDirRelative] = $this->CreateFixtureContext('env-missing-label-');
$sInvalidExtensionXml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<extension format="1.0">
<description>Test extension without code and label</description>
<version>1.0.0</version>
<mandatory>false</mandatory>
<more_info_url/>
</extension>
XML;
$sExtensionsDirAbsolute = APPROOT.$sExtensionsDirRelative;
file_put_contents($sExtensionsDirAbsolute.'/extension.xml', $sInvalidExtensionXml);
$oExtensionMap = ItopExtensionsMap::GetExtensionsMap($sEnvironment);
$oExtensionMap->MarkAsChosen('');
//echo json_encode(array_keys($oExtensionMap->GetAllExtensions()), JSON_PRETTY_PRINT);
$this->expectException(CoreException::class);
$this->expectExceptionMessage("Selected extension(s) cannot be installed: Missing extension code ($sExtensionsDirAbsolute)");
$oExtensionMap->CheckExtensionsValidity();
}
public function testCheckExtensionsValidityThrowExceptionWhenSelectedExtensionCodeIsMissingAndExtensionIsChosen(): void
{
[$sEnvironment, $sExtensionsDirRelative] = $this->CreateFixtureContext('env-missing-label-');
$sInvalidExtensionXml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<extension format="1.0">
<label>Broken extension</label>
<description>Test extension without code and label</description>
<version>1.0.0</version>
<mandatory>false</mandatory>
<more_info_url/>
</extension>
XML;
$sExtensionsDirAbsolute = APPROOT.$sExtensionsDirRelative;
file_put_contents($sExtensionsDirAbsolute.'/extension.xml', $sInvalidExtensionXml);
$oExtensionMap = ItopExtensionsMap::GetExtensionsMap($sEnvironment);
$oExtensionMap->MarkAsChosen('');
//echo json_encode(array_keys($oExtensionMap->GetAllExtensions()), JSON_PRETTY_PRINT);
$this->expectException(CoreException::class);
$this->expectExceptionMessage("Selected extension(s) cannot be installed: Missing extension code (Broken extension)");
$oExtensionMap->CheckExtensionsValidity();
}
private function CreateFixtureContext(string $sEnvPrefix): array
{
$sEnvironment = str_replace('.', '-', uniqid($sEnvPrefix, true));
$sExtensionsDirRelative = 'data/'.$sEnvironment.'-modules';
mkdir(APPROOT.$sExtensionsDirRelative, 0777, true);
$this->aFileToClean[] = APPROOT.$sExtensionsDirRelative;
return [$sEnvironment, $sExtensionsDirRelative];
}
public function SerializeExtensionMap(iTopExtensionsMap $oiTopExtensionsMap): array
{
$aRes = [];
foreach ($oiTopExtensionsMap->GetAllExtensions() as $oExtension) {
$aRes[] = [
'sCode' => $oExtension->sCode,
'sSource' => $oExtension->sSource,
'sVersion' => $oExtension->sVersion,
'aModules' => $oExtension->aModules,
'aModuleVersion' => $oExtension->aModuleVersion,
'aModuleInfo' => $oExtension->aModuleInfo,
];
}
return $aRes;
}
}