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.

2290 lines
74 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 __DIR__.'/../include/CAPITest.php';
/**
* @onBefore prepareTestData
*
* @onAfter clearData
*/
class testConnector extends CAPITest {
/**
* Non-existent ID.
*/
private const INVALID_ID = self::INVALID_NUMBER;
/**
* Invalid protocol, data_type, status etc.
*/
private const INVALID_NUMBER = 999999;
/**
* @var array
*/
private static array $data = [
'connectorids' => [],
// Created connectors during connector.create test (deleted at the end).
'created' => []
];
/**
* Prepare data for tests.
*/
public function prepareTestData(): void {
$connectors = [
'get_custom_defaults' => [
'name' => 'API test connector.get with custom defaults',
'url' => 'http://localhost/',
'description' => 'Custom description'
],
'get_data_type_events' => [
'name' => 'API test connector.get with data type (events)',
'data_type' => ZBX_CONNECTOR_DATA_TYPE_EVENTS,
'url' => 'http://localhost/'
],
'get_url' => [
'name' => 'API test connector.get with URL (user macro)',
'url' => '{$URL}'
],
'get_http_proxy' => [
'name' => 'API test connector.get with HTTP proxy (user macro)',
'url' => 'http://localhost/',
'http_proxy' => '{$HTTP_PROXY}'
],
'get_authtype_basic' => [
'name' => 'API test connector.get with authtype (basic), username and password',
'url' => 'http://localhost/',
'authtype' => ZBX_HTTP_AUTH_BASIC,
'username' => 'test',
'password' => '12345678'
],
'get_authtype_bearer' => [
'name' => 'API test connector.get with authtype (bearer)',
'url' => 'http://localhost/',
'authtype' => ZBX_HTTP_AUTH_BEARER,
'token' => '{$BEARER_TOKEN}'
],
'get_status_disabled' => [
'name' => 'API test connector.get with status (disabled)',
'url' => 'http://localhost/',
'status' => ZBX_CONNECTOR_STATUS_DISABLED
],
'get_tags' => [
'name' => 'API test connector.get with two tags',
'url' => 'http://localhost/',
'tags' => [
[
'tag' => 'abc',
'operator' => CONDITION_OPERATOR_EQUAL,
'value' => '123'
],
[
'tag' => 'xyz',
'operator' => CONDITION_OPERATOR_EXISTS
]
]
],
'update_custom_defaults' => [
'name' => 'API test connector.update with custom defaults',
'url' => 'http://localhost/',
'ssl_cert_file' => 'ssl_cert_file',
'ssl_key_file' => 'ssl_key_file',
'ssl_key_password' => 'ssl_key_password'
],
'update_authtype_basic' => [
'name' => 'API test connector.update with authtype (basic), username and password',
'url' => 'http://localhost/',
'authtype' => ZBX_HTTP_AUTH_BASIC,
'username' => 'test',
'password' => '12345678'
],
'update_tags' => [
'name' => 'API test connector.update with evaltype (or) and two tags',
'url' => 'http://localhost/',
'tags_evaltype' => CONDITION_EVAL_TYPE_AND_OR,
'tags' => [
[
'tag' => 'abc',
'operator' => CONDITION_OPERATOR_EQUAL,
'value' => '123'
],
[
'tag' => 'xyz',
'operator' => CONDITION_OPERATOR_EXISTS
]
]
],
'delete_single' => [
'name' => 'API test connector.delete - single',
'url' => 'http://localhost/'
],
'delete_multiple_1' => [
'name' => 'API test connector.delete - multiple 1',
'url' => 'http://localhost/'
],
'delete_multiple_2' => [
'name' => 'API test connector.delete - multiple 2',
'url' => 'http://localhost/'
]
];
$db_connectors = CDataHelper::call('connector.create', array_values($connectors));
$this->assertArrayHasKey('connectorids', $db_connectors,
__FUNCTION__.'() failed: Could not create connectors.'
);
self::$data['connectorids'] = array_combine(array_keys($connectors), $db_connectors['connectorids']);
}
/**
* Data provider for connector.create. Array contains invalid connectors.
*
* @return array
*/
public static function getConnectorCreateDataInvalid(): array {
return [
'Test connector.create: empty request' => [
'connector' => [],
'expected_error' => 'Invalid parameter "/": cannot be empty.'
],
'Test connector.create: unexpected parameter' => [
'connector' => [
'abc' => 'abc'
],
'expected_error' => 'Invalid parameter "/1": unexpected parameter "abc".'
],
// Check "name".
'Test connector.create: missing "name"' => [
'connector' => [
'description' => ''
],
'expected_error' => 'Invalid parameter "/1": the parameter "name" is missing.'
],
'Test connector.create: invalid "name" (empty string)' => [
'connector' => [
'name' => ''
],
'expected_error' => 'Invalid parameter "/1/name": cannot be empty.'
],
'Test connector.create: invalid "name" (too long)' => [
'connector' => [
'name' => str_repeat('a', DB::getFieldLength('connector', 'name') + 1)
],
'expected_error' => 'Invalid parameter "/1/name": value is too long.'
],
'Test connector.create: multiple connectors with the same "name"' => [
'connector' => [
[
'name' => 'API create connector',
'url' => 'http://localhost/'
],
[
'name' => 'API create connector',
'url' => 'http://localhost/'
]
],
'expected_error' => 'Invalid parameter "/2": value (name)=(API create connector) already exists.'
],
'Test connector.create: invalid "name" (duplicate)' => [
'connector' => [
'name' => 'API test connector.get with custom defaults',
'url' => 'http://localhost/'
],
'expected_error' => 'Connector "API test connector.get with custom defaults" already exists.'
],
// Check "protocol".
'Test connector.create: invalid "protocol" (string)' => [
'connector' => [
'name' => 'API create connector',
'protocol' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/protocol": an integer is expected.'
],
'Test connector.create: invalid "protocol" (not in range)' => [
'connector' => [
'name' => 'API create connector',
'protocol' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/protocol": value must be 0.'
],
// Check "data_type".
'Test connector.create: invalid "data_type" (string)' => [
'connector' => [
'name' => 'API create connector',
'data_type' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/data_type": an integer is expected.'
],
'Test connector.create: invalid "data_type" (not in range)' => [
'connector' => [
'name' => 'API create connector',
'data_type' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/data_type": value must be one of '.
implode(', ', [ZBX_CONNECTOR_DATA_TYPE_ITEM_VALUES, ZBX_CONNECTOR_DATA_TYPE_EVENTS]).'.'
],
// Check "url".
'Test connector.create: missing "url"' => [
'connector' => [
'name' => 'API create connector'
],
'expected_error' => 'Invalid parameter "/1": the parameter "url" is missing.'
],
'Test connector.create: invalid "url" (boolean)' => [
'connector' => [
'name' => 'API create connector',
'url' => false
],
'expected_error' => 'Invalid parameter "/1/url": a character string is expected.'
],
'Test connector.create: invalid "url" (empty string)' => [
'connector' => [
'name' => 'API create connector',
'url' => ''
],
'expected_error' => 'Invalid parameter "/1/url": cannot be empty.'
],
'Test connector.create: invalid "url"' => [
'connector' => [
'name' => 'API create connector',
'url' => 'javascript:alert(123);'
],
'expected_error' => 'Invalid parameter "/1/url": unacceptable URL.'
],
'Test connector.create: invalid "url" (too long)' => [
'connector' => [
'name' => 'API create connector',
'url' => str_repeat('a', DB::getFieldLength('connector', 'url') + 1)
],
'expected_error' => 'Invalid parameter "/1/url": value is too long.'
],
// Check "max_records".
'Test connector.create: invalid "max_records" (string)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'max_records' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/max_records": an integer is expected.'
],
'Test connector.create: invalid "max_records" (not in range)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'max_records' => -1
],
'expected_error' => 'Invalid parameter "/1/max_records": value must be one of 0-'.ZBX_MAX_INT32.'.'
],
// Check "max_senders".
'Test connector.create: invalid "max_senders" (string)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'max_senders' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/max_senders": an integer is expected.'
],
'Test connector.create: invalid "max_senders" (not in range)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'max_senders' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/max_senders": value must be one of 1-100.'
],
// Check "max_attempts".
'Test connector.create: invalid "max_attempts" (string)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'max_attempts' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/max_attempts": an integer is expected.'
],
'Test connector.create: invalid "max_attempts" (not in range)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'max_attempts' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/max_attempts": value must be one of 1-5.'
],
// Check "timeout".
'Test connector.create: invalid "timeout" (boolean)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'timeout' => false
],
'expected_error' => 'Invalid parameter "/1/timeout": a character string is expected.'
],
'Test connector.create: invalid "timeout" (empty)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'timeout' => ''
],
'expected_error' => 'Invalid parameter "/1/timeout": cannot be empty.'
],
'Test connector.create: invalid "timeout" (string)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'timeout' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/timeout": a time unit is expected.'
],
'Test connector.create: invalid "timeout" (not in range)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'timeout' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/timeout": value must be one of 1-'.SEC_PER_MIN.'.'
],
// Check "http_proxy".
'Test connector.create: invalid "http_proxy" (boolean)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'http_proxy' => false
],
'expected_error' => 'Invalid parameter "/1/http_proxy": a character string is expected.'
],
'Test connector.create: invalid "http_proxy" (too long)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'http_proxy' => str_repeat('a', DB::getFieldLength('connector', 'http_proxy') + 1)
],
'expected_error' => 'Invalid parameter "/1/http_proxy": value is too long.'
],
// Check "authtype".
'Test connector.create: invalid "authtype" (string)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'authtype' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/authtype": an integer is expected.'
],
'Test connector.create: invalid "authtype" (not in range)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'authtype' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/authtype": value must be one of '.
implode(', ', [ZBX_HTTP_AUTH_NONE, ZBX_HTTP_AUTH_BASIC, ZBX_HTTP_AUTH_NTLM, ZBX_HTTP_AUTH_KERBEROS,
ZBX_HTTP_AUTH_DIGEST, ZBX_HTTP_AUTH_BEARER
]).'.'
],
// Check "username".
'Test connector.create: invalid "username" (must be empty)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'username' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/username": value must be empty.'
],
'Test connector.create: invalid "username" (boolean)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'authtype' => ZBX_HTTP_AUTH_BASIC,
'username' => false
],
'expected_error' => 'Invalid parameter "/1/username": a character string is expected.'
],
'Test connector.create: invalid "username" (too long)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'authtype' => ZBX_HTTP_AUTH_NTLM,
'username' => str_repeat('a', DB::getFieldLength('connector', 'username') + 1)
],
'expected_error' => 'Invalid parameter "/1/username": value is too long.'
],
// Check "password".
'Test connector.create: invalid "password" (must be empty)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'password' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/password": value must be empty.'
],
'Test connector.create: invalid "password" (boolean)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'authtype' => ZBX_HTTP_AUTH_BASIC,
'password' => false
],
'expected_error' => 'Invalid parameter "/1/password": a character string is expected.'
],
'Test connector.create: invalid "password" (too long)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'authtype' => ZBX_HTTP_AUTH_NTLM,
'password' => str_repeat('a', DB::getFieldLength('connector', 'password') + 1)
],
'expected_error' => 'Invalid parameter "/1/password": value is too long.'
],
// Check "token".
'Test connector.create: invalid "token" (boolean)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'token' => false
],
'expected_error' => 'Invalid parameter "/1/token": a character string is expected.'
],
'Test connector.create: invalid "token" (incompatible authtype)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'token' => '{$BEARER_TOKEN}'
],
'expected_error' => 'Invalid parameter "/1/token": value must be empty.'
],
'Test connector.create: invalid "token" (too long)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'authtype' => ZBX_HTTP_AUTH_BEARER,
'token' => str_repeat('a', DB::getFieldLength('connector', 'token') + 1)
],
'expected_error' => 'Invalid parameter "/1/token": value is too long.'
],
// Check "verify_peer".
'Test connector.create: invalid "verify_peer" (string)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'verify_peer' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/verify_peer": an integer is expected.'
],
'Test connector.create: invalid "verify_peer" (not in range)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'verify_peer' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/verify_peer": value must be one of '.
implode(', ', [ZBX_HTTP_VERIFY_PEER_OFF, ZBX_HTTP_VERIFY_PEER_ON]).'.'
],
// Check "verify_host".
'Test connector.create: invalid "verify_host" (string)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'verify_host' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/verify_host": an integer is expected.'
],
'Test connector.create: invalid "verify_host" (not in range)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'verify_host' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/verify_host": value must be one of '.
implode(', ', [ZBX_HTTP_VERIFY_HOST_OFF, ZBX_HTTP_VERIFY_HOST_ON]).'.'
],
// Check "ssl_cert_file".
'Test connector.create: invalid "ssl_cert_file" (boolean)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'ssl_cert_file' => false
],
'expected_error' => 'Invalid parameter "/1/ssl_cert_file": a character string is expected.'
],
'Test connector.create: invalid "ssl_cert_file" (too long)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'ssl_cert_file' => str_repeat('a', DB::getFieldLength('connector', 'ssl_cert_file') + 1)
],
'expected_error' => 'Invalid parameter "/1/ssl_cert_file": value is too long.'
],
// Check "ssl_key_file".
'Test connector.create: invalid "ssl_key_file" (boolean)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'ssl_cert_file' => 'ssl_cert_file',
'ssl_key_file' => false
],
'expected_error' => 'Invalid parameter "/1/ssl_key_file": a character string is expected.'
],
'Test connector.create: invalid "ssl_key_file" (too long)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'ssl_cert_file' => 'ssl_cert_file',
'ssl_key_file' => str_repeat('a', DB::getFieldLength('connector', 'ssl_key_file') + 1)
],
'expected_error' => 'Invalid parameter "/1/ssl_key_file": value is too long.'
],
// Check "ssl_key_password".
'Test connector.create: invalid "ssl_key_password" (boolean)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'ssl_cert_file' => 'ssl_cert_file',
'ssl_key_file' => 'ssl_key_file',
'ssl_key_password' => false
],
'expected_error' => 'Invalid parameter "/1/ssl_key_password": a character string is expected.'
],
'Test connector.create: invalid "ssl_key_password" (too long)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'ssl_cert_file' => 'ssl_cert_file',
'ssl_key_file' => 'ssl_key_file',
'ssl_key_password' => str_repeat('a', DB::getFieldLength('connector', 'ssl_key_password') + 1)
],
'expected_error' => 'Invalid parameter "/1/ssl_key_password": value is too long.'
],
// Check "description".
'Test connector.create: invalid "description" (boolean)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'description' => false
],
'expected_error' => 'Invalid parameter "/1/description": a character string is expected.'
],
'Test connector.create: invalid "description" (too long)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'description' => str_repeat('a', DB::getFieldLength('connector', 'description') + 1)
],
'expected_error' => 'Invalid parameter "/1/description": value is too long.'
],
// Check "status".
'Test connector.create: invalid "status" (string)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'status' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/status": an integer is expected.'
],
'Test connector.create: invalid "status" (not in range)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'status' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/status": value must be one of '.
implode(', ', [ZBX_CONNECTOR_STATUS_DISABLED, ZBX_CONNECTOR_STATUS_ENABLED]).'.'
],
// Check "tags_evaltype".
'Test connector.create: invalid "tags_evaltype" (string)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags_evaltype' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/tags_evaltype": an integer is expected.'
],
'Test connector.create: invalid "tags_evaltype" (not in range)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags_evaltype' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/tags_evaltype": value must be one of '.
implode(', ', [CONDITION_EVAL_TYPE_AND_OR, CONDITION_EVAL_TYPE_OR]).'.'
],
// Check "tags".
'Test connector.create: invalid "tags" (string)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/tags": an array is expected.'
],
'Test connector.create: invalid "tags" (array with string)' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags' => ['abc']
],
'expected_error' => 'Invalid parameter "/1/tags/1": an array is expected.'
],
'Test connector.create: missing "tag" for "tags"' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags' => [
[]
]
],
'expected_error' => 'Invalid parameter "/1/tags/1": the parameter "tag" is missing.'
],
'Test connector.create: unexpected parameter for "tags"' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags' => [
['abc' => '']
]
],
'expected_error' => 'Invalid parameter "/1/tags/1": unexpected parameter "abc".'
],
'Test connector.create: invalid "tag" (boolean) for "tags"' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags' => [
['tag' => false]
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/tag": a character string is expected.'
],
'Test connector.create: invalid "tag" (empty string) for "tags"' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags' => [
['tag' => '']
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/tag": cannot be empty.'
],
'Test connector.create: invalid "tag" (too long) for "tags"' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags' => [
['tag' => str_repeat('a', DB::getFieldLength('connector_tag', 'tag') + 1)]
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/tag": value is too long.'
],
'Test connector.create: invalid "operator" (boolean) for "tags"' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags' => [
['tag' => 'abc', 'operator' => false]
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/operator": an integer is expected.'
],
'Test connector.create: invalid "operator" (not in range) for "tags"' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags' => [
['tag' => 'abc', 'operator' => self::INVALID_NUMBER]
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/operator": value must be one of '.
implode(', ', [CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL, CONDITION_OPERATOR_LIKE,
CONDITION_OPERATOR_NOT_LIKE, CONDITION_OPERATOR_EXISTS, CONDITION_OPERATOR_NOT_EXISTS
]).'.'
],
'Test connector.create: invalid "value" (boolean) for "tags"' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags' => [
['tag' => 'abc', 'operator' => CONDITION_OPERATOR_EQUAL, 'value' => false]
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/value": a character string is expected.'
],
'Test connector.create: invalid "value" (not empty) for "tags"' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags' => [
['tag' => 'abc', 'operator' => CONDITION_OPERATOR_EXISTS, 'value' => '123']
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/value": value must be empty.'
],
'Test connector.create: invalid "value" (not empty) for "tags" 2' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags' => [
['tag' => 'abc', 'operator' => CONDITION_OPERATOR_NOT_EXISTS, 'value' => '123']
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/value": value must be empty.'
],
'Test connector.create: invalid "value" (too long) for "tags"' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags' => [
[
'tag' => 'abc',
'operator' => CONDITION_OPERATOR_EQUAL,
'value' => str_repeat('a', DB::getFieldLength('connector_tag', 'value') + 1)
]
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/value": value is too long.'
],
'Test connector.create: invalid "tag" (duplicate) for "tags"' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'tags' => [
['tag' => 'abc', 'operator' => CONDITION_OPERATOR_EQUAL, 'value' => '123'],
['tag' => 'abc', 'operator' => CONDITION_OPERATOR_EQUAL, 'value' => '123']
]
],
'expected_error' => 'Invalid parameter "/1/tags/2": value (tag, operator, value)=(abc, 0, 123) already exists.'
],
'Test connector.create: overly long username' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'authtype' => ZBX_HTTP_AUTH_BASIC,
'username' => str_repeat('z', 256)
],
'expected_error' => 'Invalid parameter "/1/username": value is too long.'
],
'Test connector.create: overly long password' => [
'connector' => [
'name' => 'API create connector',
'url' => 'http://localhost/',
'authtype' => ZBX_HTTP_AUTH_BASIC,
'password' => str_repeat('z', 256)
],
'expected_error' => 'Invalid parameter "/1/password": value is too long.'
]
];
}
/**
* Data provider for connector.create. Array contains valid connectors.
*
* @return array
*/
public static function getConnectorCreateDataValid(): array {
return [
'Test connector.create: single connector' => [
'connector' => [
'name' => 'API create single connector',
'url' => 'http://localhost/'
],
'expected_error' => null
],
'Test connector.create: multiple connectors' => [
'connector' => [
[
'name' => 'API create first connector',
'url' => 'http://localhost/'
],
[
'name' => 'API create second connector',
'url' => 'http://localhost/'
]
],
'expected_error' => null
],
'Test connector.create: longest username' => [
'connector' => [
'name' => 'API longest username connector',
'url' => 'http://localhost/',
'authtype' => ZBX_HTTP_AUTH_BASIC,
'username' => str_repeat('z', 255)
],
'expected_error' => null
],
'Test connector.create: longest password' => [
'connector' => [
'name' => 'API longest password connector',
'url' => 'http://localhost/',
'authtype' => ZBX_HTTP_AUTH_BASIC,
'password' => str_repeat('z', 255)
],
'expected_error' => null
]
];
}
/**
* Test connector.create with errors like missing fields, optional invalid fields and valid fields.
*
* @dataProvider getConnectorCreateDataInvalid
* @dataProvider getConnectorCreateDataValid
*/
public function testConnector_Create(array $connectors, ?string $expected_error): void {
// Accept single and multiple connectors just like API method. Work with multidimensional array in result.
if (!array_key_exists(0, $connectors)) {
$connectors = zbx_toArray($connectors);
}
$sql_connectors = 'SELECT NULL FROM connector';
$old_hash_connectors = CDBHelper::getHash($sql_connectors);
$result = $this->call('connector.create', $connectors, $expected_error);
if ($expected_error === null) {
// Something was changed in DB.
$this->assertNotSame($old_hash_connectors, CDBHelper::getHash($sql_connectors));
$this->assertEquals(count($connectors), count($result['result']['connectorids']));
// Add connector IDs to create array, so they can be deleted after tests are complete.
self::$data['created'] = array_merge(self::$data['created'], $result['result']['connectorids']);
$db_connectors = $this->getConnectors($result['result']['connectorids']);
$db_defaults = DB::getDefaults('connector');
// Check individual fields.
foreach ($result['result']['connectorids'] as $num => $connectorid) {
$connector = $connectors[$num];
$db_connector = $db_connectors[$connectorid];
// Required fields.
$this->assertNotEmpty($db_connector['name']);
$this->assertSame($connector['name'], $db_connector['name']);
$this->assertNotEmpty($db_connector['url']);
$this->assertSame($connector['url'], $db_connector['url']);
// Numeric fields.
foreach (['protocol', 'data_type', 'max_records', 'max_senders', 'max_attempts', 'authtype',
'verify_peer', 'verify_host', 'status', 'tags_evaltype'] as $field) {
if (array_key_exists($field, $connector)) {
$this->assertEquals($connector[$field], $db_connector[$field]);
}
else {
$this->assertEquals($db_defaults[$field], $db_connector[$field]);
}
}
// Text fields.
if (array_key_exists('timeout', $connector)) {
$this->assertSame($connector['timeout'], $db_connector['timeout']);
}
else {
$this->assertSame($db_defaults['timeout'], $db_connector['timeout']);
}
foreach (['http_proxy', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'description'] as $field) {
if (array_key_exists($field, $connector)) {
$this->assertSame($connector[$field], $db_connector[$field]);
}
else {
$this->assertEmpty($db_connector[$field]);
}
}
if (in_array($db_connector['authtype'], [ZBX_HTTP_AUTH_BASIC, ZBX_HTTP_AUTH_NTLM,
ZBX_HTTP_AUTH_KERBEROS, ZBX_HTTP_AUTH_DIGEST])) {
foreach (['username', 'password'] as $field) {
if (array_key_exists($field, $connector)) {
$this->assertSame($connector[$field], $db_connector[$field]);
}
else {
$this->assertEmpty($db_connector[$field]);
}
}
}
else {
$this->assertEmpty($db_connector['username']);
$this->assertEmpty($db_connector['password']);
}
if ($db_connector['authtype'] == ZBX_HTTP_AUTH_BEARER) {
$this->assertNotEmpty($db_connector['token']);
$this->assertSame($connector['token'], $db_connector['token']);
}
else {
$this->assertEmpty($db_connector['token']);
}
// Tags.
if (array_key_exists('tags', $connector) && $connector['tags']) {
$this->assertEqualsCanonicalizing($connector['tags'], $db_connector['tags']);
}
else {
$this->assertEmpty($db_connector['tags']);
}
}
}
else {
$this->assertSame($old_hash_connectors, CDBHelper::getHash($sql_connectors));
}
}
/**
* Data provider for connector.get. Array contains invalid connector parameters.
*
* @return array
*/
public static function getConnectorGetDataInvalid(): array {
return [
// Check unexpected params.
'Test connector.get: unexpected parameter' => [
'request' => [
'abc' => 'abc'
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/": unexpected parameter "abc".'
],
// Check "connectorids" field.
'Test connector.get: invalid "connectorids" (empty string)' => [
'request' => [
'connectorids' => ''
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/connectorids": an array is expected.'
],
'Test connector.get: invalid "connectorids" (array with empty string)' => [
'request' => [
'connectorids' => ['']
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/connectorids/1": a number is expected.'
],
// Check filter.
'Test connector.get: invalid "filter" (empty string)' => [
'request' => [
'filter' => ''
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/filter": an array is expected.'
],
// Check unexpected parameters that exist in object, but not in filter.
'Test connector.get: unexpected parameter in "filter"' => [
'request' => [
'filter' => [
'max_records' => 'max_records'
]
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/filter": unexpected parameter "max_records".'
],
// Check "search" option.
'Test connector.get: invalid "search" (string)' => [
'request' => [
'search' => 'abc'
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/search": an array is expected.'
],
// Check unexpected parameters that exist in object, but not in search.
'Test connector.get: unexpected parameter in "search"' => [
'request' => [
'search' => [
'connectorid' => 'connectorid'
]
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/search": unexpected parameter "connectorid".'
],
// Check "output" option.
'Test connector.get: invalid parameter "output" (string)' => [
'request' => [
'output' => 'abc'
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/output": value must be "'.API_OUTPUT_EXTEND.'".'
],
'Test connector.get: invalid parameter "output" (array with string)' => [
'request' => [
'output' => ['abc']
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/output/1": value must be one of "connectorid", "name", "protocol", "data_type", "url", "max_records", "max_senders", "max_attempts", "timeout", "http_proxy", "authtype", "username", "password", "token", "verify_peer", "verify_host", "ssl_cert_file", "ssl_key_file", "ssl_key_password", "description", "status", "tags_evaltype".'
],
// Check "selectTags" option.
'Test connector.get: invalid parameter "selectTags" (string)' => [
'request' => [
'selectTags' => 'abc'
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/selectTags": value must be one of "'.API_OUTPUT_EXTEND.'", "'.API_OUTPUT_COUNT.'".'
],
'Test connector.get: invalid parameter "selectTags" (array with string)' => [
'request' => [
'selectTags' => ['abc']
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/selectTags/1": value must be one of "tag", "operator", "value".'
],
// Check common fields that are not flags, but require strict validation.
'Test connector.get: invalid parameter "searchByAny" (string)' => [
'request' => [
'searchByAny' => 'abc'
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/searchByAny": a boolean is expected.'
],
'Test connector.get: invalid parameter "searchWildcardsEnabled" (string)' => [
'request' => [
'searchWildcardsEnabled' => 'abc'
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/searchWildcardsEnabled": a boolean is expected.'
],
'Test connector.get: invalid parameter "sortfield" (bool)' => [
'request' => [
'sortfield' => false
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/sortfield": an array is expected.'
],
'Test connector.get: invalid parameter "sortfield"' => [
'request' => [
'sortfield' => 'abc'
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/sortfield/1": value must be one of "connectorid", "name", "data_type", "status".'
],
'Test connector.get: invalid parameter "sortorder" (bool)' => [
'request' => [
'sortorder' => false
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/sortorder": an array or a character string is expected.'
],
'Test connector.get: invalid parameter "sortorder" (not in range)' => [
'request' => [
'sortorder' => 'abc'
],
'expected_result' => [],
'expected_error' =>
'Invalid parameter "/sortorder": value must be one of "'.ZBX_SORT_UP.'", "'.ZBX_SORT_DOWN.'".'
],
'Test connector.get: invalid parameter "limit" (bool)' => [
'request' => [
'limit' => false
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/limit": an integer is expected.'
],
'Test connector.get: invalid parameter "preservekeys" (string)' => [
'request' => [
'preservekeys' => 'abc'
],
'expected_result' => [],
'expected_error' => 'Invalid parameter "/preservekeys": a boolean is expected.'
]
];
}
/**
* Data provider for connector.get. Array contains valid connector parameters.
*,
* @return array
*/
public static function getConnectorGetDataValid(): array {
return [
// Check validity of "connectorids" without getting any results.
'Test connector.get: empty "connectorids"' => [
'request' => [
'connectorids' => []
],
'expected_result' => [],
'expected_error' => null
],
// Check no fields are returned on empty selection.
'Test connector.get: empty "output"' => [
'request' => [
'output' => [],
'connectorids' => ['get_custom_defaults', 'get_status_disabled']
],
'expected_result' => [
[],
[]
],
'expected_error' => null
],
// Check only specific fields are returned.
'Test connector.get: specific field "output"' => [
'request' => [
'output' => ['connectorid', 'name', 'url'],
'connectorids' => ['get_custom_defaults']
],
'expected_result' => [
[
'connectorid' => 'get_custom_defaults',
'name' => 'API test connector.get with custom defaults',
'url' => 'http://localhost/'
]
],
'expected_error' => null
],
// Filter by data type.
'Test connector.get: filter by "data_type"' => [
'request' => [
'output' => ['name', 'data_type'],
'connectorids' => ['get_custom_defaults', 'get_data_type_events'],
'filter' => [
'data_type' => ZBX_CONNECTOR_DATA_TYPE_EVENTS
]
],
'expected_result' => [
[
'name' => 'API test connector.get with data type (events)',
'data_type' => (string) ZBX_CONNECTOR_DATA_TYPE_EVENTS
]
],
'expected_error' => null
],
// Filter by authentication type.
'Test connector.get: filter by "authtype"' => [
'request' => [
'output' => ['name', 'authtype', 'username', 'password'],
'connectorids' => ['get_custom_defaults', 'get_authtype_basic'],
'filter' => [
'authtype' => ZBX_HTTP_AUTH_BASIC
]
],
'expected_result' => [
[
'name' => 'API test connector.get with authtype (basic), username and password',
'authtype' => (string) ZBX_HTTP_AUTH_BASIC,
'username' => 'test',
'password' => '12345678'
]
],
'expected_error' => null
],
// Filter by status.
'Test connector.get: filter by "status"' => [
'request' => [
'output' => ['name', 'status'],
'connectorids' => ['get_custom_defaults', 'get_status_disabled'],
'filter' => [
'status' => ZBX_CONNECTOR_STATUS_DISABLED
]
],
'expected_result' => [
[
'name' => 'API test connector.get with status (disabled)',
'status' => (string) ZBX_CONNECTOR_STATUS_DISABLED
]
],
'expected_error' => null
],
// Search by name.
'Test connector.get: search by "name"' => [
'request' => [
'output' => ['name'],
'search' => [
'name' => 'API test connector.get with custom defaults'
]
],
'expected_result' => [
[
'name' => 'API test connector.get with custom defaults'
]
],
'expected_error' => null
],
// Search by URL.
'Test connector.get: search by "url"' => [
'request' => [
'output' => ['name', 'url'],
'search' => [
'url' => '{$URL}'
]
],
'expected_result' => [
[
'name' => 'API test connector.get with URL (user macro)',
'url' => '{$URL}'
]
],
'expected_error' => null
],
// Search by HTTP proxy.
'Test connector.get: search by "http_proxy"' => [
'request' => [
'output' => ['name', 'http_proxy'],
'search' => [
'http_proxy' => '{$HTTP_PROXY}'
]
],
'expected_result' => [
[
'name' => 'API test connector.get with HTTP proxy (user macro)',
'http_proxy' => '{$HTTP_PROXY}'
]
],
'expected_error' => null
],
// Search by username.
'Test connector.get: search by "username"' => [
'request' => [
'output' => ['name', 'authtype', 'username', 'password'],
'connectorids' => ['get_custom_defaults', 'get_authtype_basic'],
'search' => [
'username' => 'test'
]
],
'expected_result' => [
[
'name' => 'API test connector.get with authtype (basic), username and password',
'authtype' => (string) ZBX_HTTP_AUTH_BASIC,
'username' => 'test',
'password' => '12345678'
]
],
'expected_error' => null
],
// Search by Bearer token.
'Test connector.get: search by "token"' => [
'request' => [
'output' => ['name', 'authtype', 'token'],
'search' => [
'token' => '{$BEARER_TOKEN}'
]
],
'expected_result' => [
[
'name' => 'API test connector.get with authtype (bearer)',
'authtype' => (string) ZBX_HTTP_AUTH_BEARER,
'token' => '{$BEARER_TOKEN}'
]
],
'expected_error' => null
],
// Search by description.
'Test connector.get: search by "description"' => [
'request' => [
'output' => ['name', 'description'],
'search' => [
'description' => 'Custom description'
]
],
'expected_result' => [
[
'name' => 'API test connector.get with custom defaults',
'description' => 'Custom description'
]
],
'expected_error' => null
],
// Check tags are returned.
'Test connector.get: tags' => [
'request' => [
'output' => ['connectorid', 'name'],
'selectTags' => ['tag', 'operator', 'value'],
'connectorids' => ['get_custom_defaults', 'get_tags']
],
'expected_result' => [
[
'connectorid' => 'get_custom_defaults',
'name' => 'API test connector.get with custom defaults',
'tags' => []
],
[
'connectorid' => 'get_tags',
'name' => 'API test connector.get with two tags',
'tags' => [
[
'tag' => 'abc',
'operator' => (string) CONDITION_OPERATOR_EQUAL,
'value' => '123'
],
[
'tag' => 'xyz',
'operator' => (string) CONDITION_OPERATOR_EXISTS,
'value' => ''
]
]
]
],
'expected_error' => null
],
// Check tag count is returned.
'Test connector.get: tag count' => [
'request' => [
'output' => ['connectorid', 'name'],
'selectTags' => API_OUTPUT_COUNT,
'connectorids' => ['get_custom_defaults', 'get_tags']
],
'expected_result' => [
[
'connectorid' => 'get_custom_defaults',
'name' => 'API test connector.get with custom defaults',
'tags' => '0'
],
[
'connectorid' => 'get_tags',
'name' => 'API test connector.get with two tags',
'tags' => '2'
]
],
'expected_error' => null
]
];
}
/**
* Test connector.get with all options.
*
* @dataProvider getConnectorGetDataInvalid
* @dataProvider getConnectorGetDataValid
*/
public function testConnector_Get(array $request, array $expected_result, ?string $expected_error): void {
// Replace ID placeholders with real IDs.
$request = self::resolveIds($request);
foreach ($expected_result as &$connector) {
$connector = self::resolveIds($connector);
}
unset($connector);
$result = $this->call('connector.get', $request, $expected_error);
if ($expected_error === null) {
$this->assertSame($expected_result, $result['result']);
}
}
/**
* Data provider for connector.update. Array contains invalid connector parameters.
*
* @return array
*/
public static function getConnectorUpdateDataInvalid(): array {
return [
'Test connector.update: empty request' => [
'connector' => [],
'expected_error' => 'Invalid parameter "/": cannot be empty.'
],
// Check "connectorid".
'Test connector.update: missing "connectorid"' => [
'connector' => [
'name' => 'API update connector'
],
'expected_error' => 'Invalid parameter "/1": the parameter "connectorid" is missing.'
],
'Test connector.update: invalid "connectorid" (empty string)' => [
'connector' => [
'connectorid' => ''
],
'expected_error' => 'Invalid parameter "/1/connectorid": a number is expected.'
],
'Test connector.update: invalid "connectorid" (non-existent)' => [
'connector' => [
'connectorid' => self::INVALID_ID
],
'expected_error' => 'No permissions to referred object or it does not exist!'
],
'Test connector.update: multiple connectors with the same "connectorid"' => [
'connector' => [
['connectorid' => 0],
['connectorid' => 0]
],
'expected_error' => 'Invalid parameter "/2": value (connectorid)=(0) already exists.'
],
// Check "name".
'Test connector.update: invalid "name" (empty string)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'name' => ''
],
'expected_error' => 'Invalid parameter "/1/name": cannot be empty.'
],
'Test connector.update: invalid "name" (too long)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'name' => str_repeat('a', DB::getFieldLength('connector', 'name') + 1)
],
'expected_error' => 'Invalid parameter "/1/name": value is too long.'
],
// Check "protocol".
'Test connector.update: invalid "protocol" (string)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'protocol' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/protocol": an integer is expected.'
],
'Test connector.update: invalid "protocol" (not in range)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'protocol' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/protocol": value must be 0.'
],
// Check "data_type".
'Test connector.update: invalid "data_type" (string)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'data_type' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/data_type": an integer is expected.'
],
'Test connector.update: invalid "data_type" (not in range)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'data_type' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/data_type": value must be one of '.
implode(', ', [ZBX_CONNECTOR_DATA_TYPE_ITEM_VALUES, ZBX_CONNECTOR_DATA_TYPE_EVENTS]).'.'
],
// Check "url".
'Test connector.update: invalid "url" (boolean)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'url' => false
],
'expected_error' => 'Invalid parameter "/1/url": a character string is expected.'
],
'Test connector.update: invalid "url" (empty string)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'url' => ''
],
'expected_error' => 'Invalid parameter "/1/url": cannot be empty.'
],
'Test connector.update: invalid "url"' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'url' => 'javascript:alert(123);'
],
'expected_error' => 'Invalid parameter "/1/url": unacceptable URL.'
],
'Test connector.update: invalid "url" (too long)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'url' => str_repeat('a', DB::getFieldLength('connector', 'url') + 1)
],
'expected_error' => 'Invalid parameter "/1/url": value is too long.'
],
// Check "max_records".
'Test connector.update: invalid "max_records" (string)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'max_records' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/max_records": an integer is expected.'
],
'Test connector.update: invalid "max_records" (not in range)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'max_records' => -1
],
'expected_error' => 'Invalid parameter "/1/max_records": value must be one of 0-'.ZBX_MAX_INT32.'.'
],
// Check "max_senders".
'Test connector.update: invalid "max_senders" (string)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'max_senders' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/max_senders": an integer is expected.'
],
'Test connector.update: invalid "max_senders" (not in range)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'max_senders' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/max_senders": value must be one of 1-100.'
],
// Check "max_attempts".
'Test connector.update: invalid "max_attempts" (string)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'max_attempts' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/max_attempts": an integer is expected.'
],
'Test connector.update: invalid "max_attempts" (not in range)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'max_attempts' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/max_attempts": value must be one of 1-5.'
],
// Check "timeout".
'Test connector.update: invalid "timeout" (boolean)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'timeout' => false
],
'expected_error' => 'Invalid parameter "/1/timeout": a character string is expected.'
],
'Test connector.update: invalid "timeout" (empty)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'timeout' => ''
],
'expected_error' => 'Invalid parameter "/1/timeout": cannot be empty.'
],
'Test connector.update: invalid "timeout" (string)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'timeout' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/timeout": a time unit is expected.'
],
'Test connector.update: invalid "timeout" (not in range)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'timeout' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/timeout": value must be one of 1-'.SEC_PER_MIN.'.'
],
// Check "http_proxy".
'Test connector.update: invalid "http_proxy" (boolean)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'http_proxy' => false
],
'expected_error' => 'Invalid parameter "/1/http_proxy": a character string is expected.'
],
'Test connector.update: invalid "http_proxy" (too long)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'http_proxy' => str_repeat('a', DB::getFieldLength('connector', 'http_proxy') + 1)
],
'expected_error' => 'Invalid parameter "/1/http_proxy": value is too long.'
],
// Check "authtype".
'Test connector.update: invalid "authtype" (string)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'authtype' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/authtype": an integer is expected.'
],
'Test connector.update: invalid "authtype" (not in range)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'authtype' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/authtype": value must be one of '.
implode(', ', [ZBX_HTTP_AUTH_NONE, ZBX_HTTP_AUTH_BASIC, ZBX_HTTP_AUTH_NTLM, ZBX_HTTP_AUTH_KERBEROS,
ZBX_HTTP_AUTH_DIGEST, ZBX_HTTP_AUTH_BEARER
]).'.'
],
// Check "username".
'Test connector.update: invalid "username" (must be empty)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'username' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/username": value must be empty.'
],
'Test connector.update: invalid "username" (boolean)' => [
'connector' => [
'connectorid' => 'update_authtype_basic',
'username' => false
],
'expected_error' => 'Invalid parameter "/1/username": a character string is expected.'
],
'Test connector.update: invalid "username" (too long)' => [
'connector' => [
'connectorid' => 'update_authtype_basic',
'username' => str_repeat('a', DB::getFieldLength('connector', 'username') + 1)
],
'expected_error' => 'Invalid parameter "/1/username": value is too long.'
],
// Check "password".
'Test connector.update: invalid "password" (must be empty)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'password' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/password": value must be empty.'
],
'Test connector.update: invalid "password" (boolean)' => [
'connector' => [
'connectorid' => 'update_authtype_basic',
'password' => false
],
'expected_error' => 'Invalid parameter "/1/password": a character string is expected.'
],
'Test connector.update: invalid "password" (too long)' => [
'connector' => [
'connectorid' => 'update_authtype_basic',
'password' => str_repeat('a', DB::getFieldLength('connector', 'password') + 1)
],
'expected_error' => 'Invalid parameter "/1/password": value is too long.'
],
// Check "token".
'Test connector.update: invalid "token" (boolean)' => [
'connector' => [
'connectorid' => 'update_authtype_basic',
'token' => false
],
'expected_error' => 'Invalid parameter "/1/token": a character string is expected.'
],
'Test connector.update: invalid "token" (incompatible authtype)' => [
'connector' => [
'connectorid' => 'update_authtype_basic',
'token' => '{$BEARER_TOKEN}'
],
'expected_error' => 'Invalid parameter "/1/token": value must be empty.'
],
'Test connector.update: invalid "token" (too long)' => [
'connector' => [
'connectorid' => 'update_authtype_basic',
'authtype' => ZBX_HTTP_AUTH_BEARER,
'token' => str_repeat('a', DB::getFieldLength('connector', 'token') + 1)
],
'expected_error' => 'Invalid parameter "/1/token": value is too long.'
],
// Check "verify_peer".
'Test connector.update: invalid "verify_peer" (string)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'verify_peer' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/verify_peer": an integer is expected.'
],
'Test connector.update: invalid "verify_peer" (not in range)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'verify_peer' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/verify_peer": value must be one of '.
implode(', ', [ZBX_HTTP_VERIFY_PEER_OFF, ZBX_HTTP_VERIFY_PEER_ON]).'.'
],
// Check "verify_host".
'Test connector.update: invalid "verify_host" (string)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'verify_host' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/verify_host": an integer is expected.'
],
'Test connector.update: invalid "verify_host" (not in range)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'verify_host' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/verify_host": value must be one of '.
implode(', ', [ZBX_HTTP_VERIFY_HOST_OFF, ZBX_HTTP_VERIFY_HOST_ON]).'.'
],
// Check "ssl_cert_file".
'Test connector.update: invalid "ssl_cert_file" (boolean)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'ssl_cert_file' => false
],
'expected_error' => 'Invalid parameter "/1/ssl_cert_file": a character string is expected.'
],
'Test connector.update: invalid "ssl_cert_file" (too long)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'ssl_cert_file' => str_repeat('a', DB::getFieldLength('connector', 'ssl_cert_file') + 1)
],
'expected_error' => 'Invalid parameter "/1/ssl_cert_file": value is too long.'
],
// Check "ssl_key_file".
'Test connector.update: invalid "ssl_key_file" (boolean)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'ssl_key_file' => false
],
'expected_error' => 'Invalid parameter "/1/ssl_key_file": a character string is expected.'
],
'Test connector.update: invalid "ssl_key_file" (too long)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'ssl_key_file' => str_repeat('a', DB::getFieldLength('connector', 'ssl_key_file') + 1)
],
'expected_error' => 'Invalid parameter "/1/ssl_key_file": value is too long.'
],
// Check "ssl_key_password".
'Test connector.update: invalid "ssl_key_password" (boolean)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'ssl_key_password' => false
],
'expected_error' => 'Invalid parameter "/1/ssl_key_password": a character string is expected.'
],
'Test connector.update: invalid "ssl_key_password" (too long)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'ssl_key_password' => str_repeat('a', DB::getFieldLength('connector', 'ssl_key_password') + 1)
],
'expected_error' => 'Invalid parameter "/1/ssl_key_password": value is too long.'
],
// Check "description".
'Test connector.update: invalid "description" (boolean)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'description' => false
],
'expected_error' => 'Invalid parameter "/1/description": a character string is expected.'
],
'Test connector.update: invalid "description" (too long)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'description' => str_repeat('a', DB::getFieldLength('connector', 'description') + 1)
],
'expected_error' => 'Invalid parameter "/1/description": value is too long.'
],
// Check "status".
'Test connector.update: invalid "status" (string)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'status' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/status": an integer is expected.'
],
'Test connector.update: invalid "status" (not in range)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'status' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/status": value must be one of '.
implode(', ', [ZBX_CONNECTOR_STATUS_DISABLED, ZBX_CONNECTOR_STATUS_ENABLED]).'.'
],
// Check "tags_evaltype".
'Test connector.update: invalid "tags_evaltype" (string)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags_evaltype' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/tags_evaltype": an integer is expected.'
],
'Test connector.update: invalid "tags_evaltype" (not in range)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags_evaltype' => self::INVALID_NUMBER
],
'expected_error' => 'Invalid parameter "/1/tags_evaltype": value must be one of '.
implode(', ', [CONDITION_EVAL_TYPE_AND_OR, CONDITION_EVAL_TYPE_OR]).'.'
],
// Check "tags".
'Test connector.update: invalid "tags" (string)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags' => 'abc'
],
'expected_error' => 'Invalid parameter "/1/tags": an array is expected.'
],
'Test connector.update: invalid "tags" (array with string)' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags' => ['abc']
],
'expected_error' => 'Invalid parameter "/1/tags/1": an array is expected.'
],
'Test connector.update: missing "tag" for "tags"' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags' => [
[]
]
],
'expected_error' => 'Invalid parameter "/1/tags/1": the parameter "tag" is missing.'
],
'Test connector.update: unexpected parameter for "tags"' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags' => [
['abc' => '']
]
],
'expected_error' => 'Invalid parameter "/1/tags/1": unexpected parameter "abc".'
],
'Test connector.update: invalid "tag" (boolean) for "tags"' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags' => [
['tag' => false]
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/tag": a character string is expected.'
],
'Test connector.update: invalid "tag" (empty string) for "tags"' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags' => [
['tag' => '']
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/tag": cannot be empty.'
],
'Test connector.update: invalid "tag" (too long) for "tags"' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags' => [
['tag' => str_repeat('a', DB::getFieldLength('connector_tag', 'tag') + 1)]
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/tag": value is too long.'
],
'Test connector.update: invalid "operator" (boolean) for "tags"' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags' => [
['tag' => 'abc', 'operator' => false]
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/operator": an integer is expected.'
],
'Test connector.update: invalid "operator" (not in range) for "tags"' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags' => [
['tag' => 'abc', 'operator' => self::INVALID_NUMBER]
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/operator": value must be one of '.
implode(', ', [CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL, CONDITION_OPERATOR_LIKE,
CONDITION_OPERATOR_NOT_LIKE, CONDITION_OPERATOR_EXISTS, CONDITION_OPERATOR_NOT_EXISTS
]).'.'
],
'Test connector.update: invalid "value" (boolean) for "tags"' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags' => [
['tag' => 'abc', 'operator' => CONDITION_OPERATOR_EQUAL, 'value' => false]
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/value": a character string is expected.'
],
'Test connector.update: invalid "value" (not empty) for "tags"' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags' => [
['tag' => 'abc', 'operator' => CONDITION_OPERATOR_EXISTS, 'value' => '123']
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/value": value must be empty.'
],
'Test connector.update: invalid "value" (not empty) for "tags" 2' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags' => [
['tag' => 'abc', 'operator' => CONDITION_OPERATOR_NOT_EXISTS, 'value' => '123']
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/value": value must be empty.'
],
'Test connector.update: invalid "value" (too long) for "tags"' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags' => [
[
'tag' => 'abc',
'operator' => CONDITION_OPERATOR_EQUAL,
'value' => str_repeat('a', DB::getFieldLength('connector_tag', 'value') + 1)
]
]
],
'expected_error' => 'Invalid parameter "/1/tags/1/value": value is too long.'
],
'Test connector.update: invalid "tag" (duplicate) for "tags"' => [
'connector' => [
'connectorid' => 'update_custom_defaults',
'tags' => [
['tag' => 'abc', 'operator' => CONDITION_OPERATOR_EQUAL, 'value' => '123'],
['tag' => 'abc', 'operator' => CONDITION_OPERATOR_EQUAL, 'value' => '123']
]
],
'expected_error' => 'Invalid parameter "/1/tags/2": value (tag, operator, value)=(abc, 0, 123) already exists.'
]
];
}
/**
* Data provider for connector.update. Array contains valid connector parameters.
*
* @return array
*/
public static function getConnectorUpdateDataValid(): array {
return [
'Test connector.update: update single connector without changes' => [
'connector' => [
'connectorid' => 'update_custom_defaults'
],
'expected_error' => null
],
'Test connector.update: update multiple connectors' => [
'connector' => [
[
'connectorid' => 'update_custom_defaults',
'name' => 'API test connector.update - first connector'
],
[
'connectorid' => 'update_authtype_basic',
'name' => 'API test connector.update - second connector'
]
],
'expected_error' => null
]
];
}
/**
* Test connector.update method.
*
* @dataProvider getConnectorUpdateDataInvalid
* @dataProvider getConnectorUpdateDataValid
*/
public function testConnector_Update(array $connectors, ?string $expected_error): void {
// Accept single and multiple connectors just like API method. Work with multidimensional array in result.
if (!array_key_exists(0, $connectors)) {
$connectors = zbx_toArray($connectors);
}
// Replace ID placeholders with real IDs.
foreach ($connectors as &$connector) {
$connector = self::resolveIds($connector);
}
unset($connector);
$sql_connectors = 'SELECT NULL FROM connector';
$old_hash_connectors = CDBHelper::getHash($sql_connectors);
if ($expected_error === null) {
$connectorids = array_column($connectors, 'connectorid');
$db_connectors = $this->getConnectors($connectorids);
$this->call('connector.update', $connectors, $expected_error);
$connectors_upd = $this->getConnectors($connectorids);
// Compare records from DB before and after API call.
foreach ($connectors as $connector) {
$db_connector = $db_connectors[$connector['connectorid']];
$connector_upd = $connectors_upd[$connector['connectorid']];
// Required fields.
$this->assertNotEmpty($connector_upd['name']);
$this->assertNotEmpty($connector_upd['url']);
// Numeric fields.
foreach (['protocol', 'data_type', 'max_records', 'max_senders', 'max_attempts', 'authtype',
'verify_peer', 'verify_host', 'status', 'tags_evaltype'] as $field) {
if (array_key_exists($field, $connector)) {
$this->assertEquals($connector[$field], $connector_upd[$field]);
}
else {
$this->assertEquals($db_connector[$field], $connector_upd[$field]);
}
}
// Text fields.
foreach (['timeout', 'http_proxy', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'description']
as $field) {
if (array_key_exists($field, $connector)) {
$this->assertSame($connector[$field], $connector_upd[$field]);
}
else {
$this->assertSame($db_connector[$field], $connector_upd[$field]);
}
}
if (in_array($connector_upd['authtype'], [ZBX_HTTP_AUTH_BASIC, ZBX_HTTP_AUTH_NTLM,
ZBX_HTTP_AUTH_KERBEROS, ZBX_HTTP_AUTH_DIGEST])) {
foreach (['username', 'password'] as $field) {
if (array_key_exists($field, $connector)) {
$this->assertSame($connector[$field], $connector_upd[$field]);
}
else {
$this->assertSame($db_connector[$field], $connector_upd[$field]);
}
}
}
else {
$this->assertEmpty($connector_upd['username']);
$this->assertEmpty($connector_upd['password']);
}
if ($connector_upd['authtype'] == ZBX_HTTP_AUTH_BEARER) {
$this->assertNotEmpty($connector_upd['token']);
if (array_key_exists($field, $connector)) {
$this->assertSame($connector['token'], $connector_upd['token']);
}
else {
$this->assertSame($db_connector['token'], $connector_upd['token']);
}
}
else {
$this->assertEmpty($connector_upd['token']);
}
// Tags.
if (array_key_exists('tags', $connector)) {
if ($connector['tags']) {
$this->assertNotEmpty($connector_upd['tags']);
$this->assertEqualsCanonicalizing($connector['tags'], $connector_upd['tags']);
}
else {
$this->assertEmpty($connector_upd['tags']);
}
}
else {
$this->assertEqualsCanonicalizing($db_connector['tags'], $connector_upd['tags']);
}
}
// Restore connector original data after each test.
$this->call('connector.update', $db_connectors);
}
else {
// Call method and make sure it really returns the error.
$this->call('connector.update', $connectors, $expected_error);
// Make sure nothing has changed as well.
$this->assertSame($old_hash_connectors, CDBHelper::getHash($sql_connectors));
}
}
/**
* Data provider for connector.delete. Array contains invalid connectors that are not possible to delete.
*
* @return array
*/
public static function getConnectorDeleteDataInvalid(): array {
return [
// Check connector IDs.
'Test connector.delete: empty ID' => [
'connectorids' => [''],
'expected_error' => 'Invalid parameter "/1": a number is expected.'
],
'Test connector.delete: non-existent ID' => [
'connectorids' => [self::INVALID_ID],
'expected_error' => 'No permissions to referred object or it does not exist!'
],
'Test connector.delete: with two same IDs' => [
'connectorids' => [0, 0],
'expected_error' => 'Invalid parameter "/2": value (0) already exists.'
]
];
}
/**
* Data provider for connector.delete. Array contains valid connectors.
*
* @return array
*/
public static function getConnectorDeleteDataValid(): array {
return [
'Test connector.delete: delete single connector' => [
'connector' => ['delete_single'],
'expected_error' => null
],
'Test connector.delete: delete multiple connectors' => [
'connector' => [
'delete_multiple_1',
'delete_multiple_2'
],
'expected_error' => null
]
];
}
/**
* Test connector.delete method.
*
* @dataProvider getConnectorDeleteDataInvalid
* @dataProvider getConnectorDeleteDataValid
*/
public function testConnector_Delete(array $connectorids, ?string $expected_error): void {
// Replace ID placeholders with real IDs.
foreach ($connectorids as &$connectorid) {
if (self::isValidIdPlaceholder($connectorid)) {
$connectorid = self::$data['connectorids'][$connectorid];
}
}
unset($connectorid);
$sql_connectors = 'SELECT NULL FROM connector';
$old_hash_connectors = CDBHelper::getHash($sql_connectors);
$this->call('connector.delete', $connectorids, $expected_error);
if ($expected_error === null) {
$this->assertNotSame($old_hash_connectors, CDBHelper::getHash($sql_connectors));
$this->assertEquals(0, CDBHelper::getCount(
'SELECT c.connectorid FROM connector c WHERE '.dbConditionId('c.connectorid', $connectorids)
));
// connector.delete checks if given IDs exist, so they need to be removed from self::$data['connectorids']
foreach ($connectorids as $connectorid) {
$key = array_search($connectorid, self::$data['connectorids']);
if ($key !== false) {
unset(self::$data['connectorids'][$key]);
}
}
}
else {
$this->assertSame($old_hash_connectors, CDBHelper::getHash($sql_connectors));
}
}
/**
* Get the original connectors before update.
*
* @param array $connectorids
*
* @return array
*/
private function getConnectors(array $connectorids): array {
$response = $this->call('connector.get', [
'output' => ['connectorid', 'name', 'protocol', 'data_type', 'url', 'max_records', 'max_senders',
'max_attempts', 'timeout', 'http_proxy', 'authtype', 'username', 'password', 'token', 'verify_peer',
'verify_host', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'description', 'status',
'tags_evaltype'
],
'selectTags' => ['tag', 'operator', 'value'],
'connectorids' => $connectorids,
'preservekeys' => true
]);
return $response['result'];
}
/**
* Delete all created data after test.
*/
public static function clearData(): void {
// Delete connectors.
$connectorids = array_values(self::$data['connectorids']);
$connectorids = array_merge($connectorids, self::$data['created']);
CDataHelper::call('connector.delete', $connectorids);
}
/**
* Helper method to convert placeholders to real IDs.
*
* @param array $request
*
* @return array
*/
private static function resolveIds(array $request): array {
if (array_key_exists('connectorids', $request)) {
if (is_array($request['connectorids'])) {
foreach ($request['connectorids'] as &$id_placeholder) {
if (self::isValidIdPlaceholder($id_placeholder)) {
$id_placeholder = self::$data['connectorids'][$id_placeholder];
}
}
unset($id_placeholder);
}
elseif (self::isValidIdPlaceholder($request['connectorids'])) {
$request['connectorids'] = self::$data['connectorids'][$request['connectorids']];
}
}
elseif (array_key_exists('connectorid', $request) && self::isValidIdPlaceholder($request['connectorid'])) {
$request['connectorid'] = self::$data['connectorids'][$request['connectorid']];
}
return $request;
}
/**
* Helper method to check ID placeholder.
*
* @param $id_placeholder
*
* @return bool
*/
private static function isValidIdPlaceholder($id_placeholder): bool {
// Do not compare != 0 (it will not work) or !== 0 or !== '0' (avoid type check here).
return !is_array($id_placeholder) && $id_placeholder != '0' && $id_placeholder !== ''
&& $id_placeholder !== null && $id_placeholder != self::INVALID_ID;
}
}