You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

759 lines
24 KiB

<?php
/*
** Zabbix
** Copyright (C) 2001-2023 Zabbix SIA
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** 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 General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
require_once dirname(__FILE__).'/../../include/CWebTest.php';
require_once dirname(__FILE__).'/../behaviors/CMessageBehavior.php';
/**
* @backup users, media_type
*
* @onBefore prepareMediaTypeData
*
* @dataSource LoginUsers
*/
class testFormUserMedia extends CWebTest {
private static $mediatype_sql = 'SELECT * FROM media';
/**
* Attach MessageBehavior to the test.
*
* @return array
*/
public function getBehaviors() {
return [CMessageBehavior::class];
}
/**
* Enable media types before test.
*/
public function prepareMediaTypeData() {
$mediatypeids = CDBHelper::getAll("SELECT mediatypeid FROM media_type WHERE name IN ('Email', 'SMS',".
"'Test script', 'MS Teams', 'Slack', 'Zendesk')"
);
foreach ($mediatypeids as $mediatype) {
CDataHelper::call('mediatype.update', [
[
'mediatypeid' => $mediatype['mediatypeid'],
'status' => 0
]
]);
}
}
public function getMediaData() {
return [
// User media with multiple e-mails - all fields specified.
[
[
'expected' => TEST_GOOD,
'fields' => [
'Type' => 'Email',
'When active' => '1-5,09:00-18:00',
'Enabled' => true,
'Use if severity' => ['Average', 'High', 'Disaster']
],
'emails' => [
['email' => '123@456.ttt', 'action' => USER_ACTION_UPDATE, 'index' => 0],
['email' => 'Mr Email <bestEmail@zabbix.com>'],
['email' => '∑Ω-symbols <utf-8@zabbix.coom>'],
['email' => '"Zabbix\@\<H(comment)Q\>" <zabbix@company.com>']
]
]
],
// User media with only mandatory fields specified.
[
[
'expected' => TEST_GOOD,
'fields' => [
'Type' => 'SMS',
'Send to' => '+371 66600666'
],
'additional media' => [
[
'Type' => 'MS Teams',
'Send to' => 'MS Teams channel 666'
],
[
'Type' => 'Zendesk',
'Send to' => '192.168.256.256'
],
[
'Type' => 'Test script',
'Send to' => 'Path to test script'
]
]
]
],
// User with multiple "When active" periods.
[
[
'expected' => TEST_GOOD,
'fields' => [
'Type' => 'SMS',
'Send to' => '+371 66600666',
'When active' => '{$DATE.TIME};6-7,09:00-15:00',
'Enabled' => false
]
]
],
// Empty email address.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'Email'
],
'emails' => [
['email' => ' ', 'action' => USER_ACTION_UPDATE, 'index' => 0]
],
'error_message' => 'Incorrect value for field "sendto_emails": cannot be empty.'
]
],
// Email address without the "@" symbol.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'Email'
],
'emails' => [
['email' => 'no_at.zabbix.com', 'action' => USER_ACTION_UPDATE, 'index' => 0]
],
'error_message' => 'Invalid email address "no_at.zabbix.com".'
]
],
// Email address without the domain.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'Email'
],
'emails' => [
['email' => 'no_domain@zabbix', 'action' => USER_ACTION_UPDATE, 'index' => 0]
],
'error_message' => 'Invalid email address "no_domain@zabbix".'
]
],
// Email address with numbers in domain.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'Email'
],
'emails' => [
['email' => 'number_in_domain@zabbix.2u', 'action' => USER_ACTION_UPDATE, 'index' => 0]
],
'error_message' => 'Invalid email address "number_in_domain@zabbix.2u".'
]
],
// Email address with name and missing "<" and ">" symbols.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'Email'
],
'emails' => [
['email' => 'Mr Person person@zabbix.com', 'action' => USER_ACTION_UPDATE, 'index' => 0]
],
'error_message' => 'Invalid email address "Mr Person person@zabbix.com".'
]
],
// Email address without the recipient specified - just the domain.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'Email'
],
'emails' => [
['email' => '@zabbix.com', 'action' => USER_ACTION_UPDATE, 'index' => 0]
],
'error_message' => 'Invalid email address "@zabbix.com".'
]
],
// Email address that contains a space.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'Email'
],
'emails' => [
['email' => 'person @zabbix.com', 'action' => USER_ACTION_UPDATE, 'index' => 0]
],
'error_message' => 'Invalid email address "person @zabbix.com".'
]
],
// Empty MS Teams channel name.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'MS Teams',
'Send to' => ''
],
'error_message' => 'Incorrect value for field "sendto": cannot be empty.'
]
],
// Empty SMS recipient.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'SMS',
'Send to' => ''
],
'error_message' => 'Incorrect value for field "sendto": cannot be empty.'
]
],
// Empty Test script recipient.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'Test script',
'Send to' => ''
],
'error_message' => 'Incorrect value for field "sendto": cannot be empty.'
]
],
// String in when active.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'SMS',
'Send to' => '192.168.0.1',
'When active' => 'allways'
],
'error_message' => 'Incorrect value for field "period": a time period is expected'
]
],
// Only time period in when active.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'SMS',
'Send to' => '12345678',
'When active' => '00:00-24:00'
],
'error_message' => 'Incorrect value for field "period": a time period is expected'
]
],
// Only days in when active.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'SMS',
'Send to' => '12345678',
'When active' => '1-5'
],
'error_message' => 'Incorrect value for field "period": a time period is expected'
]
],
// When active value is set to a specific moment.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'SMS',
'Send to' => '12345678',
'When active' => '1-5,15:00-15:00'
],
'error_message' => 'Incorrect value for field "period": a time period is expected'
]
],
// When active defined with incorrect order.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'SMS',
'Send to' => '12345678',
'When active' => '15:00-18:00,1-5'
],
'error_message' => 'Incorrect value for field "period": a time period is expected'
]
],
// When active defined using a regular macro.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'SMS',
'Send to' => '12345678',
'When active' => '{TIME}'
],
'error_message' => 'Incorrect value for field "period": a time period is expected'
]
],
// When active defined using a LLD macro.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'SMS',
'Send to' => '12345678',
'When active' => '{#DATE.TIME}'
],
'error_message' => 'Incorrect value for field "period": a time period is expected'
]
],
// Multiple When active periods separated by comma.
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => 'SMS',
'Send to' => '12345678',
'When active' => '1-5,09:00-18:00,6-7,12:00-15:00'
],
'error_message' => 'Incorrect value for field "period": a time period is expected'
]
]
];
}
/**
* @dataProvider getMediaData
*/
public function testFormUserMedia_Add($data) {
$old_hash = CDBHelper::getHash(self::$mediatype_sql);
// Open the user media tab for user-zabbix user.
$user_form = $this->getUserMediaTab('user-zabbix');
// Check that no media are configured.
$this->assertTrue($user_form->getField('Media')->getRows()->count() === 0);
// Add media.
$add_button = $this->query('button:Add')->one();
$add_button->click();
$this->setMediaValues($data);
// Check if media was added and its configuration.
if ($data['expected'] === TEST_GOOD) {
$this->checkMediaConfiguration($data);
// Add other media if required.
foreach (CTestArrayHelper::get($data, 'additional media', []) as $i => $media) {
$add_button->click();
$media_form = COverlayDialogElement::find()->one()->waitUntilReady()->asForm();
$media_form->fill($media);
$media_form->submit();
$this->page->waitUntilReady();
$user_form->invalidate();
$this->assertEquals($user_form->getField('Media')->asTable()->getRows()->count(), $i + 2);
}
}
else {
$this->assertMessage(TEST_BAD, null, $data['error_message']);
$this->assertEquals($old_hash, CDBHelper::getHash(self::$mediatype_sql));
}
}
/**
* @dataProvider getMediaData
*/
public function testFormUserMedia_Edit($data) {
$old_hash = CDBHelper::getHash(self::$mediatype_sql);
// Edit selected media.
$edit_row = $this->getUserMediaTab('Admin')->asTable()->query('xpath:.//tr[@id="medias_0"]')->one()->asTableRow();
$original_period = $edit_row->getColumn('When active')->getText();
$edit_row->query('button:Edit')->one()->click();
$this->setMediaValues($data);
// Check if media was updated and its configuration.
if ($data['expected'] === TEST_GOOD) {
$this->checkMediaConfiguration($data, $original_period, true);
}
else {
$this->assertMessage(TEST_BAD, null, $data['error_message']);
$this->assertEquals($old_hash, CDBHelper::getHash(self::$mediatype_sql));
}
}
public function testFormUserMedia_DisabledMediaTypes() {
// Get Media types table.
$mediatype_table = $this->getUserMediaTab('Admin')->getField('Media')->asTable();
// Check that disabled media types have popup icon in Type column.
$discord_row = $mediatype_table->findRow('Type', 'Discord', true);
$type_column = $discord_row->getColumn('Type');
$this->assertTrue($type_column->query('xpath:.//button['.CXPathHelper::fromClass('zi-i-warning').']')->one()->isValid());
$this->assertEquals('Media type disabled by Administration.', $type_column->query('class:hint-box')->one()->getText());
// Check that status of disabled media types is not cickable.
$this->assertFalse($discord_row->getColumn('Status')->query('xpath:.//a')->one(false)->isValid());
// Check that disabled media types are shown in red color in media configuration form.
$discord_row->query('button:Edit')->one()->click();
$dialog = COverlayDialogElement::find()->waitUntilReady()->one();
$this->assertEquals('focusable red', $dialog->asForm()->getField('Type')->query('button:Discord')->one()->getAttribute('class'));
$dialog->close();
// Check that there is no icon and no hintbox for disabled user mediathat belong to enabled media type.
$email_row = $mediatype_table->findRow('Send to', 'test2@zabbix.com');
$type_column = $email_row->getColumn('Type');
foreach (["xpath:.//a[".CXPathHelper::fromClass('icon-info')."]", 'class:hint-box'] as $selector) {
$this->assertFalse($type_column->query($selector)->one(false)->isValid());
}
// Check that status of disabled user media is cickable.
$this->assertTrue($email_row->getColumn('Status')->query('button:Disabled')->one()->isValid());
// Check that disabled media types are not shown if user media with enabled media type is edited.
$email_row->query('button:Edit')->one()->click();
$type_field = COverlayDialogElement::find()->waitUntilReady()->one()->asForm()->getField('Type');
$this->assertFalse($type_field->query('button:Discord')->one(false)->isValid());
$this->assertFalse($type_field->query('class:focusable red')->one(false)->isValid());
}
public function testFormUserMedia_StatusChangeAndRemove() {
$old_hash = CDBHelper::getHash(self::$mediatype_sql);
$this->page->login();
foreach (['Cancel', 'Update'] as $action) {
$this->page->open('zabbix.php?action=user.edit&userid=1');
$this->query('id:tab_mediaTab')->waitUntilVisible()->one()->click();
$table = $this->query('xpath://ul[@id="userMediaFormList"]//table')->asTable()->one();
// Change status of one of the media.
$row = $table->findRow('Send to', 'test@zabbix.com');
$this->assertEquals($row->getColumn('Status')->getText(), 'Enabled');
$row->getColumn('Status')->click();
$this->assertEquals($row->getColumn('Status')->getText(), 'Disabled');
// Remove one of the media.
$row->getColumn('Action')->query('button:Remove')->one()->click();
$this->assertFalse($table->findRow('Send to', 'test@zabbix.com')->isValid());
$this->query('button', $action)->one()->click();
$this->query('link', 'Admin')->waitUntilVisible()->one()->click();
$this->query('id:tab_mediaTab')->waitUntilVisible()->one()->click();
if ($action === 'Update') {
$this->assertFalse($table->findRow('Send to', 'test@zabbix.com')->isValid());
}
else {
$this->assertEquals($old_hash, CDBHelper::getHash(self::$mediatype_sql));
}
}
}
public function testFormUserMedia_EmailRemoval() {
$emails = [
['email' => '0@zabbix.com', 'action' => USER_ACTION_UPDATE, 'index' => 0],
['email' => '1@zabbix.com'],
['email' => '2@zabbix.com'],
['email' => '3@zabbix.com']
];
$this->page->login()->open('zabbix.php?action=user.edit&userid=50');
$user_form = $this->query('name:user_form')->asForm()->waitUntilPresent()->one();
$user_form->selectTab('Media');
// Add media with multiple emails.
$user_form->query('button:Add')->one()->click();
$media_form = $this->query('name:media_form')->waitUntilVisible()->asForm()->one();
$media_form->getField('Type')->fill('Email');
$email_list = $media_form->getField('Send to')->asMultifieldTable();
$email_list->setFieldMapping(['email'])->fill($emails);
// Check that all emails are entered in media configuration form.
$this->assertEquals($email_list->getRows()->count(), count($emails));
// Remove email 3@zabbix.com and check that it's removed.
$this->removeEmailFromList('3@zabbix.com');
$this->checkEmailNotPresent('3@zabbix.com');
// Edit the media - remove email 2@zabbix.com and check that it's removed.
$media_list = $user_form->getField('Media')->asTable()->waitUntilVisible();
$row = $media_list->getRow(0);
$row->query('button:Edit')->one()->click();
$this->removeEmailFromList('2@zabbix.com');
$this->checkEmailNotPresent('3@zabbix.com');
}
public function getUserData() {
return [
// Create a user with media.
[
[
'action' => 'create',
'user_fields' => [
'Username' => 'created-user',
'Groups' => 'Zabbix administrators',
'Password' => 'test5678',
'Password (once again)' => 'test5678'
],
'media_fields' => [
'Type' => 'SMS',
'Send to' => '+371 74661x'
],
'role' => 'Super admin role',
'expected_message' => 'User added'
]
],
// Update a user with media.
[
[
'action' => 'update',
'username' => 'user-zabbix',
'media_fields' => [
'Type' => 'Slack',
'Send to' => 'zabbix channel'
],
'expected_message' => 'User updated'
]
],
// Delete a user with media.
[
[
'action' => 'delete',
'username' => 'test-user',
'expected_message' => 'User deleted'
]
]
];
}
/**
* @dataProvider getUserData
*/
public function testFormUserMedia_UserWithMediaActions($data) {
$this->page->login()->open('zabbix.php?action=user.list');
// Fill in user form for the created user or just open an existing one.
if ($data['action'] === 'create') {
$this->page->query('button:Create user')->one()->click();
$user_form = $this->query('name:user_form')->asForm()->waitUntilVisible()->one();
$user_form->fill($data['user_fields']);
$user_form->selectTab('Permissions');
$user_form->fill(['Role' => $data['role']]);
}
else {
$this->query('link', $data['username'])->waitUntilVisible()->one()->click();
$user_form = $this->query('name:user_form')->asForm()->waitUntilVisible()->one();
}
// Fill in and submit user media form.
if ($data['action'] !== 'delete') {
$user_form->selectTab('Media');
$user_form->query('button:Add')->one()->click();
$media_form = $this->query('id:media_form')->asForm()->waitUntilVisible()->one();
$media_form->fill($data['media_fields']);
$media_form->submit();
$this->page->waitUntilReady();
}
switch ($data['action']) {
case 'create':
case 'update':
$user_form->submit();
break;
case 'delete':
// Get userid for of the user to be deleted to verify media deletion along with the user.
$userid = CDBHelper::getValue('SELECT userid FROM users WHERE username ='.zbx_dbstr($data['username']));
$this->query('button:Delete')->one()->click();
$this->page->acceptAlert();
break;
}
// Check that the action took place.
$this->assertMessage(TEST_GOOD, $data['expected_message']);
if ($data['action'] === 'delete') {
$this->assertEquals(0, CDBHelper::getCount('SELECT null FROM media WHERE userid='.zbx_dbstr($userid)));
}
else {
$user = CTestArrayHelper::get($data, 'user_fields.Username') ? $data['user_fields']['Username'] : $data['username'];
$this->query('link', $user)->waitUntilVisible()->one()->click();
$user_form = $this->query('name:user_form')->asForm()->waitUntilVisible()->one();
$media_field = $user_form->getField('Media')->asTable();
$this->assertTrue($media_field->getRows()->count() === 1);
$row = $media_field->getRow(0);
// Verify the values of "Type" and "Send to" for the created and updated media.
foreach ($data['media_fields'] as $field => $value) {
$this->assertEquals($row->getColumn($field)->getText(), $value);
}
}
}
/**
* Open Media tab of user configuration form for a defined user.
*
* @param string $user User for which the Media tab should be opened.
*
* @return CFormElement
*/
private function getUserMediaTab($user) {
$this->page->login()->open('zabbix.php?action=user.list');
$this->query('link', $user)->waitUntilVisible()->one()->click();
$user_form = $this->query('name:user_form')->asForm()->waitUntilPresent()->one()->selectTab('Media');
return $user_form;
}
/**
* Remove the specified Email from the "Send to" field of a media type.
*
* @param string $email Email to be removed from the "Send to" field.
*/
private function removeEmailFromList($email) {
$media_form = $this->query('name:media_form')->waitUntilVisible()->asForm()->one();
$email_list = $media_form->getField('Send to')->asMultifieldTable(['mapping' => ['email']]);
// Remove the email from the list.
$email_list->fill(['action' => USER_ACTION_REMOVE, 'email' => $email]);
$media_form->submit();
$this->page->waitUntilReady();
}
/**
* Check that the removed email is not present in 'Send to' field.
*
* @param string $email Email that should be removed from the "Send to" field.
*/
private function checkEmailNotPresent($email) {
$user_form = $this->query('name:user_form')->asForm()->waitUntilVisible()->one();
$row = $user_form->getField('Media')->asTable()->getRow(0);
$this->assertStringNotContainsString($email, $row->getColumn('Send to')->getText());
}
/**
* Populate the media type configuration form.
*
* @param array $data data provider
*/
private function setMediaValues($data) {
$media_form = $this->query('id:media_form')->waitUntilVisible()->asForm()->one();
$media_form->fill($data['fields']);
// Check that there is possibility to add only multiple emails to media.
$clickable = ($data['fields']['Type'] === 'Email');
foreach (['id:email_send_to_add', 'button:Remove'] as $selector) {
$this->assertEquals($clickable, $media_form->query($selector)->one()->isClickable());
}
// Fill in e-mails if such exist.
if (array_key_exists('emails', $data)) {
$email_list = $media_form->getField('Send to')->asMultifieldTable();
$email_list->setFieldMapping(['email'])->fill($data['emails']);
}
$media_form->submit();
$this->page->waitUntilReady();
}
/**
* Check media type configuration in user configuration form.
*
* @param array $data data provider
* @param string $original_period default media type active period or period set prior to editing the form
* @param boolean $edit_send_to flag that specifies whether the "Send to" parameter was edited
*/
private function checkMediaConfiguration($data, $original_period = '1-7,00:00-24:00', $edit_send_to = true) {
// Check media type.
$media_field = $this->query('name:user_form')->asForm()->waitUntilVisible()->one()->getField('Media')->asTable();
if (!$edit_send_to) {
$this->assertTrue($media_field->getRows()->count() === 1);
$row = $media_field->getRow(0);
}
else {
$row = $this->query('xpath://tr[@id="medias_0"]')->asTableRow()->one();
}
$this->assertEquals($row->getColumn('Type')->getText(), $data['fields']['Type']);
// Check the value of the "Send to" field.
if (array_key_exists('emails', $data)) {
$get_send_to = $row->query('xpath:./td[2]/div[@class="hint-box"]')->one()->getText();
$media_emails = [];
foreach ($data['emails'] as $email) {
$media_emails[] = $email['email'];
}
$send_to = implode(', ', $media_emails);
}
else {
$this->assertFalse($row->query('xpath:./td[2]/div[@class="hint-box"]')->one(false)->isValid());
$get_send_to = $row->getColumn('Send to')->getText();
$send_to = $data['fields']['Send to'];
}
$this->assertEquals($send_to, $get_send_to);
// Check media active period.
$when_active = $row->getColumn('When active')->getText();
$this->assertEquals($when_active, CTestArrayHelper::get($data, 'fields.When active', $original_period));
// Check media status.
$get_status = $row->getColumn('Status')->getText();
$status = CTestArrayHelper::get($data, 'fields.Enabled', true) ? 'Enabled' : 'Disabled';
$this->assertEquals($get_status, $status);
// Check selected severities.
$reference_severities = [
'Not classified' => '1',
'Information' => '2',
'Warning' => '3',
'Average' => '4',
'High' => '5',
'Disaster' => '6'
];
if (array_key_exists('Use if severity', $data['fields'])) {
// Check that the passed severities are turned on.
foreach ($data['fields']['Use if severity'] as $used_severity) {
$actual_severity = $row->query('xpath:./td[4]/div/div['.$reference_severities[$used_severity].']')->one()->getText();
$this->assertEquals($actual_severity, $used_severity.' (on)');
unset($reference_severities[$used_severity]);
}
// Check that other severities are turned off.
foreach ($reference_severities as $name => $unused_severity) {
$actual_severity = $row->query('xpath:./td[4]/div/div['.$unused_severity.']')->one()->getText();
$this->assertEquals($name.' (off)', $actual_severity);
}
}
else {
// Check that when no severities are passed - they all are turned on by default.
for ($i = 1; $i < 7; $i++) {
$severity = $row->query('xpath:./td[4]/div/div['.$i.']')->one()->getText();
$this->assertStringContainsString('(on)', $severity);
}
}
}
}