N°8761 - Assist in cleaning up data prior to uninstalling extensions

This commit is contained in:
Eric Espie
2026-03-16 17:44:55 +01:00
parent 37ecd8f63f
commit 8a52604ed9

View File

@@ -0,0 +1,253 @@
<?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\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:
* - Singleton behavior: ensuring GetInstance always returns the same instance, and SetInstance can override it.
* - 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);
$this->assertEmpty($aResult);
}
/**
* 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);
$this->assertEmpty($aResult);
}
/**
* 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);
$this->assertInstanceOf(DeletionPlanSummaryEntity::class, $aResult[$sClass]);
$this->assertEquals($aExpectedValues['iDeleteCount'], $aResult[$sClass]->iDeleteCount);
$this->assertEquals($aExpectedValues['iUpdateCount'], $aResult[$sClass]->iUpdateCount);
$this->assertEquals($aExpectedValues['iMode'], $aResult[$sClass]->iMode);
$this->assertEquals($aExpectedValues['sIssue'], $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('datamodels/2.x/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(\Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException::class);
$this->expectExceptionMessage('Deletion Plan cannot be executed due to issues');
$oMockService->ExecuteDeletionPlan(['SomeClass']);
}
}