[], // 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; } }