mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 18:48:51 +02:00
Merge remote-tracking branch 'origin/support/3.2' into develop
This commit is contained in:
@@ -501,4 +501,26 @@ abstract class ItopTestCase extends TestCase
|
||||
}
|
||||
closedir($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* An alternative to assertEquals in case the order of the elements in the array is not important
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected function AssertArraysHaveSameItems(array $aExpectedClasses, array $aClasses, string $sMessage = ''): void
|
||||
{
|
||||
sort($aClasses);
|
||||
sort($aExpectedClasses);
|
||||
|
||||
$sExpected = implode("\n", $aExpectedClasses);
|
||||
$sActual = implode("\n", $aClasses);
|
||||
if ($sExpected === $sActual) {
|
||||
$this->assertTrue(true);
|
||||
return;
|
||||
}
|
||||
$sMessage .= "\nExpected:\n$sExpected\nActual:\n$sActual";
|
||||
var_export($aClasses);
|
||||
|
||||
$this->fail($sMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Application;
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\iUIBlockFactory;
|
||||
use Combodo\iTop\Service\InterfaceDiscovery\InterfaceDiscovery;
|
||||
use Combodo\iTop\Test\UnitTest\ItopCustomDatamodelTestCase;
|
||||
use MetaModel;
|
||||
use utils;
|
||||
@@ -44,7 +46,7 @@ class ApplicationExtensionTest extends ItopCustomDatamodelTestCase
|
||||
if ($sCallMethod === static::ENUM_API_CALL_METHOD_ENUMPLUGINS) {
|
||||
$iExtendingClassesCount = count(MetaModel::EnumPlugins($sAPIFQCN));
|
||||
} else {
|
||||
$iExtendingClassesCount = count(utils::GetClassesForInterface($sAPIFQCN, '', ['[\\\\/]lib[\\\\/]', '[\\\\/]node_modules[\\\\/]', '[\\\\/]test[\\\\/]', '[\\\\/]tests[\\\\/]']));
|
||||
$iExtendingClassesCount = count(InterfaceDiscovery::GetInstance()->FindItopClasses($sAPIFQCN));
|
||||
}
|
||||
$this->assertGreaterThan(0, $iExtendingClassesCount, "Found no class extending the $sAPIFQCN API");
|
||||
}
|
||||
|
||||
@@ -219,14 +219,18 @@ HTML
|
||||
'simple-body-with-placeholder' => [
|
||||
'EN US',
|
||||
['body' => '<p>Ticket "$this->title$" created.</p>'],
|
||||
['body' => '<p>Ticket "Test UserRequest" created.</p>'],
|
||||
['body' => '<div class="email-is-html-content">
|
||||
<p>Ticket "Test UserRequest" created.</p>
|
||||
</div>'],
|
||||
],
|
||||
'simple-body-with-placeholder-TEST-mode' => [
|
||||
'EN US',
|
||||
['body' => '<p>Ticket "$this->title$" created.</p>', 'status' => 'test'],
|
||||
['body' =>
|
||||
<<<HTML
|
||||
<p>Ticket "Test UserRequest" created.</p><div style="border: dashed;">
|
||||
<div class="email-is-html-content">
|
||||
<p>Ticket "Test UserRequest" created.</p>
|
||||
</div><div style="border: dashed;">
|
||||
<h1>Testing email notification <span class="object-ref " title="****"><a class="object-ref-link" href="****">Test action</a></span></h1>
|
||||
<p>The email should be sent with the following properties
|
||||
<ul>
|
||||
@@ -260,7 +264,9 @@ HTML
|
||||
<body>
|
||||
<table data-something-that-would-be-removed-by-the-sanitizer-through-ckeditor-but-that-will-stay-with-the-template="bar">
|
||||
<tr><td>Formatted eMail</td></tr>
|
||||
<tr><td><p>Ticket "Test UserRequest" created.</p></td></tr>
|
||||
<tr><td><div class="email-is-html-content">
|
||||
<p>Ticket "Test UserRequest" created.</p>
|
||||
</div></td></tr>
|
||||
</body>
|
||||
HTML
|
||||
],
|
||||
|
||||
@@ -241,4 +241,30 @@ SQL
|
||||
$this->assertEquals('from table 2', $sFromTable2Data, "Data was not moved as expected");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the table has been renamed
|
||||
*
|
||||
* @covers ModuleInstallerAPI::RenameTableInDB
|
||||
*
|
||||
* @return void
|
||||
* @throws \CoreException
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function testRenameTableInDB()
|
||||
{
|
||||
$sOrigTable = MetaModel::DBGetTable('Person');
|
||||
$aOrigTableInfo = CMDBSource::GetTableInfo($sOrigTable);
|
||||
$this->assertNotEmpty($aOrigTableInfo, 'Origin table does not exist');
|
||||
|
||||
$sDstTable = static::$sWorkTable;
|
||||
$this->assertFalse(CMDBSource::IsTable($sDstTable), 'Work table already exists');
|
||||
|
||||
ModuleInstallerAPI::RenameTableInDB($sOrigTable, $sDstTable);
|
||||
|
||||
$this->assertEquals($aOrigTableInfo, CMDBSource::GetTableInfo($sDstTable), 'Table was not renamed');
|
||||
|
||||
// Revert
|
||||
ModuleInstallerAPI::RenameTableInDB($sDstTable, $sOrigTable);
|
||||
$this->assertEquals($aOrigTableInfo, CMDBSource::GetTableInfo($sOrigTable));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,4 +32,318 @@ class CKEditorHelperTest extends ItopTestCase
|
||||
'GetJSFilesRelPathsForCKEditor' => ['GetJSFilesRelPathsForCKEditor'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider DOMSanitizerForCKEditorProvider
|
||||
*
|
||||
* @param $aSanitizerConfiguration
|
||||
* @param $aExpectedCKEditorConfiguration
|
||||
*
|
||||
* @return void
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function testDOMSanitizerForCKEditor($aSanitizerConfiguration, $aExpectedCKEditorConfiguration)
|
||||
{
|
||||
$oSanitizer = new TestDOMSanitizer();
|
||||
$oSanitizer->SetTagsWhiteList($aSanitizerConfiguration['tagsWhiteList']);
|
||||
$oSanitizer->SetAttrsWhiteList($aSanitizerConfiguration['attrsWhiteList']);
|
||||
$oSanitizer->SetStylesWhiteList($aSanitizerConfiguration['stylesWhiteList']);
|
||||
$oSanitizer->SetTagsBlackList($aSanitizerConfiguration['tagsBlackList']);
|
||||
$oSanitizer->SetAttrsBlackList($aSanitizerConfiguration['attrsBlackList']);
|
||||
|
||||
$aCKEditorConfiguration = CKEditorHelper::GetDOMSanitizerForCKEditor($oSanitizer);
|
||||
$this->assertEquals($aExpectedCKEditorConfiguration, $aCKEditorConfiguration);
|
||||
}
|
||||
|
||||
public function DOMSanitizerForCKEditorProvider(): array
|
||||
{
|
||||
return [
|
||||
'Allow list small dataset' => [
|
||||
[
|
||||
'tagsWhiteList' => [
|
||||
'html' => array(),
|
||||
'p' => array('style', 'class'),
|
||||
'a' => array('href', 'name'),
|
||||
],
|
||||
'attrsWhiteList' => [
|
||||
'href' => '/^(https:)/i'
|
||||
],
|
||||
'stylesWhiteList' => [
|
||||
'color',
|
||||
'font-size'
|
||||
],
|
||||
'tagsBlackList' => [],
|
||||
'attrsBlackList' => [],
|
||||
],
|
||||
[
|
||||
'allow' => [
|
||||
[
|
||||
'name' => 'html',
|
||||
'attributes' => false,
|
||||
'classes' => false,
|
||||
'styles' => false
|
||||
],
|
||||
[
|
||||
'name' => 'p',
|
||||
'attributes' => false,
|
||||
'classes' => true,
|
||||
'styles' => [
|
||||
'color' => true,
|
||||
'font-size' => true
|
||||
]
|
||||
],
|
||||
[
|
||||
'name' => 'a',
|
||||
'attributes' => [
|
||||
'href' => [
|
||||
'pattern' => '/^(https:)/i'
|
||||
],
|
||||
'name' => true
|
||||
],
|
||||
'classes' => false,
|
||||
'styles' => false
|
||||
]
|
||||
],
|
||||
'disallow' => []
|
||||
]
|
||||
],
|
||||
'Allow list medium dataset' => [
|
||||
[
|
||||
'tagsWhiteList' => [
|
||||
'h1' => array('style', 'class'),
|
||||
'h2' => array('style', 'class'),
|
||||
'h3' => array('style', 'class'),
|
||||
'h4' => array('style', 'class'),
|
||||
'table' => array('style', 'class', 'width', 'summary', 'align', 'border', 'cellpadding', 'cellspacing', 'style'),
|
||||
'tr' => array('style', 'class', 'align', 'valign', 'bgcolor', 'style'),
|
||||
'ul' => array(),
|
||||
'ol' => array(),
|
||||
],
|
||||
'attrsWhiteList' => [
|
||||
'href' => '/^(https:)/i',
|
||||
'src' => '/^(https:)/i',
|
||||
'width' => '/^([0-9]+(px|em|%)?)$/i',
|
||||
'height' => '/^([0-9]+(px|em|%)?)$/i',
|
||||
'align' => '/^(left|right|center|justify)$/i',
|
||||
'valign' => '/^(top|middle|bottom)$/i',
|
||||
'bgcolor' => '/^#[0-9a-f]{6}$/i',
|
||||
],
|
||||
'stylesWhiteList' => [
|
||||
'color',
|
||||
'float',
|
||||
'font',
|
||||
'font-family',
|
||||
'font-size',
|
||||
'font-style',
|
||||
'height',
|
||||
'margin',
|
||||
'padding',
|
||||
'text-align',
|
||||
'vertical-align',
|
||||
'width',
|
||||
'white-space',
|
||||
],
|
||||
'tagsBlackList' => [],
|
||||
'attrsBlackList' => [],
|
||||
],
|
||||
[
|
||||
'allow' => [
|
||||
[
|
||||
'name' => 'h1',
|
||||
'attributes' => false,
|
||||
'classes' => true,
|
||||
'styles' => [
|
||||
'color' => true,
|
||||
'font' => true,
|
||||
'font-family' => true,
|
||||
'font-size' => true,
|
||||
'font-style' => true,
|
||||
'height' => true,
|
||||
'margin' => true,
|
||||
'padding' => true,
|
||||
'text-align' => true,
|
||||
'vertical-align' => true,
|
||||
'width' => true,
|
||||
'white-space' => true,
|
||||
'float' => true
|
||||
]
|
||||
],
|
||||
[
|
||||
'name' => 'h2',
|
||||
'attributes' => false,
|
||||
'classes' => true,
|
||||
'styles' => [
|
||||
'color' => true,
|
||||
'font' => true,
|
||||
'font-family' => true,
|
||||
'font-size' => true,
|
||||
'font-style' => true,
|
||||
'height' => true,
|
||||
'margin' => true,
|
||||
'padding' => true,
|
||||
'text-align' => true,
|
||||
'vertical-align' => true,
|
||||
'width' => true,
|
||||
'white-space' => true,
|
||||
'float' => true
|
||||
]
|
||||
],
|
||||
[
|
||||
'name' => 'h3',
|
||||
'attributes' => false,
|
||||
'classes' => true,
|
||||
'styles' => [
|
||||
'color' => true,
|
||||
'font' => true,
|
||||
'font-family' => true,
|
||||
'font-size' => true,
|
||||
'font-style' => true,
|
||||
'height' => true,
|
||||
'margin' => true,
|
||||
'padding' => true,
|
||||
'text-align' => true,
|
||||
'vertical-align' => true,
|
||||
'width' => true,
|
||||
'white-space' => true,
|
||||
'float' => true
|
||||
]
|
||||
],
|
||||
[
|
||||
'name' => 'h4',
|
||||
'attributes' => false,
|
||||
'classes' => true,
|
||||
'styles' => [
|
||||
'color' => true,
|
||||
'font' => true,
|
||||
'font-family' => true,
|
||||
'font-size' => true,
|
||||
'font-style' => true,
|
||||
'height' => true,
|
||||
'margin' => true,
|
||||
'padding' => true,
|
||||
'text-align' => true,
|
||||
'vertical-align' => true,
|
||||
'width' => true,
|
||||
'white-space' => true,
|
||||
'float' => true
|
||||
]
|
||||
],
|
||||
[
|
||||
'name' => 'table',
|
||||
'attributes' => [
|
||||
'width' => [
|
||||
'pattern' => '/^([0-9]+(px|em|%)?)$/i'
|
||||
],
|
||||
'summary' => true,
|
||||
'align' => [
|
||||
'pattern' => '/^(left|right|center|justify)$/i'
|
||||
],
|
||||
'border' => true,
|
||||
'cellpadding' => true,
|
||||
'cellspacing' => true,
|
||||
],
|
||||
'classes' => true,
|
||||
'styles' => [
|
||||
'color' => true,
|
||||
'font' => true,
|
||||
'font-family' => true,
|
||||
'font-size' => true,
|
||||
'font-style' => true,
|
||||
'height' => true,
|
||||
'margin' => true,
|
||||
'padding' => true,
|
||||
'text-align' => true,
|
||||
'vertical-align' => true,
|
||||
'width' => true,
|
||||
'white-space' => true,
|
||||
'float' => true
|
||||
]
|
||||
],
|
||||
[
|
||||
'name' => 'tr',
|
||||
'attributes' => [
|
||||
'align' => [
|
||||
'pattern' => '/^(left|right|center|justify)$/i'
|
||||
],
|
||||
'valign' => [
|
||||
'pattern' => '/^(top|middle|bottom)$/i'
|
||||
],
|
||||
'bgcolor' => [
|
||||
'pattern' => '/^#[0-9a-f]{6}$/i'
|
||||
]
|
||||
],
|
||||
'classes' => true,
|
||||
'styles' => [
|
||||
'color' => true,
|
||||
'font' => true,
|
||||
'font-family' => true,
|
||||
'font-size' => true,
|
||||
'font-style' => true,
|
||||
'height' => true,
|
||||
'margin' => true,
|
||||
'padding' => true,
|
||||
'text-align' => true,
|
||||
'vertical-align' => true,
|
||||
'width' => true,
|
||||
'white-space' => true,
|
||||
'float' => true
|
||||
]
|
||||
],
|
||||
[
|
||||
'name' => 'ul',
|
||||
'attributes' => false,
|
||||
'classes' => false,
|
||||
'styles' => false
|
||||
],
|
||||
[
|
||||
'name' => 'ol',
|
||||
'attributes' => false,
|
||||
'classes' => false,
|
||||
'styles' => false
|
||||
]
|
||||
],
|
||||
'disallow' => []
|
||||
]
|
||||
],
|
||||
'Disallow list small dataset' => [
|
||||
[
|
||||
'tagsWhiteList' => [],
|
||||
'attrsWhiteList' => [],
|
||||
'stylesWhiteList' => [],
|
||||
'tagsBlackList' => [
|
||||
'html',
|
||||
'p',
|
||||
'a',
|
||||
],
|
||||
'attrsBlackList' => [
|
||||
'href',
|
||||
],
|
||||
],
|
||||
[
|
||||
'allow' => [],
|
||||
'disallow' => [
|
||||
[
|
||||
'name' => 'html',
|
||||
'attributes' => [
|
||||
'href' => true
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'p',
|
||||
'attributes' => [
|
||||
'href' => true
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'a',
|
||||
'attributes' => [
|
||||
'href' => true
|
||||
],
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Application\Helper;
|
||||
|
||||
|
||||
use DOMSanitizer;
|
||||
|
||||
class TestDOMSanitizer extends DOMSanitizer
|
||||
{
|
||||
protected static array $aTagsWhiteList = [];
|
||||
private static $aStylesWhiteList = [];
|
||||
private static $aAttrsWhiteList = [];
|
||||
|
||||
protected static array $aTagsBlackList = [];
|
||||
private static $aAttrsBlackList = [];
|
||||
|
||||
public function GetTagsWhiteList()
|
||||
{
|
||||
return static::$aTagsWhiteList;
|
||||
}
|
||||
|
||||
public function GetTagsBlackList()
|
||||
{
|
||||
return static::$aTagsBlackList;
|
||||
}
|
||||
|
||||
public function GetAttrsWhiteList()
|
||||
{
|
||||
return static::$aAttrsWhiteList;
|
||||
}
|
||||
|
||||
public function GetAttrsBlackList()
|
||||
{
|
||||
return static::$aAttrsBlackList;
|
||||
}
|
||||
|
||||
public function GetStylesWhiteList()
|
||||
{
|
||||
return static::$aStylesWhiteList;
|
||||
}
|
||||
|
||||
public function SetTagsWhiteList(array $aTagsWhiteList)
|
||||
{
|
||||
static::$aTagsWhiteList = $aTagsWhiteList;
|
||||
}
|
||||
|
||||
public function SetAttrsWhiteList(array $aAttrsWhiteList)
|
||||
{
|
||||
static::$aAttrsWhiteList = $aAttrsWhiteList;
|
||||
}
|
||||
|
||||
public function SetStylesWhiteList(array $aStylesWhiteList)
|
||||
{
|
||||
static::$aStylesWhiteList = $aStylesWhiteList;
|
||||
}
|
||||
|
||||
public function SetTagsBlackList(array $aTagsBlackList)
|
||||
{
|
||||
static::$aTagsBlackList = $aTagsBlackList;
|
||||
}
|
||||
|
||||
public function SetAttrsBlackList(array $aAttrsBlackList)
|
||||
{
|
||||
static::$aAttrsBlackList = $aAttrsBlackList;
|
||||
}
|
||||
|
||||
public function LoadDoc($sHTML)
|
||||
{
|
||||
// TODO: Implement LoadDoc() method.
|
||||
}
|
||||
|
||||
public function PrintDoc()
|
||||
{
|
||||
// TODO: Implement PrintDoc() method.
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ class iTopComposerTest extends ItopTestCase
|
||||
*/
|
||||
public function testIsQuestionnableFolder($sDirName, $bIsTest)
|
||||
{
|
||||
$isTestDir = iTopComposer::IsQuestionnableFolder($sDirName);
|
||||
$isTestDir = iTopComposer::IsQuestionnableFile($sDirName);
|
||||
$this->assertIsInt($isTestDir);
|
||||
if (true === $bIsTest) {
|
||||
$this->assertTrue(($isTestDir > 0));
|
||||
@@ -65,13 +65,13 @@ class iTopComposerTest extends ItopTestCase
|
||||
public function testListAllFoldersAbsPaths()
|
||||
{
|
||||
$oiTopComposer = new iTopComposer();
|
||||
$aDirs = $oiTopComposer->ListAllFoldersAbsPaths();
|
||||
$aDirs = $oiTopComposer->ListAllFilesAbsPaths();
|
||||
|
||||
$this->assertTrue(is_array($aDirs));
|
||||
|
||||
foreach ($aDirs as $sDir) {
|
||||
$sDirName = basename($sDir);
|
||||
$this->assertMatchesRegularExpression(iTopComposer::QUESTIONNABLE_FOLDER_REGEXP, $sDirName, "Directory not matching test dir : $sDir");
|
||||
$this->assertMatchesRegularExpression(iTopComposer::QUESTIONNABLE_FILES_REGEXP, $sDirName, "Directory not matching test dir : $sDir");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -79,13 +79,13 @@ class iTopComposerTest extends ItopTestCase
|
||||
public function testListDeniedFolderAbsPaths()
|
||||
{
|
||||
$oiTopComposer = new iTopComposer();
|
||||
$aDirs = $oiTopComposer->ListDeniedFoldersAbsPaths();
|
||||
$aDirs = $oiTopComposer->ListDeniedFilesAbsPaths();
|
||||
|
||||
$this->assertTrue(is_array($aDirs));
|
||||
|
||||
$aDeniedDirWrongFormat = [];
|
||||
foreach ($aDirs as $sDir) {
|
||||
if (false === iTopComposer::IsQuestionnableFolder($sDir)) {
|
||||
if (false === iTopComposer::IsQuestionnableFile($sDir)) {
|
||||
$aDeniedDirWrongFormat[] = $sDir;
|
||||
}
|
||||
}
|
||||
@@ -97,7 +97,7 @@ class iTopComposerTest extends ItopTestCase
|
||||
public function testListAllowedFoldersAbsPaths()
|
||||
{
|
||||
$oiTopComposer = new iTopComposer();
|
||||
$aDirs = $oiTopComposer->ListAllowedFoldersAbsPaths();
|
||||
$aDirs = $oiTopComposer->ListAllowedFilesAbsPaths();
|
||||
|
||||
$this->assertTrue(is_array($aDirs));
|
||||
}
|
||||
@@ -109,7 +109,7 @@ class iTopComposerTest extends ItopTestCase
|
||||
{
|
||||
$oiTopComposer = new iTopComposer();
|
||||
|
||||
$aDeniedButStillPresent = $oiTopComposer->ListDeniedButStillPresentFoldersAbsPaths();
|
||||
$aDeniedButStillPresent = $oiTopComposer->ListDeniedButStillPresentFilesAbsPaths();
|
||||
|
||||
$this->assertEmpty(
|
||||
$aDeniedButStillPresent,
|
||||
@@ -125,11 +125,11 @@ class iTopComposerTest extends ItopTestCase
|
||||
{
|
||||
$oDependenciesHandler = new iTopComposer();
|
||||
$aAllowedAndDeniedDirs = array_merge(
|
||||
$oDependenciesHandler->ListAllowedFoldersAbsPaths(),
|
||||
$oDependenciesHandler->ListDeniedFoldersAbsPaths()
|
||||
$oDependenciesHandler->ListAllowedFilesAbsPaths(),
|
||||
$oDependenciesHandler->ListDeniedFilesAbsPaths()
|
||||
);
|
||||
|
||||
$aExistingDirs = $oDependenciesHandler->ListAllFoldersAbsPaths();
|
||||
$aExistingDirs = $oDependenciesHandler->ListAllFilesAbsPaths();
|
||||
|
||||
$aMissing = array_diff($aExistingDirs, $aAllowedAndDeniedDirs);
|
||||
$aExtra = array_diff($aAllowedAndDeniedDirs, $aExistingDirs);
|
||||
|
||||
@@ -40,11 +40,11 @@ class iTopNPMTest extends ItopTestCase
|
||||
{
|
||||
$oDependenciesHandler = new iTopNPM();
|
||||
$aAllowedAndDeniedDirs = array_merge(
|
||||
$oDependenciesHandler->ListAllowedFoldersAbsPaths(),
|
||||
$oDependenciesHandler->ListDeniedFoldersAbsPaths()
|
||||
$oDependenciesHandler->ListAllowedFilesAbsPaths(),
|
||||
$oDependenciesHandler->ListDeniedFilesAbsPaths()
|
||||
);
|
||||
|
||||
$aExistingDirs = $oDependenciesHandler->ListAllFoldersAbsPaths();
|
||||
$aExistingDirs = $oDependenciesHandler->ListAllFilesAbsPaths();
|
||||
|
||||
$aMissing = array_diff($aExistingDirs, $aAllowedAndDeniedDirs);
|
||||
$aExtra = array_diff($aAllowedAndDeniedDirs, $aExistingDirs);
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Service\Cache;
|
||||
|
||||
use Combodo\iTop\Service\Cache\DataModelDependantCache;
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use Exception;
|
||||
|
||||
class DataModelDependantCacheTest extends ItopTestCase
|
||||
{
|
||||
private DataModelDependantCache $oCacheService;
|
||||
private string $sCacheRootDir;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->RequireOnceItopFile('setup/setuputils.class.inc.php');
|
||||
|
||||
$this->sCacheRootDir = self::CreateTmpdir();
|
||||
|
||||
$this->oCacheService = DataModelDependantCache::GetInstance();
|
||||
$this->SetNonPublicProperty($this->oCacheService, 'sStorageRootDir', $this->sCacheRootDir);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->SetNonPublicProperty($this->oCacheService, 'sStorageRootDir', null);
|
||||
self::RecurseRmdir($this->sCacheRootDir);
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testShouldStoreAndFetchVariousDataTypes(): void
|
||||
{
|
||||
$this->oCacheService->Store('pool-A', 'key-array', ['value1', 'value2']);
|
||||
$this->oCacheService->Store('pool-A', 'key-string', 'foo');
|
||||
$this->oCacheService->Store('pool-A', 'key-int', 1971);
|
||||
|
||||
$this->assertEquals(['value1', 'value2'], $this->oCacheService->Fetch('pool-A', 'key-array'));
|
||||
$this->assertEquals('foo', $this->oCacheService->Fetch('pool-A', 'key-string'));
|
||||
$this->assertEquals(1971, $this->oCacheService->Fetch('pool-A', 'key-int'));
|
||||
}
|
||||
|
||||
public function testShouldNotAllowToStoreNull(): void
|
||||
{
|
||||
$this->ExpectExceptionMessage('Cannot store NULL in the cache');
|
||||
$this->oCacheService->Store('pool-A', 'key', null);
|
||||
}
|
||||
|
||||
public function testShouldStoreInADirectoryRebuiltOnCompilation(): void
|
||||
{
|
||||
// Given the storage is reset to the default
|
||||
$this->SetNonPublicProperty($this->oCacheService, 'sStorageRootDir', null);
|
||||
|
||||
// Then
|
||||
$sFilePath = $this->InvokeNonPublicMethod(DataModelDependantCache::class, 'MakeCacheFileName', $this->oCacheService, ['pool-A', 'key']);
|
||||
$this->assertStringContainsString('data/cache-', $sFilePath);
|
||||
}
|
||||
|
||||
public function testPoolShouldSeparateEntriesHavingTheSameKey(): void
|
||||
{
|
||||
// Given
|
||||
$this->oCacheService->Store('pool-A', 'key', ['data-default']);
|
||||
$this->oCacheService->Store('pool-B', 'key', ['data-pool-B']);
|
||||
|
||||
// Then
|
||||
$this->assertEquals(['data-default'], $this->oCacheService->Fetch('pool-A', 'key'));
|
||||
$this->assertEquals(['data-pool-B'], $this->oCacheService->Fetch('pool-B', 'key'));
|
||||
$this->assertEquals(null, $this->oCacheService->Fetch('pool-C-unknown', 'key'));
|
||||
}
|
||||
public function testPoolsShouldBeVisibleInThePath(): void
|
||||
{
|
||||
$sFilePath = $this->InvokeNonPublicMethod(DataModelDependantCache::class, 'MakeCacheFileName', $this->oCacheService, ['pool-B', 'key']);
|
||||
$this->assertStringContainsString('pool-B', $sFilePath);
|
||||
}
|
||||
|
||||
public function testFetchShouldReturnNullForNonExistingKey(): void
|
||||
{
|
||||
$this->assertNull($this->oCacheService->Fetch('pool-A', 'non-existing-key'));
|
||||
}
|
||||
|
||||
public function testAnUnknownPoolShouldFailSilently(): void
|
||||
{
|
||||
$this->assertNull($this->oCacheService->Fetch('unknown-pool', 'non-existing-key'));
|
||||
}
|
||||
|
||||
public function testDeleteItemAndHasEntry()
|
||||
{
|
||||
// Given an empy cache
|
||||
// Then
|
||||
$this->assertFalse($this->oCacheService->HasEntry('pool-A', 'key'), 'HasEntry should return false for non-existing key');
|
||||
|
||||
// When
|
||||
$this->oCacheService->Store('pool-A', 'key', 'some data...');
|
||||
|
||||
// Then
|
||||
$this->assertTrue($this->oCacheService->HasEntry('pool-A', 'key'), 'HasEntry should return true for newly created key');
|
||||
|
||||
// When
|
||||
$this->oCacheService->DeleteItem('pool-A', 'key');
|
||||
|
||||
// Then
|
||||
$this->assertFalse($this->oCacheService->HasEntry('pool-A', 'key'), 'HasEntry should return true for a removed key');
|
||||
}
|
||||
|
||||
public function testDeleteItemShouldPreserveOtherEntries()
|
||||
{
|
||||
// Given
|
||||
$this->oCacheService->Store('pool-A', 'key', 'some data...');
|
||||
$this->oCacheService->Store('pool-A', 'key2', 'some data...');
|
||||
|
||||
// Then
|
||||
$this->oCacheService->DeleteItem('pool-A', 'key');
|
||||
|
||||
// When
|
||||
$this->assertTrue($this->oCacheService->HasEntry('pool-A', 'key2'));
|
||||
}
|
||||
|
||||
public function testDeleteItemShouldPreserveHomonymsFromDifferentPools()
|
||||
{
|
||||
// Given
|
||||
$this->oCacheService->Store('pool-A', 'key', 'some data...');
|
||||
$this->oCacheService->Store('pool-B', 'key', 'some data...');
|
||||
|
||||
// When
|
||||
$this->oCacheService->DeleteItem('pool-B', 'key');
|
||||
|
||||
// Then
|
||||
$this->assertTrue($this->oCacheService->HasEntry('pool-A', 'key'), 'DeleteItem should not have altered the pool "default"');
|
||||
}
|
||||
|
||||
public function testClearShouldRemoveAllEntriesFromTheCurrentPool()
|
||||
{
|
||||
// Given
|
||||
$this->oCacheService->Store('pool-A', 'key', 'some data...');
|
||||
$this->oCacheService->Store('pool-A', 'key2', 'some data...');
|
||||
$this->oCacheService->Store('pool-B', 'key', 'some data...');
|
||||
|
||||
// When
|
||||
$this->oCacheService->Clear('pool-A');
|
||||
|
||||
// Then
|
||||
$this->assertFalse($this->oCacheService->HasEntry('pool-A', 'key'), 'DeleteItem should remove all entries from the current pool');
|
||||
$this->assertFalse($this->oCacheService->HasEntry('pool-A', 'key2'), 'DeleteItem should remove all entries from the current pool');
|
||||
$this->assertTrue($this->oCacheService->HasEntry('pool-B', 'key'), 'DeleteItem should not alter entries from other pools');
|
||||
}
|
||||
|
||||
public function testGetEntryModificationTime()
|
||||
{
|
||||
// Given an entry created at a specific time
|
||||
$this->oCacheService->Store('pool-A', 'key', 'some data...');
|
||||
$iRefTime = time();
|
||||
$sFilePath = $this->InvokeNonPublicMethod(DataModelDependantCache::class, 'MakeCacheFileName', $this->oCacheService, ['pool-A', 'key']);
|
||||
touch($sFilePath, $iRefTime);
|
||||
|
||||
// Then
|
||||
$this->assertEquals($iRefTime, $this->oCacheService->GetEntryModificationTime('pool-A', 'key'), 'GetEntryModificationTime should return the modification time of the cache file');
|
||||
$this->assertEquals(null, $this->oCacheService->GetEntryModificationTime('pool-A', 'non-existing-key'), 'GetEntryModificationTime should return null for an invalid key');
|
||||
}
|
||||
public function testKeyUndesiredCharactersShouldBeTransformedToUnderscore()
|
||||
{
|
||||
$sUglyKey = 'key with ugly characters:\{&"#@ç^²/,;[(|🤔';
|
||||
$sFilePath = $this->InvokeNonPublicMethod(DataModelDependantCache::class, 'MakeCacheFileName', $this->oCacheService, ['pool-A', $sUglyKey]);
|
||||
$this->assertEquals('key_with_ugly_characters______________________.php', basename($sFilePath));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Service\InterfaceDiscovery;
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\iUIBlockFactory;
|
||||
use Combodo\iTop\Service\InterfaceDiscovery\InterfaceDiscovery;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
|
||||
class InterfaceDiscoveryTest extends ItopDataTestCase
|
||||
{
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->SetNonPublicProperty(InterfaceDiscovery::GetInstance(), 'aForcedClassMap', null);
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testShouldSelectTheRequestedItopClasses()
|
||||
{
|
||||
$oInterfaceDiscoveryService = InterfaceDiscovery::GetInstance();
|
||||
|
||||
$this->GivenClassMap($oInterfaceDiscoveryService, [
|
||||
'Combodo\iTop\Application\UI\Base\Component\Alert\Alert' => APPROOT . '/sources/Application/UI/Base/Component/Alert/Alert.php',
|
||||
'Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory' => APPROOT . '/sources/Application/UI/Base/Component/Alert/AlertUIBlockFactory.php',
|
||||
'Combodo\iTop\Application\UI\Base\Component\ButtonGroup\ButtonGroupUIBlockFactory' => APPROOT . '/sources/Application/UI/Base/Component/ButtonGroup/ButtonGroupUIBlockFactory.php',
|
||||
]);
|
||||
|
||||
$this->AssertArraysHaveSameItems(
|
||||
[
|
||||
'Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory',
|
||||
'Combodo\iTop\Application\UI\Base\Component\ButtonGroup\ButtonGroupUIBlockFactory',
|
||||
],
|
||||
$oInterfaceDiscoveryService->FindItopClasses(iUIBlockFactory::class)
|
||||
);
|
||||
}
|
||||
|
||||
public function testShouldExcludeSpecifiedDirectories()
|
||||
{
|
||||
$oInterfaceDiscoveryService = InterfaceDiscovery::GetInstance();
|
||||
|
||||
$this->GivenClassMap($oInterfaceDiscoveryService, [
|
||||
'Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory' => APPROOT . '/sources/Application/UI/Base/Component/Alert/AlertUIBlockFactory.php',
|
||||
'Combodo\iTop\Application\UI\Base\Component\ButtonGroup\ButtonGroupUIBlockFactory' => APPROOT . '/sources/Application/UI/Base/Component/ButtonGroup/ButtonGroupUIBlockFactory.php',
|
||||
]);
|
||||
|
||||
$this->AssertArraysHaveSameItems(
|
||||
[],
|
||||
$oInterfaceDiscoveryService->FindItopClasses(iUIBlockFactory::class, ['Component/ButtonGroup', '/Alert/'])
|
||||
);
|
||||
}
|
||||
public function testShouldExcludeAliases()
|
||||
{
|
||||
$oInterfaceDiscoveryService = InterfaceDiscovery::GetInstance();
|
||||
|
||||
$this->GivenClassMap($oInterfaceDiscoveryService, [
|
||||
'Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory' => APPROOT . '/sources/Application/UI/Base/Component/Alert/AlertUIBlockFactory.php',
|
||||
'AlbertIsBlockingTheFactory' => APPROOT . '/sources/Application/UI/Base/Component/Alert/AlertUIBlockFactory.php',
|
||||
]);
|
||||
|
||||
class_alias('Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory', 'AlbertIsBlockingTheFactory');
|
||||
|
||||
$this->AssertArraysHaveSameItems(
|
||||
[
|
||||
'Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory',
|
||||
],
|
||||
$oInterfaceDiscoveryService->FindItopClasses(iUIBlockFactory::class)
|
||||
);
|
||||
}
|
||||
|
||||
private function GivenClassMap(InterfaceDiscovery $oInterfaceDiscoveryService, array $aClassMap): void
|
||||
{
|
||||
$this->SetNonPublicProperty($oInterfaceDiscoveryService, 'aForcedClassMap', $aClassMap);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user