<?php /* ** Copyright (C) 2001-2025 Zabbix SIA ** ** This program is free software: you can redistribute it and/or modify it under the terms of ** the GNU Affero General Public License as published by the Free Software Foundation, version 3. ** ** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; ** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU Affero General Public License for more details. ** ** You should have received a copy of the GNU Affero General Public License along with this program. ** If not, see <https://www.gnu.org/licenses/>. **/ require_once dirname(__FILE__).'/../common/testPageGroups.php'; require_once dirname(__FILE__).'/../behaviors/CMessageBehavior.php'; require_once dirname(__FILE__).'/../behaviors/CTableBehavior.php'; /** * @backup hosts * * @onBefore preparePageHostGroupsData * * @dataSource DiscoveredHosts, HostTemplateGroups */ class testPageHostGroups extends testPageGroups { /** * Attach TableBehavior to set column names and MessageBehavior to the test. * * @return array */ public function getBehaviors() { return [ CMessageBehavior::class, [ 'class' => CTableBehavior::class, 'column_names' => ['', 'Name', 'Count', 'Hosts', 'Info'] ] ]; } protected $link = 'zabbix.php?action=hostgroup.list'; protected $object = 'host'; const DISCOVERED_HOST = 'Discovered host from prototype 1'; const GROUP = 'Group with two disabled hosts testPageHostGroup'; const HOST1 = 'One disabled host testPageHostGroup'; const HOST2 = 'Two disabled host testPageHostGroup'; /** * Prepare data for enable/disable hosts test. */ public static function preparePageHostGroupsData() { // Create three groups with disabled hosts and two groups with enabled hosts for testing. CDataHelper::call('hostgroup.create', [ [ 'name' => self::GROUP ], [ 'name' => 'Group with disabled host testPageHostGroup' ], [ 'name' => 'Group2 with disabled host testPageHostGroup' ], [ 'name' => 'Group with enabled host testPageHostGroup' ], [ 'name' => 'Group with two enabled hosts testPageHostGroup' ], [ 'name' => 'Group 3 for Delete test' ] ]); $host_groupids = CDataHelper::getIds('name'); CDataHelper::createHosts([ [ 'host' => self::HOST1, 'interfaces' => [], 'status' => HOST_STATUS_NOT_MONITORED, 'groups' => [ 'groupid' => $host_groupids[self::GROUP] ] ], [ 'host' => self::HOST2, 'interfaces' => [], 'status' => HOST_STATUS_NOT_MONITORED, 'groups' => [ 'groupid' => $host_groupids[self::GROUP] ] ], [ 'host' => 'Disabled host testPageHostGroup', 'interfaces' => [], 'status' => HOST_STATUS_NOT_MONITORED, 'groups' => [ 'groupid' => $host_groupids['Group with disabled host testPageHostGroup'] ] ], [ 'host' => 'Disabled host2 testPageHostGroup', 'interfaces' => [], 'status' => HOST_STATUS_NOT_MONITORED, 'groups' => [ 'groupid' => $host_groupids['Group2 with disabled host testPageHostGroup'] ] ], [ 'host' => 'Enabled host testPageHostGroup', 'interfaces' => [], 'groups' => [ 'groupid' => $host_groupids['Group with enabled host testPageHostGroup'] ] ], [ 'host' => 'One enabled host testPageHostGroup', 'interfaces' => [], 'groups' => [ 'groupid' => $host_groupids['Group with two enabled hosts testPageHostGroup'] ] ], [ 'host' => 'Two enabled host testPageHostGroup', 'interfaces' => [], 'groups' => [ 'groupid' => $host_groupids['Group with two enabled hosts testPageHostGroup'] ] ] ]); } public static function getLayoutData() { return [ [ [ [ 'Name' => 'Applications', 'Count' => '', 'Hosts' => '', 'Info' => '' ], [ 'Name' => self::DELETE_ONE_GROUP, 'Count' => '1', 'Hosts' => 'Host for host group testing', 'Info' => '' ], [ 'Name' => self::GROUP, 'Count' => '2', 'Hosts' => self::HOST1.', '.self::HOST2, 'Info' => '' ], [ 'Name' => self::LLD.': '.self::DISCOVERED_GROUP, 'Count' => '1', 'Hosts' => self::DISCOVERED_HOST, 'Info' => '' ], [ 'Name' => '1st LLD, ..., sixth LLD: グループプロトタイプ番号 1 KEY', 'Count' => '6', 'Hosts' => 'KEY host prototype number 0, KEY host prototype number 1, KEY host prototype number 2,'. ' KEY host prototype number 3, KEY host prototype number 4, KEY host prototype number 5', 'Info' => '' ], [ 'Name' => 'LLD number 8, ..., sevenths LLD: Trešais grupu prototips KEY', 'Count' => '3', 'Hosts' => 'KEY host prototype number 6, KEY host prototype number 7, KEY host prototype number 8', 'Info' => '' ], [ 'Name' => '15th LLD 🙃^天!, 16th LLD: Two prototype group KEY', 'Count' => '2', 'Hosts' => 'KEY host prototype number 14, KEY host prototype number 15', 'Info' => '' ] ] ] ]; } /** * @dataProvider getLayoutData */ public function testPageHostGroups_Layout($data) { $links = [ 'name' => self::DISCOVERED_GROUP, 'lld' => self::LLD, 'count' => '1', 'host_template' => self::DISCOVERED_HOST ]; $this->checkLayout($data, $links); } public function testPageHostGroups_Sort() { $this->checkColumnSorting(); } public static function getHostGroupsFilterData() { return [ // Too many spaces in field. [ [ 'Name' => ' host' ] ], [ [ 'Name' => 'host ' ] ], // Template group name. [ [ 'Name' => 'Templates' ] ], // Exact match. [ [ 'Name' => 'Group with disabled host testPageHostGroup', 'expected' => ['Group with disabled host testPageHostGroup'] ] ], // Partial match. [ [ 'Name' => 'with two enabled hosts', 'expected' => ['Group with two enabled hosts testPageHostGroup'] ] ], [ [ 'Name' => ' enabled ', 'expected' => ['Group with enabled host testPageHostGroup', 'Group with two enabled hosts testPageHostGroup' ] ] ], [ [ 'Name' => 'with disabled', 'expected' => ['Group2 with disabled host testPageHostGroup', 'Group with disabled host testPageHostGroup'] ] ], // Not case sensitive. [ [ 'Name' => 'group2', 'expected' => ['Group2 with disabled host testPageHostGroup'] ] ], [ [ 'Name' => 'GROUP2', 'expected' => ['Group2 with disabled host testPageHostGroup'] ] ] ]; } /** * @dataProvider getFilterData * @dataProvider getHostGroupsFilterData */ public function testPageHostGroups_Filter($data) { $this->filter($data); } public static function getHostGroupsCancelData() { return [ [ [ 'action' => 'Enable hosts', 'message' => 'Enable selected hosts?' ] ], [ [ 'action' => 'Disable hosts', 'message' => 'Disable hosts in the selected host groups?' ] ] ]; } /** * @dataProvider getCancelData * @dataProvider getHostGroupsCancelData */ public function testPageHostGroups_Cancel($data) { $this->cancel($data); } /** * Check that status of host is changed in groups table when change host status in overlay dialog on host group page. */ public function testPageHostGroups_SingleEnableDisable() { $data = [ 'group' => self::GROUP, // Group linked to two hosts, but only one host's status will change. 'change_host' => self::HOST1, 'host' => self::HOST2 ]; $this->page->login()->open($this->link)->waitUntilReady(); $table = $this->getTable(); $hosts = $table->findRow('Name', $data['group'])->getColumn('Hosts'); foreach ([true, false] as $status) { // Change host status. $hosts->query('link', $data['change_host'])->one()->click(); $dialog = COverlayDialogElement::find()->one()->waitUntilReady(); $dialog->asForm()->fill(['Enabled' => $status])->submit(); $dialog->ensureNotPresent(); // Check status in table. $table->waitUntilReloaded(); $this->assertMessage(TEST_GOOD, 'Host updated', 'Updated status of host "'.$data['change_host'].'".'); CMessageElement::find()->one()->close(); $this->assertTrue($hosts->query('link', $data['change_host'])->one()->hasClass($status ? 'green' : 'red')); $this->assertTrue($hosts->query('link', $data['host'])->one()->hasClass('red')); // Check status in DB. $db_status = $status ? HOST_STATUS_MONITORED : HOST_STATUS_NOT_MONITORED; $this->assertEquals(1, CDBHelper::getCount('SELECT NULL FROM hosts WHERE status='.$db_status. ' AND name='.CDBHelper::escape($data['change_host'])) ); $this->assertEquals(1, CDBHelper::getCount('SELECT NULL FROM hosts WHERE status='.HOST_STATUS_NOT_MONITORED. ' AND name='.CDBHelper::escape($data['host'])) ); } } public static function getEnableHostsData() { return [ [ [ 'Applications' => '' ] ], [ [ 'Group with disabled host testPageHostGroup' => ['Disabled host testPageHostGroup'] ] ], [ [ 'Group2 with disabled host testPageHostGroup' => ['Disabled host2 testPageHostGroup'], self::GROUP => [self::HOST1, self::HOST2] ] ] ]; } /** * @dataProvider getEnableHostsData */ public function testPageHostGroups_EnableHosts($data) { $this->checkHostStatusChange($data); } public static function getDisableHostsData() { return [ [ [ 'Group for Host prototype' => '' ] ], [ [ 'Group with enabled host testPageHostGroup' => ['Enabled host testPageHostGroup'] ] ], [ [ self::LLD.': '.self::DISCOVERED_GROUP => [self::DISCOVERED_HOST], 'Group with two enabled hosts testPageHostGroup' => ['One enabled host testPageHostGroup', 'Two enabled host testPageHostGroup'] ] ] ]; } /** * @dataProvider getDisableHostsData */ public function testPageHostGroups_DisableHosts($data) { $this->checkHostStatusChange($data, 'disable'); } private function checkHostStatusChange($data, $status = 'enable') { if (count($data) === 1 && array_values($data)[0] === '') { $old_hash = CDBHelper::getHash(self::HOSTS_SQL); } $this->page->login()->open($this->link)->waitUntilReady(); $table = $this->getTable(); $this->selectTableRows(array_keys($data)); $this->assertSelectedCount(count($data)); $this->query('button', ucfirst($status).' hosts')->one()->click(); $this->page->acceptAlert(); $this->page->waitUntilReady(); $table->waitUntilReloaded(); $this->assertSelectedCount(0); $details = []; foreach ($data as $group => $hosts) { // Skip checks if group without hosts. if (!is_array($hosts)) { $this->assertEquals($old_hash, CDBHelper::getHash(self::HOSTS_SQL)); continue; } $row = $table->findRow('Name', $group); // Check hosts status in DB. $db_status = ($status === 'enable') ? HOST_STATUS_MONITORED : HOST_STATUS_NOT_MONITORED; $this->assertEquals(count($hosts), CDBHelper::getCount('SELECT NULL FROM hosts WHERE status='. $db_status.' AND name IN ('.CDBHelper::escape($hosts).')') ); foreach ($hosts as $host) { // Check hosts color on frontend. $host_link = $row->getColumn('Hosts')->query('link', $host)->one(); $this->assertTrue($host_link->hasClass(($status === 'enable') ? 'green' : 'red')); // Prepare message details text. $details[] = 'Updated status of host "'.$host.'".'; } } $message_title = 'Host'.(count($details) === 1 ? ' ' : 's ').$status.'d'; $this->assertMessage(TEST_GOOD, $message_title, $details); } public static function getHostGroupsDeleteData() { return [ // Delete all. [ [ 'expected' => TEST_BAD, 'error' => 'Host group "Discovered hosts" is group for discovered hosts and cannot be deleted.' ] ], // One of the groups can't be deleted. [ [ 'expected' => TEST_BAD, 'groups' => [self::DELETE_ONE_GROUP, self::LLD.': '.self::DISCOVERED_GROUP], 'error' => 'Host "Host for host group testing" cannot be without host group.' ] ], // The group can't be deleted. [ [ 'expected' => TEST_BAD, 'groups' => self::DELETE_ONE_GROUP, 'error' => 'Host "Host for host group testing" cannot be without host group.' ] ], [ [ 'expected' => TEST_BAD, 'groups' => 'Group for Maintenance', 'error' => 'Cannot delete host group "Group for Maintenance" because maintenance'. ' "Maintenance for host group testing" must contain at least one host or host group.' ] ], [ [ 'expected' => TEST_BAD, 'groups' => 'Group for Correlation', 'error' => 'Group "Group for Correlation" cannot be deleted, because it is used in a correlation condition.' ] ], [ [ 'expected' => TEST_BAD, 'groups' => 'Group for Script', 'error' => 'Host group "Group for Script" cannot be deleted, because it is used in a global script.' ] ], [ [ 'expected' => TEST_BAD, 'groups' => 'Group for Host prototype', 'error' => 'Group "Group for Host prototype" cannot be deleted, because it is used by a host prototype.' ] ], [ [ 'expected' => TEST_BAD, 'groups' => 'Discovered hosts', 'error' => 'Host group "Discovered hosts" is group for discovered hosts and cannot be deleted.' ] ] ]; } /** * @dataProvider getDeleteData * @dataProvider getHostGroupsDeleteData */ public function testPageHostGroups_Delete($data) { $this->delete($data); } public static function getLLDLinksData() { return [ [ [ 'name' => 'Single prototype group KEY', 'links' => ['17th LLD'] ] ], [ [ 'name' => 'Two prototype group KEY', 'links' => ['15th LLD 🙃^天!', '16th LLD'] ] ], [ [ 'name' => 'Trešais grupu prototips KEY', 'links' => ['LLD number 8', 'sevenths LLD'], 'ellipsis' => true ] ] ]; } /** * @dataProvider getLLDLinksData */ public function testPageHostGroups_CheckLLDLinks($data) { $link_ids = CDataHelper::get('HostTemplateGroups.lld_host_prototype_ids'); $this->page->login()->open($this->link)->waitUntilReady(); // Locate the table cell that corresponds to the desired hostgroup name. $table_cell = $this->query('link', $data['name'])->one()->parents('class:wordbreak')->one(); foreach ($data['links'] as $lld_name) { $link = $table_cell->query('link', $lld_name)->one(); $this->assertTrue($link->isClickable()); $link_url = 'host_prototypes.php?form=update&parent_discoveryid='.$link_ids[$lld_name]['lld_id'].'&hostid='. $link_ids[$lld_name]['host_prototype_id'].'&context=host'; $this->assertEquals($link_url, $link->getAttribute('href')); } // Check that 3 dots are added after 1st LLD name, if there's more than 2 parent LLDs and no more LLDs are shown. $name_parts = preg_split('/(,|:) /', $table_cell->getText()); if (array_key_exists('ellipsis', $data)) { $this->assertEquals([$data['links'][0], '...', $data['links'][1], $data['name']], $name_parts); } else { array_push($data['links'], $data['name']); $this->assertEquals($data['links'], $name_parts); } } }