mirror of
https://github.com/Combodo/iTop.git
synced 2026-05-17 14:28:53 +02:00
N°9165 - secure data cleanup
This commit is contained in:
@@ -0,0 +1,301 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Module\DataFeatureRemoval\Service;
|
||||
|
||||
use Cleanup;
|
||||
use Combodo\iTop\DataFeatureRemoval\Entity\DataCleanupSummaryEntity;
|
||||
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException;
|
||||
use Combodo\iTop\DataFeatureRemoval\Service\DataCleanupService;
|
||||
use Combodo\iTop\Test\UnitTest\ItopCustomDatamodelTestCase;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
/**
|
||||
* Unit tests for the CleanupService cf the Combodo Data Feature Removal module.
|
||||
*
|
||||
* These tests cover:
|
||||
* - GetCleanupSummary method: handling null and empty input, and verifying summary output for various delete/update scenarios.
|
||||
* - ExecuteCleanup method: confirming that an exception is thrown when issues are detected in the deletion plan.
|
||||
*
|
||||
* Key aspects tested:
|
||||
* - Consistent singleton instance management.
|
||||
* - Accurate summary generation for deletion and update operations, including mode and issue reporting per class.
|
||||
* - Edge cases such as null, empty, and multiple classes.
|
||||
* - Proper exception handling when the deletion plan contains issues.
|
||||
*
|
||||
* The tests use PHPUnit, mocks for Cleanup and CleanupService, and data providers to cover multiple scenarios.
|
||||
*
|
||||
* @see DataCleanupService
|
||||
* @see DataCleanupSummaryEntity
|
||||
* @see ItopDataTestCase
|
||||
*/
|
||||
class DataCleanupServiceTest extends ItopCustomDatamodelTestCase
|
||||
{
|
||||
private ExecutionLimits&MockObject $oExecutionLimits;
|
||||
|
||||
public function GetDatamodelDeltaAbsPath(): string
|
||||
{
|
||||
return __DIR__.'/data_cleanup_delta.xml';
|
||||
}
|
||||
|
||||
//--- GetCleanupSummary tests ---
|
||||
|
||||
/**
|
||||
* Tests that GetCleanupSummary returns an empty array when passed null as input.
|
||||
*/
|
||||
public function testGetCleanupSummaryReturnsEmptyArrayWhenNull(): void
|
||||
{
|
||||
$oService = new DataCleanupService();
|
||||
$aResult = $oService->GetCleanupSummary(null);
|
||||
|
||||
$this->assertIsArray($aResult, 'Expected result to be an array when input is null.');
|
||||
$this->assertEmpty($aResult, 'Expected result to be empty array when input is null.');
|
||||
}
|
||||
|
||||
//--- ExecuteCleanup tests ---
|
||||
|
||||
public function testExecuteCleanup_DeleteOneObjPerClassWithoutLimit()
|
||||
{
|
||||
$this->GivenDFRTreeInDB(<<<EOF
|
||||
DFRToRemoveLeaf_1 <- DFRToUpdate_1
|
||||
DFRToRemoveLeaf_1 <- DFRRemovedCollateral_1
|
||||
DFRRemovedCollateral_1 <- DFRRemovedCollateralCascade_1
|
||||
EOF);
|
||||
|
||||
$aClasses = [ 'DFRToRemoveLeaf' ];
|
||||
$oService = new DataCleanupService();
|
||||
$aRes = $oService->ExecuteCleanup($aClasses);
|
||||
$aExpected = [
|
||||
['DFRToUpdate', 1, 0 ],
|
||||
['DFRToRemoveLeaf', 0, 1 ],
|
||||
['DFRRemovedCollateral', 0, 1 ],
|
||||
['DFRRemovedCollateralCascade', 0, 1 ],
|
||||
];
|
||||
$this->AssertSummaryEquals($aExpected, $aRes);
|
||||
}
|
||||
|
||||
public function testExecuteCleanup_DeleteManyObjPerClassWithoutLimit()
|
||||
{
|
||||
$this->GivenDFRTreeInDB(<<<EOF
|
||||
DFRToRemoveLeaf_1 <- DFRToUpdate_1
|
||||
DFRToRemoveLeaf_1 <- DFRRemovedCollateral_1
|
||||
DFRRemovedCollateral_1 <- DFRRemovedCollateralCascade_1
|
||||
|
||||
DFRToRemoveLeaf_2 <- DFRToUpdate_2
|
||||
DFRToRemoveLeaf_2 <- DFRRemovedCollateral_2
|
||||
DFRRemovedCollateral_2 <- DFRRemovedCollateralCascade_2
|
||||
|
||||
DFRToRemoveLeaf_3 <- DFRToUpdate_3
|
||||
DFRToRemoveLeaf_3 <- DFRRemovedCollateral_3
|
||||
DFRRemovedCollateral_3 <- DFRRemovedCollateralCascade_3
|
||||
EOF);
|
||||
|
||||
$aClasses = [ 'DFRToRemoveLeaf' ];
|
||||
$oService = new DataCleanupService();
|
||||
$aRes = $oService->ExecuteCleanup($aClasses);
|
||||
$aExpected = [
|
||||
['DFRToUpdate', 3, 0 ],
|
||||
['DFRToRemoveLeaf', 0, 3 ],
|
||||
['DFRRemovedCollateral', 0, 3 ],
|
||||
['DFRRemovedCollateralCascade', 0, 3 ],
|
||||
];
|
||||
$this->AssertSummaryEquals($aExpected, $aRes);
|
||||
}
|
||||
|
||||
public function testGetCleanupSummary_DeleteManyObjPerClassWithoutLimit()
|
||||
{
|
||||
$this->GivenDFRTreeInDB(<<<EOF
|
||||
DFRToRemoveLeaf_1 <- DFRToUpdate_1
|
||||
DFRToRemoveLeaf_1 <- DFRRemovedCollateral_1
|
||||
DFRRemovedCollateral_1 <- DFRRemovedCollateralCascade_1
|
||||
|
||||
DFRToRemoveLeaf_2 <- DFRToUpdate_2
|
||||
DFRToRemoveLeaf_2 <- DFRRemovedCollateral_2
|
||||
DFRRemovedCollateral_2 <- DFRRemovedCollateralCascade_2
|
||||
|
||||
DFRToRemoveLeaf_3 <- DFRToUpdate_3
|
||||
DFRToRemoveLeaf_3 <- DFRRemovedCollateral_3
|
||||
DFRRemovedCollateral_3 <- DFRRemovedCollateralCascade_3
|
||||
EOF);
|
||||
|
||||
$aClasses = [ 'DFRToRemoveLeaf' ];
|
||||
$oService = new DataCleanupService();
|
||||
$aRes = $oService->GetCleanupSummary($aClasses);
|
||||
$aExpected = [
|
||||
['DFRToUpdate', 3, 0 ],
|
||||
['DFRToRemoveLeaf', 0, 3 ],
|
||||
['DFRRemovedCollateral', 0, 3 ],
|
||||
['DFRRemovedCollateralCascade', 0, 3 ],
|
||||
];
|
||||
$this->AssertSummaryEquals($aExpected, $aRes);
|
||||
}
|
||||
|
||||
public function testExecuteCleanup_ManualDeleteShouldFail()
|
||||
{
|
||||
$this->GivenDFRTreeInDB(<<<EOF
|
||||
DFRToRemoveLeaf_1 <- DFRManual_1
|
||||
EOF);
|
||||
|
||||
$aClasses = [ 'DFRToRemoveLeaf' ];
|
||||
$this->expectException(DataFeatureRemovalException::class);
|
||||
$this->expectExceptionMessage('Deletion Plan cannot be executed due to issues');
|
||||
$oService = new DataCleanupService();
|
||||
$oService->ExecuteCleanup($aClasses);
|
||||
}
|
||||
|
||||
public function testGetCleanupSummary_ManualDeleteShouldFail()
|
||||
{
|
||||
$this->GivenDFRTreeInDB(<<<EOF
|
||||
DFRToRemoveLeaf_1 <- DFRManual_1
|
||||
DFRToRemoveLeaf_1 <- DFRManual_2
|
||||
DFRToRemoveLeaf_2 <- DFRManual_3
|
||||
EOF);
|
||||
|
||||
$aClasses = [ 'DFRToRemoveLeaf' ];
|
||||
$oService = new DataCleanupService();
|
||||
$aRes = $oService->GetCleanupSummary($aClasses);
|
||||
$aExpected = [
|
||||
['DFRManual', 0, 0, 3 ],
|
||||
['DFRToRemoveLeaf', 0, 2],
|
||||
];
|
||||
$this->AssertSummaryEquals($aExpected, $aRes);
|
||||
}
|
||||
|
||||
private function AssertSummaryEquals(array $expected, $actual, $sMessage = '')
|
||||
{
|
||||
$aExpected = [];
|
||||
foreach ($expected as $line) {
|
||||
$sClass = $line[0];
|
||||
$iUpdate = $line[1];
|
||||
$iDelete = $line[2];
|
||||
$iIssue = $line[3] ?? 0;
|
||||
|
||||
$oCleanupSummaryEntity = new DataCleanupSummaryEntity($sClass);
|
||||
$oCleanupSummaryEntity->iUpdateCount = $iUpdate;
|
||||
$oCleanupSummaryEntity->iDeleteCount = $iDelete;
|
||||
$oCleanupSummaryEntity->iIssueCount = $iIssue;
|
||||
$aExpected[$sClass] = $oCleanupSummaryEntity;
|
||||
}
|
||||
$this->assertEquals($aExpected, $actual, $sMessage);
|
||||
}
|
||||
|
||||
public static function ExecuteCleanup_StopInProcessKeepDatabaseOk(): array
|
||||
{
|
||||
return [
|
||||
'Stop after 1' => [
|
||||
1,
|
||||
[
|
||||
['DFRToUpdate', 1, 0],
|
||||
],
|
||||
],
|
||||
'Stop after 2' => [
|
||||
2,
|
||||
[
|
||||
['DFRToUpdate', 1, 0],
|
||||
['DFRRemovedCollateralCascade', 0, 1],
|
||||
],
|
||||
],
|
||||
'Stop after 3' => [
|
||||
3,
|
||||
[
|
||||
['DFRToUpdate', 1, 0],
|
||||
['DFRRemovedCollateralCascade', 0, 1],
|
||||
['DFRRemovedCollateral', 0, 1],
|
||||
],
|
||||
],
|
||||
'Stop after 4' => [
|
||||
4,
|
||||
[
|
||||
['DFRToUpdate', 1, 0],
|
||||
['DFRRemovedCollateralCascade', 0, 1],
|
||||
['DFRRemovedCollateral', 0, 1],
|
||||
['DFRToRemoveLeaf', 0, 1],
|
||||
],
|
||||
],
|
||||
'Stop after 5' => [
|
||||
5,
|
||||
[
|
||||
['DFRToUpdate', 2, 0],
|
||||
['DFRRemovedCollateralCascade', 0, 1],
|
||||
['DFRRemovedCollateral', 0, 1],
|
||||
['DFRToRemoveLeaf', 0, 1],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ExecuteCleanup_StopInProcessKeepDatabaseOk
|
||||
*/
|
||||
public function testExecuteCleanup_StopInProcessKeepDatabaseOk(int $iExecutionCount, array $aExpected): void
|
||||
{
|
||||
$this->GivenDFRTreeInDB(<<<EOF
|
||||
DFRToRemoveLeaf_1 <- DFRToUpdate_1
|
||||
DFRToRemoveLeaf_1 <- DFRRemovedCollateral_1
|
||||
DFRRemovedCollateral_1 <- DFRRemovedCollateralCascade_1
|
||||
|
||||
DFRToRemoveLeaf_2 <- DFRToUpdate_2
|
||||
DFRToRemoveLeaf_2 <- DFRRemovedCollateral_2
|
||||
DFRRemovedCollateral_2 <- DFRRemovedCollateralCascade_2
|
||||
|
||||
DFRToRemoveLeaf_3 <- DFRToUpdate_3
|
||||
DFRToRemoveLeaf_3 <- DFRRemovedCollateral_3
|
||||
DFRRemovedCollateral_3 <- DFRRemovedCollateralCascade_3
|
||||
EOF);
|
||||
|
||||
$aClasses = [ 'DFRToRemoveLeaf' ];
|
||||
$oDeletionPlaService = new DataCleanupService();
|
||||
$this->GivenExecutionLimits($iExecutionCount);
|
||||
$this->SetNonPublicProperty($oDeletionPlaService, 'oExecutionLimits', $this->oExecutionLimits);
|
||||
$aRes = $oDeletionPlaService->ExecuteCleanup($aClasses);
|
||||
$this->AssertSummaryEquals($aExpected, $aRes);
|
||||
}
|
||||
|
||||
private function GivenDFRTreeInDB(string $sTree)
|
||||
{
|
||||
$aTree = explode("\n", $sTree);
|
||||
foreach ($aTree as $sLine) {
|
||||
if (trim($sLine) === "") {
|
||||
continue;
|
||||
}
|
||||
$this->GivenDFRTreeLineInDB($sLine);
|
||||
}
|
||||
}
|
||||
|
||||
private array $aIdByObjectName = [];
|
||||
private function GivenDFRTreeLineInDB(string $sLine)
|
||||
{
|
||||
list($sLeft, $sRight) = explode('<-', $sLine);
|
||||
$sLeft = trim($sLeft);
|
||||
|
||||
$iLeftId = $this->aIdByObjectName[$sLeft] ?? 0;
|
||||
if ($iLeftId === 0) {
|
||||
list($sChildClass, ) = explode('_', $sLeft, 2);
|
||||
$iLeftId = $this->GivenObjectInDB($sChildClass, ['name' => $sLeft]);
|
||||
$this->aIdByObjectName[$sLeft] = $iLeftId;
|
||||
}
|
||||
|
||||
$sRight = trim($sRight);
|
||||
list($sChildClass, ) = explode('_', $sRight, 2);
|
||||
$iRightId = $this->GivenObjectInDB($sChildClass, ['name' => $sRight, 'extkey_id' => $iLeftId]);
|
||||
$this->aIdByObjectName[$sRight] = $iRightId;
|
||||
}
|
||||
|
||||
private function GivenExecutionLimits(int $iStopAfterCallNumberReached): void
|
||||
{
|
||||
$matcher = $this->any();
|
||||
|
||||
$this->oExecutionLimits = $this->createMock(ExecutionLimits::class);
|
||||
$this->oExecutionLimits->expects($matcher)
|
||||
->method('ShouldStopExecution')->willReturnCallback(function () use ($matcher, $iStopAfterCallNumberReached) {
|
||||
$invocationCount = $matcher->getInvocationCount();
|
||||
|
||||
return ($invocationCount >= $iStopAfterCallNumberReached);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,277 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Module\DataFeatureRemoval\Service;
|
||||
|
||||
use Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanSummaryEntity;
|
||||
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException;
|
||||
use Combodo\iTop\DataFeatureRemoval\Service\DeletionPlanService;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use DeletionPlan;
|
||||
|
||||
/**
|
||||
* Unit tests for the DeletionPlanService class in the Combodo Data Feature Removal module.
|
||||
*
|
||||
* These tests cover:
|
||||
* - GetDeletionPlanSummary method: handling null and empty input, and verifying summary output for various delete/update scenarios.
|
||||
* - ExecuteDeletionPlan method: confirming that an exception is thrown when issues are detected in the deletion plan.
|
||||
*
|
||||
* Key aspects tested:
|
||||
* - Consistent singleton instance management.
|
||||
* - Accurate summary generation for deletion and update operations, including mode and issue reporting per class.
|
||||
* - Edge cases such as null, empty, and multiple classes.
|
||||
* - Proper exception handling when the deletion plan contains issues.
|
||||
*
|
||||
* The tests use PHPUnit, mocks for DeletionPlan and DeletionPlanService, and data providers to cover multiple scenarios.
|
||||
*
|
||||
* @see DeletionPlanService
|
||||
* @see DeletionPlanSummaryEntity
|
||||
* @see ItopDataTestCase
|
||||
*/
|
||||
class DeletionPlanServiceTest extends ItopDataTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->RequireOnceItopFile('env-production/combodo-data-feature-removal/vendor/autoload.php');
|
||||
}
|
||||
|
||||
//--- GetDeletionPlanSummary tests ---
|
||||
|
||||
/**
|
||||
* Tests that GetDeletionPlanSummary returns an empty array when passed null as input.
|
||||
*/
|
||||
public function testGetDeletionPlanSummaryReturnsEmptyArrayWhenNull(): void
|
||||
{
|
||||
$oService = DeletionPlanService::GetInstance();
|
||||
$aResult = $oService->GetDeletionPlanSummary(null);
|
||||
|
||||
$this->assertIsArray($aResult, 'Expected result to be an array when input is null.');
|
||||
$this->assertEmpty($aResult, 'Expected result to be empty array when input is null.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that GetDeletionPlanSummary returns an empty array when the input class list is empty.
|
||||
*/
|
||||
public function testGetDeletionPlanSummaryReturnsEmptyArrayWhenEmptyClasses(): void
|
||||
{
|
||||
$oMockService = $this->getMockBuilder(DeletionPlanService::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['GetDeletionPlan'])
|
||||
->getMock();
|
||||
|
||||
$oDeletionPlan = new DeletionPlan();
|
||||
$oMockService->method('GetDeletionPlan')->willReturn($oDeletionPlan);
|
||||
|
||||
DeletionPlanService::SetInstance($oMockService);
|
||||
|
||||
$aResult = $oMockService->GetDeletionPlanSummary([]);
|
||||
|
||||
$this->assertIsArray($aResult, 'Expected result to be an array when input class list is empty.');
|
||||
$this->assertEmpty($aResult, 'Expected result to be empty array when input class list is empty.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests GetDeletionPlanSummary for various delete/update scenarios using a data provider.
|
||||
* Verifies summary output for each class matches expected values.
|
||||
*
|
||||
* @dataProvider GetDeletionPlanSummaryWithDeletesProvider
|
||||
*/
|
||||
public function testGetDeletionPlanSummaryWithDeletes(array $aToDelete, array $aToUpdate, array $aExpected): void
|
||||
{
|
||||
$oDeletionPlan = $this->createMock(DeletionPlan::class);
|
||||
$oDeletionPlan->method('ListDeletes')->willReturn($aToDelete);
|
||||
$oDeletionPlan->method('ListUpdates')->willReturn($aToUpdate);
|
||||
|
||||
$oMockService = $this->getMockBuilder(DeletionPlanService::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['GetDeletionPlan'])
|
||||
->getMock();
|
||||
|
||||
$oMockService->method('GetDeletionPlan')->willReturn($oDeletionPlan);
|
||||
|
||||
$aResult = $oMockService->GetDeletionPlanSummary(['SomeClass']);
|
||||
|
||||
foreach ($aExpected as $sClass => $aExpectedValues) {
|
||||
$this->assertArrayHasKey(
|
||||
$sClass,
|
||||
$aResult,
|
||||
"Expected key '$sClass' to exist in summary."
|
||||
);
|
||||
$this->assertInstanceOf(
|
||||
DeletionPlanSummaryEntity::class,
|
||||
$aResult[$sClass],
|
||||
"Expected summary for '$sClass' to be instance of DeletionPlanSummaryEntity."
|
||||
);
|
||||
$this->assertEquals(
|
||||
$aExpectedValues['iDeleteCount'],
|
||||
$aResult[$sClass]->iDeleteCount,
|
||||
"Expected iDeleteCount for '$sClass' to be {$aExpectedValues['iDeleteCount']}, got {$aResult[$sClass]->iDeleteCount}."
|
||||
);
|
||||
$this->assertEquals(
|
||||
$aExpectedValues['iUpdateCount'],
|
||||
$aResult[$sClass]->iUpdateCount,
|
||||
"Expected iUpdateCount for '$sClass' to be {$aExpectedValues['iUpdateCount']}, got {$aResult[$sClass]->iUpdateCount}."
|
||||
);
|
||||
$this->assertEquals(
|
||||
$aExpectedValues['iMode'],
|
||||
$aResult[$sClass]->iMode,
|
||||
"Expected iMode for '$sClass' to be {$aExpectedValues['iMode']}, got {$aResult[$sClass]->iMode}."
|
||||
);
|
||||
$this->assertEquals(
|
||||
$aExpectedValues['sIssue'],
|
||||
$aResult[$sClass]->sIssue,
|
||||
"Expected sIssue for '$sClass' to be '{$aExpectedValues['sIssue']}', got '{$aResult[$sClass]->sIssue}'."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides multiple scenarios for testGetDeletionPlanSummaryWithDeletes, including deletes, updates, issues, and multiple classes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetDeletionPlanSummaryWithDeletesProvider(): array
|
||||
{
|
||||
return [
|
||||
'single class with deletes only' => [
|
||||
'aToDelete' => [
|
||||
'Server' => [
|
||||
1 => ['to_delete' => null, 'mode' => DEL_AUTO, 'requested_explicitely' => true],
|
||||
2 => ['to_delete' => null, 'mode' => DEL_SILENT, 'requested_explicitely' => false],
|
||||
],
|
||||
],
|
||||
'aToUpdate' => [],
|
||||
'aExpected' => [
|
||||
'Server' => [
|
||||
'iDeleteCount' => 2,
|
||||
'iUpdateCount' => 0,
|
||||
'iMode' => DEL_AUTO,
|
||||
'sIssue' => null,
|
||||
],
|
||||
],
|
||||
],
|
||||
'single class with deletes and issue' => [
|
||||
'aToDelete' => [
|
||||
'Server' => [
|
||||
1 => ['to_delete' => null, 'mode' => DEL_MANUAL, 'requested_explicitely' => false, 'issue' => 'Cannot delete'],
|
||||
],
|
||||
],
|
||||
'aToUpdate' => [],
|
||||
'aExpected' => [
|
||||
'Server' => [
|
||||
'iDeleteCount' => 1,
|
||||
'iUpdateCount' => 0,
|
||||
'iMode' => DEL_MANUAL,
|
||||
'sIssue' => 'Cannot delete',
|
||||
],
|
||||
],
|
||||
],
|
||||
'single class with updates only' => [
|
||||
'aToDelete' => [],
|
||||
'aToUpdate' => [
|
||||
'Person' => [
|
||||
10 => ['to_reset' => null, 'attributes' => ['org_id' => null]],
|
||||
11 => ['to_reset' => null, 'attributes' => ['org_id' => null]],
|
||||
12 => ['to_reset' => null, 'attributes' => ['org_id' => null]],
|
||||
],
|
||||
],
|
||||
'aExpected' => [
|
||||
'Person' => [
|
||||
'iDeleteCount' => 0,
|
||||
'iUpdateCount' => 3,
|
||||
'iMode' => 0,
|
||||
'sIssue' => null,
|
||||
],
|
||||
],
|
||||
],
|
||||
'class with both deletes and updates' => [
|
||||
'aToDelete' => [
|
||||
'Server' => [
|
||||
1 => ['to_delete' => null, 'mode' => DEL_AUTO, 'requested_explicitely' => true],
|
||||
],
|
||||
],
|
||||
'aToUpdate' => [
|
||||
'Server' => [
|
||||
5 => ['to_reset' => null, 'attributes' => ['org_id' => null]],
|
||||
],
|
||||
],
|
||||
'aExpected' => [
|
||||
'Server' => [
|
||||
'iDeleteCount' => 1,
|
||||
'iUpdateCount' => 1,
|
||||
'iMode' => DEL_AUTO,
|
||||
'sIssue' => null,
|
||||
],
|
||||
],
|
||||
],
|
||||
'multiple classes' => [
|
||||
'aToDelete' => [
|
||||
'Server' => [
|
||||
1 => ['to_delete' => null, 'mode' => DEL_AUTO, 'requested_explicitely' => true],
|
||||
],
|
||||
'NetworkDevice' => [
|
||||
3 => ['to_delete' => null, 'mode' => DEL_SILENT, 'requested_explicitely' => false],
|
||||
4 => ['to_delete' => null, 'mode' => DEL_SILENT, 'requested_explicitely' => false],
|
||||
],
|
||||
],
|
||||
'aToUpdate' => [
|
||||
'Person' => [
|
||||
10 => ['to_reset' => null, 'attributes' => ['org_id' => null]],
|
||||
],
|
||||
],
|
||||
'aExpected' => [
|
||||
'Server' => [
|
||||
'iDeleteCount' => 1,
|
||||
'iUpdateCount' => 0,
|
||||
'iMode' => DEL_AUTO,
|
||||
'sIssue' => null,
|
||||
],
|
||||
'NetworkDevice' => [
|
||||
'iDeleteCount' => 2,
|
||||
'iUpdateCount' => 0,
|
||||
'iMode' => DEL_SILENT,
|
||||
'sIssue' => null,
|
||||
],
|
||||
'Person' => [
|
||||
'iDeleteCount' => 0,
|
||||
'iUpdateCount' => 1,
|
||||
'iMode' => 0,
|
||||
'sIssue' => null,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
//--- ExecuteDeletionPlan tests ---
|
||||
|
||||
/**
|
||||
* Tests that ExecuteDeletionPlan throws a DataFeatureRemovalException when the deletion plan contains issues.
|
||||
*/
|
||||
public function testExecuteDeletionPlanThrowsExceptionWhenIssuesExist(): void
|
||||
{
|
||||
$this->RequireOnceItopFile('env-production/combodo-data-feature-removal/src/Helper/DataFeatureRemovalException.php');
|
||||
|
||||
$oDeletionPlan = $this->createMock(DeletionPlan::class);
|
||||
$oDeletionPlan->method('GetIssues')->willReturn(['Some issue']);
|
||||
$oDeletionPlan->method('ListDeletes')->willReturn([]);
|
||||
$oDeletionPlan->method('ListUpdates')->willReturn([]);
|
||||
|
||||
$oMockService = $this->getMockBuilder(DeletionPlanService::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['GetDeletionPlan'])
|
||||
->getMock();
|
||||
|
||||
$oMockService->method('GetDeletionPlan')->willReturn($oDeletionPlan);
|
||||
|
||||
$this->expectException(DataFeatureRemovalException::class);
|
||||
$this->expectExceptionMessage('Deletion Plan cannot be executed due to issues');
|
||||
|
||||
$oMockService->ExecuteDeletionPlan(['SomeClass']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
|
||||
<classes>
|
||||
<class id="DFRToRemove" _created_in="itop-structure" _delta="define">
|
||||
<properties>
|
||||
<category>bizmodel,searchable</category>
|
||||
<abstract>true</abstract>
|
||||
<db_table>dfrtoremove</db_table>
|
||||
<naming>
|
||||
<attributes/>
|
||||
</naming>
|
||||
<reconciliation>
|
||||
<attributes/>
|
||||
</reconciliation>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString">
|
||||
<sql>name</sql>
|
||||
<default_value/>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
<validation_pattern/>
|
||||
<dependencies/>
|
||||
<tracking_level>all</tracking_level>
|
||||
</field>
|
||||
</fields>
|
||||
<methods/>
|
||||
<presentation>
|
||||
<list>
|
||||
<items/>
|
||||
</list>
|
||||
<search>
|
||||
<items/>
|
||||
</search>
|
||||
<details>
|
||||
<items>
|
||||
<item id="name">
|
||||
<rank>10</rank>
|
||||
</item>
|
||||
</items>
|
||||
</details>
|
||||
</presentation>
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
</class>
|
||||
<class id="DFRToUpdate" _created_in="itop-structure" _delta="define">
|
||||
<properties>
|
||||
<category>bizmodel,searchable</category>
|
||||
<abstract>false</abstract>
|
||||
<db_table>dfrtoupdate</db_table>
|
||||
<naming>
|
||||
<attributes/>
|
||||
</naming>
|
||||
<reconciliation>
|
||||
<attributes/>
|
||||
</reconciliation>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString">
|
||||
<sql>name</sql>
|
||||
<default_value/>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
<validation_pattern/>
|
||||
<dependencies/>
|
||||
<tracking_level>all</tracking_level>
|
||||
</field>
|
||||
<field id="extkey_id" xsi:type="AttributeExternalKey">
|
||||
<sql>extkey_id</sql>
|
||||
<filter/>
|
||||
<dependencies/>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
<target_class>DFRToRemove</target_class>
|
||||
<on_target_delete>DEL_AUTO</on_target_delete>
|
||||
<tracking_level>all</tracking_level>
|
||||
</field>
|
||||
</fields>
|
||||
<methods/>
|
||||
<presentation>
|
||||
<list>
|
||||
<items/>
|
||||
</list>
|
||||
<search>
|
||||
<items/>
|
||||
</search>
|
||||
<details>
|
||||
<items>
|
||||
<item id="name">
|
||||
<rank>10</rank>
|
||||
</item>
|
||||
<item id="extkey_id">
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
</items>
|
||||
</details>
|
||||
</presentation>
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
</class>
|
||||
<class id="DFRRemovedCollateral" _created_in="itop-structure" _delta="define">
|
||||
<properties>
|
||||
<category>bizmodel,searchable</category>
|
||||
<abstract>false</abstract>
|
||||
<db_table>dfrremovedcollateral</db_table>
|
||||
<naming>
|
||||
<attributes/>
|
||||
</naming>
|
||||
<reconciliation>
|
||||
<attributes/>
|
||||
</reconciliation>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString">
|
||||
<sql>name</sql>
|
||||
<default_value/>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
<validation_pattern/>
|
||||
<dependencies/>
|
||||
<tracking_level>all</tracking_level>
|
||||
</field>
|
||||
<field id="extkey_id" xsi:type="AttributeExternalKey">
|
||||
<sql>extkey_id</sql>
|
||||
<filter/>
|
||||
<dependencies/>
|
||||
<is_null_allowed>false</is_null_allowed>
|
||||
<target_class>DFRToRemove</target_class>
|
||||
<on_target_delete>DEL_AUTO</on_target_delete>
|
||||
<tracking_level>all</tracking_level>
|
||||
</field>
|
||||
</fields>
|
||||
<methods/>
|
||||
<presentation>
|
||||
<list>
|
||||
<items/>
|
||||
</list>
|
||||
<search>
|
||||
<items/>
|
||||
</search>
|
||||
<details>
|
||||
<items>
|
||||
<item id="name">
|
||||
<rank>10</rank>
|
||||
</item>
|
||||
<item id="extkey_id">
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
</items>
|
||||
</details>
|
||||
</presentation>
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
</class>
|
||||
<class id="DFRToRemoveLeaf" _created_in="itop-structure" _delta="define">
|
||||
<properties>
|
||||
<category>bizmodel,searchable</category>
|
||||
<abstract>false</abstract>
|
||||
<db_table>dfrtoremoveleaf</db_table>
|
||||
<naming>
|
||||
<attributes/>
|
||||
</naming>
|
||||
<reconciliation>
|
||||
<attributes/>
|
||||
</reconciliation>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="desc" xsi:type="AttributeString">
|
||||
<sql>desc</sql>
|
||||
<default_value/>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
<validation_pattern/>
|
||||
<dependencies/>
|
||||
<tracking_level>all</tracking_level>
|
||||
</field>
|
||||
</fields>
|
||||
<methods/>
|
||||
<presentation>
|
||||
<list>
|
||||
<items/>
|
||||
</list>
|
||||
<search>
|
||||
<items/>
|
||||
</search>
|
||||
<details>
|
||||
<items>
|
||||
<item id="name">
|
||||
<rank>10</rank>
|
||||
</item>
|
||||
<item id="desc">
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
</items>
|
||||
</details>
|
||||
</presentation>
|
||||
<parent>DFRToRemove</parent>
|
||||
</class>
|
||||
<class id="DFRRemovedCollateralCascade" _created_in="itop-structure" _delta="define">
|
||||
<properties>
|
||||
<category>bizmodel,searchable</category>
|
||||
<abstract>false</abstract>
|
||||
<db_table>dfrremovedcollateralcascade</db_table>
|
||||
<naming>
|
||||
<attributes/>
|
||||
</naming>
|
||||
<reconciliation>
|
||||
<attributes/>
|
||||
</reconciliation>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString">
|
||||
<sql>name</sql>
|
||||
<default_value/>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
<validation_pattern/>
|
||||
<dependencies/>
|
||||
<tracking_level>all</tracking_level>
|
||||
</field>
|
||||
<field id="extkey_id" xsi:type="AttributeExternalKey">
|
||||
<sql>extkey_id</sql>
|
||||
<filter/>
|
||||
<dependencies/>
|
||||
<is_null_allowed>false</is_null_allowed>
|
||||
<target_class>DFRRemovedCollateral</target_class>
|
||||
<on_target_delete>DEL_AUTO</on_target_delete>
|
||||
<tracking_level>all</tracking_level>
|
||||
</field>
|
||||
</fields>
|
||||
<methods/>
|
||||
<presentation>
|
||||
<list>
|
||||
<items/>
|
||||
</list>
|
||||
<search>
|
||||
<items/>
|
||||
</search>
|
||||
<details>
|
||||
<items>
|
||||
<item id="name">
|
||||
<rank>10</rank>
|
||||
</item>
|
||||
<item id="extkey_id">
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
</items>
|
||||
</details>
|
||||
</presentation>
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
</class>
|
||||
<class id="DFRManual" _created_in="itop-structure" _delta="define">
|
||||
<properties>
|
||||
<category>bizmodel,searchable</category>
|
||||
<abstract>false</abstract>
|
||||
<db_table>dfrmanual</db_table>
|
||||
<naming>
|
||||
<attributes/>
|
||||
</naming>
|
||||
<reconciliation>
|
||||
<attributes/>
|
||||
</reconciliation>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString">
|
||||
<sql>name</sql>
|
||||
<default_value/>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
<validation_pattern/>
|
||||
<dependencies/>
|
||||
<tracking_level>all</tracking_level>
|
||||
</field>
|
||||
<field id="extkey_id" xsi:type="AttributeExternalKey">
|
||||
<sql>extkey_id</sql>
|
||||
<filter/>
|
||||
<dependencies/>
|
||||
<is_null_allowed>false</is_null_allowed>
|
||||
<target_class>DFRToRemove</target_class>
|
||||
<on_target_delete>DEL_MANUAL</on_target_delete>
|
||||
<tracking_level>all</tracking_level>
|
||||
</field>
|
||||
</fields>
|
||||
<methods/>
|
||||
<presentation>
|
||||
<list>
|
||||
<items/>
|
||||
</list>
|
||||
<search>
|
||||
<items/>
|
||||
</search>
|
||||
<details>
|
||||
<items>
|
||||
<item id="name">
|
||||
<rank>10</rank>
|
||||
</item>
|
||||
<item id="extkey_id">
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
</items>
|
||||
</details>
|
||||
</presentation>
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
</class>
|
||||
</classes>
|
||||
<dictionaries>
|
||||
<dictionary id="EN US">
|
||||
<entries>
|
||||
<entry id="Class:DFRToRemove/Name" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRToRemove/ComplementaryName" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRToRemove" _delta="define"><![CDATA[DFRToRemove]]></entry>
|
||||
<entry id="Class:DFRToRemove+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRToRemove/Attribute:name" _delta="define"><![CDATA[Name]]></entry>
|
||||
<entry id="Class:DFRToRemove/Attribute:name+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRToUpdate/Name" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRToUpdate/ComplementaryName" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRToUpdate" _delta="define"><![CDATA[DFRToUpdate]]></entry>
|
||||
<entry id="Class:DFRToUpdate+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRToUpdate/Attribute:name" _delta="define"><![CDATA[Name]]></entry>
|
||||
<entry id="Class:DFRToUpdate/Attribute:name+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRToUpdate/Attribute:extkey_id" _delta="define"><![CDATA[Dfrtoremove id]]></entry>
|
||||
<entry id="Class:DFRToUpdate/Attribute:extkey_id+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateral/Name" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateral/ComplementaryName" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateral" _delta="define"><![CDATA[DFRRemovedCollateral]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateral+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateral/Attribute:name" _delta="define"><![CDATA[Name]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateral/Attribute:name+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateral/Attribute:extkey_id" _delta="define"><![CDATA[Dfrtoremove id]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateral/Attribute:extkey_id+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRToRemoveLeaf/Name" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRToRemoveLeaf/ComplementaryName" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRToRemoveLeaf" _delta="define"><![CDATA[DFRToRemoveLeaf]]></entry>
|
||||
<entry id="Class:DFRToRemoveLeaf+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRToRemoveLeaf/Attribute:desc" _delta="define"><![CDATA[Desc]]></entry>
|
||||
<entry id="Class:DFRToRemoveLeaf/Attribute:desc+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateralCascade/Name" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateralCascade/ComplementaryName" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateralCascade" _delta="define"><![CDATA[DFRRemovedCollateralCascade]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateralCascade+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateralCascade/Attribute:name" _delta="define"><![CDATA[Name]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateralCascade/Attribute:name+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateralCascade/Attribute:extkey_id" _delta="define"><![CDATA[Dfrremovedcollateral id]]></entry>
|
||||
<entry id="Class:DFRRemovedCollateralCascade/Attribute:extkey_id+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRManual/Name" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRManual/ComplementaryName" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRManual" _delta="define"><![CDATA[DFRManual]]></entry>
|
||||
<entry id="Class:DFRManual+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRManual/Attribute:name" _delta="define"><![CDATA[Name]]></entry>
|
||||
<entry id="Class:DFRManual/Attribute:name+" _delta="define"><![CDATA[]]></entry>
|
||||
<entry id="Class:DFRManual/Attribute:extkey_id" _delta="define"><![CDATA[Dfrtoremove id]]></entry>
|
||||
<entry id="Class:DFRManual/Attribute:extkey_id+" _delta="define"><![CDATA[]]></entry>
|
||||
</entries>
|
||||
</dictionary>
|
||||
</dictionaries>
|
||||
</itop_design>
|
||||
Reference in New Issue
Block a user