diff --git a/phpunit/functional/Glpi/Inventory/Assets/Environment.php b/phpunit/functional/Glpi/Inventory/Assets/Environment.php index e4755da69c9..e0d5112324a 100644 --- a/phpunit/functional/Glpi/Inventory/Assets/Environment.php +++ b/phpunit/functional/Glpi/Inventory/Assets/Environment.php @@ -50,6 +50,12 @@ public static function assetProvider(): array 'xml' => " + + pc002 + + + ggheb7ne7 + LC_ALL C @@ -144,4 +150,52 @@ public function testHandle() 'Environments has not been linked to computer :(' ); } + + public function testGenericAssetEnvironment(): void + { + /** @var \DBmysql $DB */ + global $DB; + + //create generic asset + $definition = $this->initAssetDefinition( + system_name: 'MyAsset' . $this->getUniqueString(), + capacities: array_merge( + [ + \Glpi\Asset\Capacity\IsInventoriableCapacity::class, + \Glpi\Asset\Capacity\HasEnvironmentsCapacity::class + ] + ) + ); + $classname = $definition->getAssetClassName(); + + $this->login(); + $conf = new \Glpi\Inventory\Conf(); + $this->assertTrue($conf->saveConf([ + 'import_env' => 1, + ])); + $this->logout(); + + $converter = new \Glpi\Inventory\Converter(); + $data = $converter->convert(self::assetProvider()[0]['xml']); + $json = json_decode($data); + //we change itemtype to our asset + $json->itemtype = $classname; + $inventory = $this->doInventory($json); + + //check created asset + $assets_id = $inventory->getAgent()->fields['items_id']; + $this->assertGreaterThan(0, $assets_id); + $asset = new $classname(); + $this->assertTrue($asset->getFromDB($assets_id)); + + $this->assertSame( + 4, + countElementsInTable(\Item_Environment::getTable(), ['itemtype' => $classname, 'items_id' => $assets_id]), + 'Environments has not been linked to asset :(' + ); + + //check for tab presence + $this->login(); + $this->assertArrayHasKey('Item_Environment$1', $asset->defineAllTabs()); + } } diff --git a/phpunit/functional/Glpi/Inventory/Assets/Process.php b/phpunit/functional/Glpi/Inventory/Assets/Process.php index 4553c154d5c..cc1e5d2f6ac 100644 --- a/phpunit/functional/Glpi/Inventory/Assets/Process.php +++ b/phpunit/functional/Glpi/Inventory/Assets/Process.php @@ -50,6 +50,12 @@ public static function assetProvider(): array 'xml' => " + + pc002 + + + ggheb7ne7 + php-fpm: pool www 0.0 @@ -157,4 +163,52 @@ public function testHandle() 'Process has not been linked to computer :(' ); } + + public function testGenericAssetProcess(): void + { + /** @var \DBmysql $DB */ + global $DB; + + //create generic asset + $definition = $this->initAssetDefinition( + system_name: 'MyAsset' . $this->getUniqueString(), + capacities: array_merge( + [ + \Glpi\Asset\Capacity\IsInventoriableCapacity::class, + \Glpi\Asset\Capacity\HasProcessCapacity::class + ] + ) + ); + $classname = $definition->getAssetClassName(); + + $this->login(); + $conf = new \Glpi\Inventory\Conf(); + $this->assertTrue($conf->saveConf([ + 'import_process' => 1, + ])); + $this->logout(); + + $converter = new \Glpi\Inventory\Converter(); + $data = $converter->convert(self::assetProvider()[0]['xml']); + $json = json_decode($data); + //we change itemtype to our asset + $json->itemtype = $classname; + $inventory = $this->doInventory($json); + + //check created asset + $assets_id = $inventory->getAgent()->fields['items_id']; + $this->assertGreaterThan(0, $assets_id); + $asset = new $classname(); + $this->assertTrue($asset->getFromDB($assets_id)); + + $this->assertSame( + 1, + countElementsInTable(\Item_Process::getTable(), ['itemtype' => $classname, 'items_id' => $assets_id]), + 'Process has not been linked to asset :(' + ); + + //check for tab presence + $this->login(); + $this->assertArrayHasKey('Item_Process$1', $asset->defineAllTabs()); + } } diff --git a/src/Glpi/Asset/Capacity/HasEnvironmentsCapacity.php b/src/Glpi/Asset/Capacity/HasEnvironmentsCapacity.php new file mode 100644 index 00000000000..382d8221c7c --- /dev/null +++ b/src/Glpi/Asset/Capacity/HasEnvironmentsCapacity.php @@ -0,0 +1,86 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Asset\Capacity; + +use CommonGLPI; +use Item_Environment; +use Session; + +class HasEnvironmentsCapacity extends AbstractCapacity +{ + public function getLabel(): string + { + return Item_Environment::getTypeName(Session::getPluralNumber()); + } + + public function getIcon(): string + { + return Item_Environment::getIcon(); + } + + public function isUsed(string $classname): bool + { + return parent::isUsed($classname) + && $this->countAssetsLinkedToPeerItem($classname, Item_Environment::class) > 0; + } + + public function getCapacityUsageDescription(string $classname): string + { + return sprintf( + __('%1$s environments attached to %2$s assets'), + $this->countPeerItemsUsage($classname, Item_Environment::class), + $this->countAssetsLinkedToPeerItem($classname, Item_Environment::class) + ); + } + + public function onClassBootstrap(string $classname): void + { + $this->registerToTypeConfig('environment_types', $classname); + CommonGLPI::registerStandardTab($classname, Item_Environment::class, 85); + } + + public function onCapacityDisabled(string $classname): void + { + $this->unregisterFromTypeConfig('environment_types', $classname); + + $appliance_item = new Item_Environment(); + $appliance_item->deleteByCriteria([ + 'itemtype' => $classname, + ], true, false); + + $this->deleteRelationLogs($classname, Item_Environment::class); + } +} diff --git a/src/Glpi/Asset/Capacity/HasProcessCapacity.php b/src/Glpi/Asset/Capacity/HasProcessCapacity.php new file mode 100644 index 00000000000..605edb216d2 --- /dev/null +++ b/src/Glpi/Asset/Capacity/HasProcessCapacity.php @@ -0,0 +1,88 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Asset\Capacity; + +use Appliance; +use Appliance_Item; +use CommonGLPI; +use Item_Process; +use Session; + +class HasProcessCapacity extends AbstractCapacity +{ + public function getLabel(): string + { + return Item_Process::getTypeName(Session::getPluralNumber()); + } + + public function getIcon(): string + { + return Item_Process::getIcon(); + } + + public function isUsed(string $classname): bool + { + return parent::isUsed($classname) + && $this->countAssetsLinkedToPeerItem($classname, Item_Process::class) > 0; + } + + public function getCapacityUsageDescription(string $classname): string + { + return sprintf( + __('%1$s process attached to %2$s assets'), + $this->countPeerItemsUsage($classname, Item_Process::class), + $this->countAssetsLinkedToPeerItem($classname, Item_Process::class) + ); + } + + public function onClassBootstrap(string $classname): void + { + $this->registerToTypeConfig('process_types', $classname); + CommonGLPI::registerStandardTab($classname, Item_Process::class, 85); + } + + public function onCapacityDisabled(string $classname): void + { + $this->unregisterFromTypeConfig('process_types', $classname); + + $appliance_item = new Item_Process(); + $appliance_item->deleteByCriteria([ + 'itemtype' => $classname, + ], true, false); + + $this->deleteRelationLogs($classname, Item_Process::class); + } +} diff --git a/tests/functional/Glpi/Asset/Capacity/HasEnvironmentsCapacity.php b/tests/functional/Glpi/Asset/Capacity/HasEnvironmentsCapacity.php new file mode 100644 index 00000000000..2c914e4dbca --- /dev/null +++ b/tests/functional/Glpi/Asset/Capacity/HasEnvironmentsCapacity.php @@ -0,0 +1,200 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace tests\units\Glpi\Asset\Capacity; + +use DbTestCase; +use Entity; +use Glpi\Tests\Asset\CapacityUsageTestTrait; +use Log; + +class HasEnvironmentsCapacity extends DbTestCase +{ + use CapacityUsageTestTrait; + + protected function getTargetCapacity(): string + { + return \Glpi\Asset\Capacity\HasEnvironmentsCapacity::class; + } + + public function testCapacityActivation(): void + { + global $CFG_GLPI; + + $root_entity_id = getItemByTypeName(Entity::class, '_test_root_entity', true); + + $definition_1 = $this->initAssetDefinition( + capacities: [ + \Glpi\Asset\Capacity\HasEnvironmentsCapacity::class, + \Glpi\Asset\Capacity\HasNotepadCapacity::class, + ] + ); + $classname_1 = $definition_1->getAssetClassName(); + $definition_2 = $this->initAssetDefinition( + capacities: [ + \Glpi\Asset\Capacity\HasHistoryCapacity::class, + ] + ); + $classname_2 = $definition_2->getAssetClassName(); + $definition_3 = $this->initAssetDefinition( + capacities: [ + \Glpi\Asset\Capacity\HasEnvironmentsCapacity::class, + \Glpi\Asset\Capacity\HasHistoryCapacity::class, + ] + ); + $classname_3 = $definition_3->getAssetClassName(); + + $has_capacity_mapping = [ + $classname_1 => true, + $classname_2 => false, + $classname_3 => true, + ]; + + foreach ($has_capacity_mapping as $classname => $has_capacity) { + // Check that the class is globally registered + $this->login(); + if ($has_capacity) { + $this->array($CFG_GLPI['environment_types'])->contains($classname); + } else { + $this->array($CFG_GLPI['environment_types'])->notContains($classname); + } + + // Check that the corresponding tab is present on items + $item = $this->createItem($classname, ['name' => __FUNCTION__, 'entities_id' => $root_entity_id]); + $this->login(); + //no item in db, no tab for Item_Environment + $this->array($item->defineAllTabs())->notHasKey('Item_Environment$1'); + + //no search options + } + } + + public function testCapacityDeactivation(): void + { + /** @var array $CFG_GLPI */ + global $CFG_GLPI; + + $root_entity_id = getItemByTypeName(Entity::class, '_test_root_entity', true); + + $definition_1 = $this->initAssetDefinition( + capacities: [ + \Glpi\Asset\Capacity\HasEnvironmentsCapacity::class, + \Glpi\Asset\Capacity\HasHistoryCapacity::class, + ] + ); + $classname_1 = $definition_1->getAssetClassName(); + $definition_2 = $this->initAssetDefinition( + capacities: [ + \Glpi\Asset\Capacity\HasEnvironmentsCapacity::class, + \Glpi\Asset\Capacity\HasHistoryCapacity::class, + ] + ); + $classname_2 = $definition_2->getAssetClassName(); + + $item_1 = $this->createItem( + $classname_1, + [ + 'name' => __FUNCTION__, + 'entities_id' => $root_entity_id, + ] + ); + + $item_2 = $this->createItem( + $classname_2, + [ + 'name' => __FUNCTION__, + 'entities_id' => $root_entity_id, + ] + ); + + $environment_item_1 = $this->createItem( + \Item_Environment::class, + [ + 'itemtype' => $item_1::class, + 'items_id' => $item_1->getID() + ] + ); + + $environment_item_2 = $this->createItem( + \Item_Environment::class, + [ + 'itemtype' => $item_2::class, + 'items_id' => $item_2->getID() + ] + ); + + + $item_1_logs_criteria = [ + 'itemtype' => $classname_1, + ]; + $item_2_logs_criteria = [ + 'itemtype' => $classname_2, + ]; + + // Ensure relation, display preferences and logs exists, and class is registered to global config + $this->object(\Item_Environment::getById($environment_item_1->getID()))->isInstanceOf(\Item_Environment::class); + $this->integer(countElementsInTable(Log::getTable(), $item_1_logs_criteria))->isEqualTo(2); //create + add environment + $this->object(\Item_Environment::getById($environment_item_2->getID()))->isInstanceOf(\Item_Environment::class); + $this->integer(countElementsInTable(Log::getTable(), $item_2_logs_criteria))->isEqualTo(2); //create + add environment + $this->array($CFG_GLPI['environment_types'])->contains($classname_1); + $this->array($CFG_GLPI['environment_types'])->contains($classname_2); + + // Disable capacity and check that relations have been cleaned, and class is unregistered from global config + $this->boolean($definition_1->update(['id' => $definition_1->getID(), 'capacities' => []]))->isTrue(); + $this->boolean(\Item_Environment::getById($environment_item_1->getID()))->isFalse(); + $this->integer(countElementsInTable(Log::getTable(), $item_1_logs_criteria))->isEqualTo(0); + $this->array($CFG_GLPI['environment_types'])->notContains($classname_1); + + // Ensure relations, logs and global registration are preserved for other definition + $this->object(\Item_Environment::getById($environment_item_2->getID()))->isInstanceOf(\Item_Environment::class); + $this->integer(countElementsInTable(Log::getTable(), $item_2_logs_criteria))->isEqualTo(2); + $this->array($CFG_GLPI['environment_types'])->contains($classname_2); + } + + public function provideIsUsed(): iterable + { + yield [ + 'target_classname' => \Item_Environment::class + ]; + } + + public function provideGetCapacityUsageDescription(): iterable + { + yield [ + 'target_classname' => \Item_Environment::class, + 'expected' => '%d environments attached to %d assets' + ]; + } +} diff --git a/tests/functional/Glpi/Asset/Capacity/HasProcessCapacity.php b/tests/functional/Glpi/Asset/Capacity/HasProcessCapacity.php new file mode 100644 index 00000000000..9bc4d979e5c --- /dev/null +++ b/tests/functional/Glpi/Asset/Capacity/HasProcessCapacity.php @@ -0,0 +1,200 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace tests\units\Glpi\Asset\Capacity; + +use DbTestCase; +use Entity; +use Glpi\Tests\Asset\CapacityUsageTestTrait; +use Log; + +class HasProcessCapacity extends DbTestCase +{ + use CapacityUsageTestTrait; + + protected function getTargetCapacity(): string + { + return \Glpi\Asset\Capacity\HasProcessCapacity::class; + } + + public function testCapacityActivation(): void + { + global $CFG_GLPI; + + $root_entity_id = getItemByTypeName(Entity::class, '_test_root_entity', true); + + $definition_1 = $this->initAssetDefinition( + capacities: [ + \Glpi\Asset\Capacity\HasProcessCapacity::class, + \Glpi\Asset\Capacity\HasNotepadCapacity::class, + ] + ); + $classname_1 = $definition_1->getAssetClassName(); + $definition_2 = $this->initAssetDefinition( + capacities: [ + \Glpi\Asset\Capacity\HasHistoryCapacity::class, + ] + ); + $classname_2 = $definition_2->getAssetClassName(); + $definition_3 = $this->initAssetDefinition( + capacities: [ + \Glpi\Asset\Capacity\HasProcessCapacity::class, + \Glpi\Asset\Capacity\HasHistoryCapacity::class, + ] + ); + $classname_3 = $definition_3->getAssetClassName(); + + $has_capacity_mapping = [ + $classname_1 => true, + $classname_2 => false, + $classname_3 => true, + ]; + + foreach ($has_capacity_mapping as $classname => $has_capacity) { + // Check that the class is globally registered + $this->login(); + if ($has_capacity) { + $this->array($CFG_GLPI['process_types'])->contains($classname); + } else { + $this->array($CFG_GLPI['process_types'])->notContains($classname); + } + + // Check that the corresponding tab is present on items + $item = $this->createItem($classname, ['name' => __FUNCTION__, 'entities_id' => $root_entity_id]); + $this->login(); + //no item in db, no tab for Item_Process + $this->array($item->defineAllTabs())->notHasKey('Item_Process$1'); + + //no search options + } + } + + public function testCapacityDeactivation(): void + { + /** @var array $CFG_GLPI */ + global $CFG_GLPI; + + $root_entity_id = getItemByTypeName(Entity::class, '_test_root_entity', true); + + $definition_1 = $this->initAssetDefinition( + capacities: [ + \Glpi\Asset\Capacity\HasProcessCapacity::class, + \Glpi\Asset\Capacity\HasHistoryCapacity::class, + ] + ); + $classname_1 = $definition_1->getAssetClassName(); + $definition_2 = $this->initAssetDefinition( + capacities: [ + \Glpi\Asset\Capacity\HasProcessCapacity::class, + \Glpi\Asset\Capacity\HasHistoryCapacity::class, + ] + ); + $classname_2 = $definition_2->getAssetClassName(); + + $item_1 = $this->createItem( + $classname_1, + [ + 'name' => __FUNCTION__, + 'entities_id' => $root_entity_id, + ] + ); + + $item_2 = $this->createItem( + $classname_2, + [ + 'name' => __FUNCTION__, + 'entities_id' => $root_entity_id, + ] + ); + + $process_item_1 = $this->createItem( + \Item_Process::class, + [ + 'itemtype' => $item_1::class, + 'items_id' => $item_1->getID() + ] + ); + + $process_item_2 = $this->createItem( + \Item_Process::class, + [ + 'itemtype' => $item_2::class, + 'items_id' => $item_2->getID() + ] + ); + + + $item_1_logs_criteria = [ + 'itemtype' => $classname_1, + ]; + $item_2_logs_criteria = [ + 'itemtype' => $classname_2, + ]; + + // Ensure relation, display preferences and logs exists, and class is registered to global config + $this->object(\Item_Process::getById($process_item_1->getID()))->isInstanceOf(\Item_Process::class); + $this->integer(countElementsInTable(Log::getTable(), $item_1_logs_criteria))->isEqualTo(2); //create + add process + $this->object(\Item_Process::getById($process_item_2->getID()))->isInstanceOf(\Item_Process::class); + $this->integer(countElementsInTable(Log::getTable(), $item_2_logs_criteria))->isEqualTo(2); //create + add process + $this->array($CFG_GLPI['process_types'])->contains($classname_1); + $this->array($CFG_GLPI['process_types'])->contains($classname_2); + + // Disable capacity and check that relations have been cleaned, and class is unregistered from global config + $this->boolean($definition_1->update(['id' => $definition_1->getID(), 'capacities' => []]))->isTrue(); + $this->boolean(\Item_Process::getById($process_item_1->getID()))->isFalse(); + $this->integer(countElementsInTable(Log::getTable(), $item_1_logs_criteria))->isEqualTo(0); + $this->array($CFG_GLPI['process_types'])->notContains($classname_1); + + // Ensure relations, logs and global registration are preserved for other definition + $this->object(\Item_Process::getById($process_item_2->getID()))->isInstanceOf(\Item_Process::class); + $this->integer(countElementsInTable(Log::getTable(), $item_2_logs_criteria))->isEqualTo(2); + $this->array($CFG_GLPI['process_types'])->contains($classname_2); + } + + public function provideIsUsed(): iterable + { + yield [ + 'target_classname' => \Item_Process::class + ]; + } + + public function provideGetCapacityUsageDescription(): iterable + { + yield [ + 'target_classname' => \Item_Process::class, + 'expected' => '%d process attached to %d assets' + ]; + } +}