def test_common_request_file_descriptor_closing(): keywords = RequestsKeywords() session = keywords.create_session('alias', 'http://mocking.rules') # this prevents a real network call from being executed session.get = mock.MagicMock() with open(os.path.join(SCRIPT_DIR, '../atests/randombytes.bin'), 'rb') as f: keywords._common_request('get', session, 'http://mocking.rules', data=f) assert f.closed is True
def __init__(self): self.params = Parameters() self.headers = Headers(APITEST="1") self.data = Data() self.requestBody = None self.status = Status() self.requestsKW = RequestsKeywords() self.fapiResponse = '' self.os = OperatingSystem() self.toObj = Json2Object()
def __init__(self): # disable requests warnings requests.packages.urllib3.disable_warnings() self.ROBOT_LIBRARY_LISTENER = self self.Request = RequestsKeywords() self.Request._utf8_urlencode = self._utf8_urlencode self.Request.__init__() self.alias = 'utfsession'
class FapiKeywords(object): ROBOT_LIBRARY_SCOPE = 'Global' def __init__(self): self.params = Parameters() self.headers = Headers(APITEST="1") self.data = Data() self.requestBody = None self.status = Status() self.requestsKW = RequestsKeywords() self.fapiResponse = '' self.os = OperatingSystem() self.toObj = Json2Object() def fapi_headers_set(self, *key_value_pairs, **items): """Adds the given ``key_value_pairs`` and ``items`` to HTTP request headers. Giving items as ``key_value_pairs`` means giving keys and values as separate arguments: | Fapi Headers Set | key | value | second | ${2} | => | headers = {'a': 1, 'key': 'value', 'second': 2} Starting from Robot Framework 2.8.1, items can also be given as kwargs using ``key=value`` syntax: | Fapi Headers Set | key=value | second=${2} | The latter syntax is typically more convenient to use, but it has a limitation that keys must be strings. If given keys already exist in request headers, their values are updated. """ self.headers.set(*key_value_pairs, **items) def fapi_headers_to_dictionary(self): """ Return all headers as a dictionary """ return self.headers.to_dictionary() def fapi_headers_reset(self): """ Reset request headers """ self.headers.reset() self.headers.set(APITEST="1") def fapi_params_set(self, *key_value_pairs, **items): """Adds the given ``key_value_pairs`` and ``items`` to HTTP request parameters. Giving items as ``key_value_pairs`` means giving keys and values as separate arguments: | Fapi Params Set | key | value | second | ${2} | => | parameters = {'a': 1, 'key': 'value', 'second': 2} Starting from Robot Framework 2.8.1, items can also be given as kwargs using ``key=value`` syntax: | Fapi Params Set | key=value | second=${2} | The latter syntax is typically more convenient to use, but it has a limitation that keys must be strings. If given keys already exist in request parameters, their values are updated. """ self.params.set(*key_value_pairs, **items) def fapi_params_to_dictionary(self): """ Return all request parameters as a dictionary """ return self.params.to_dictionary() def fapi_params_reset(self): """ Reset request parameters """ self.params.reset() self.headers.set(APITEST="1") def fapi_create_session(self, alias, url, timeout=None): """ Fapi Create Session: create a HTTP session to a server ``alias`` Robot Framework alias to identify the session ``url`` Base url of the server ``timeout`` Connection timeout """ fapiHeaders = self.fapi_headers_to_dictionary() self.requestsKW.create_session(alias, url, fapiHeaders, timeout=timeout) def fapi_post(self, alias, uri, data=None, timeout=None): """ Send a POST request on the session object found using the given `alias` ``alias`` that will be used to identify the Session object in the cache ``uri`` to send the POST request to ``data`` a dictionary of key-value pairs that will be urlencoded and sent as POST data or binary data that is sent as the raw body content ``timeout`` Connection timeout """ self.requestBody = data fapiHeaders = self.fapi_headers_to_dictionary() fapiParams = self.fapi_params_to_dictionary() fBodyData, fParams = self._format_data_according_to_header( alias, fapiParams, fapiHeaders) self.fapiResponse = self.requestsKW.post_request(alias, uri, data=fBodyData, params=fParams, headers=fapiHeaders, timeout=timeout) self.fapi_log_json(self.fapiResponse.content) self.fapi_params_reset() def fapi_post_files(self, alias, uri, filesDict, data=None, timeout=None): """ Upload a file to file server. ``alias`` that will be used to identify the Session object in the cache ``uri`` to send the POST request to ``filesDict`` a dictionary of file names containing file data to POST to the server ``data`` a dictionary of key-value pairs that will be urlencoded and sent as POST data or binary data that is sent as the raw body content ``timeout`` Connection timeout """ self.requestBody = data fapiHeaders = self.fapi_headers_to_dictionary() fapiParams = self.fapi_params_to_dictionary() fBodyData, fParams = self._format_data_according_to_header( alias, fapiParams, fapiHeaders) self.fapiResponse = self.requestsKW.post_request(alias, uri, data=fBodyData, params=fParams, files=filesDict, headers=fapiHeaders, timeout=timeout) self.fapi_log_json(self.fapiResponse.content) self.fapi_params_reset() def fapi_get(self, alias, uri, timeout=None): """ Send a GET request on the session object found using the given `alias` ``alias`` that will be used to identify the Session object in the cache ``uri`` to send the GET request to ``timeout`` Connection timeout """ fapiHeaders = self.fapi_headers_to_dictionary() fapiParams = self.fapi_params_to_dictionary() self.fapiResponse = self.requestsKW.get_request(alias, uri, params=fapiParams, headers=fapiHeaders, timeout=timeout) self.fapi_log_json(self.fapiResponse.content) self.fapi_params_reset() def fapi_put(self, alias, uri, data=None, timeout=None): """ Send a PUT request on the session object found using the given `alias` ``alias`` that will be used to identify the Session object in the cache ``uri`` to send the PUT request to ``data`` a dictionary of key-value pairs that will be urlencoded and sent as POST data or binary data that is sent as the raw body content ``timeout`` Connection timeout """ self.requestBody = data fapiHeaders = self.fapi_headers_to_dictionary() fapiParams = self.fapi_params_to_dictionary() fBodyData, fParams = self._format_data_according_to_header( alias, fapiParams, fapiHeaders) self.fapiResponse = self.requestsKW.put_request(alias, uri, data=fBodyData, params=fParams, headers=fapiHeaders, timeout=timeout) self.fapi_log_json(self.fapiResponse.content) self.fapi_params_reset() def fapi_delete(self, alias, uri, timeout=None): """ Send a DELETE request on the session object found using the given `alias` ``alias`` that will be used to identify the Session object in the cache ``uri`` to send the DELETE request to ``timeout`` Connection timeout """ fapiHeaders = self.fapi_headers_to_dictionary() fapiParams = self.fapi_params_to_dictionary() self.fapiResponse = self.requestsKW.delete_request(alias, uri, params=fapiParams, headers=fapiHeaders, timeout=timeout) self.fapi_log_json(self.fapiResponse.content) self.fapi_params_reset() def fapi_delete_all_sessions(self): """ Removes all the session objects """ self.requestsKW.delete_all_sessions() def fapi_request_should_be_succeed(self): """ Fapi HTTP response code should be 200 (means success) """ print("HTTP response code is " + str(self.fapiResponse.status_code)) assert self.fapiResponse.status_code == 200 def fapi_response_code(self): """ Return Fapi HTTP response code """ return self.fapiResponse.status_code def fapi_data_to_object(self): """ Convert Fapi response content to python object """ if self.fapiResponse.content is None: return None obj = self.toObj.json_to_object(self.fapiResponse.content) return obj.data def fapi_response_data(self): """ Return Fapi response data """ if self.fapiResponse.content is None: return None obj = json.loads(self.fapiResponse.content) data = obj['data'] return data def fapi_response_data_to_object(self): """ Convert Fapi response content to python object """ if self.fapiResponse.content is None: return None obj = self.toObj.json_to_object(self.fapiResponse.content) return obj def fapi_response_headers_to_dict(self): """ Convert Fapi response headers to dictionary """ return self.fapiResponse.headers def fapi_data_field_count(self, pointer): """ Get element count specified by object pointer """ return self.data.field_count(pointer) def fapi_data_field_count_should_be(self, pointer, expectedValue): """ Element count specified by object pointer should be equal to the given `expectedValue` ``expectedValue`` is the expected value of element count """ actualValue = self.data.field_count(pointer) self.fapi_log("Actual value is: " + str(actualValue)) assert actualValue == int(expectedValue) def fapi_data_field_count_should_not_be(self, pointer, expectedValue): """ Element count specified by object pointer should not be equal to the given `expectedValue` ``expectedValue`` is the not-expected value of element count """ actualValue = self.data.field_count(pointer) assert actualValue != int(expectedValue) def fapi_status_should_be_succeed(self): """ Fapi response status code should be 0 (means success) """ dataObj = self.toObj.json_to_object(self.fapiResponse.content) assert self.status.status_code(dataObj) == 0 def fapi_status_should_be(self, expectedCode): """ Fapi response status code should be equal to the `expectedCode` ``expectedCode`` is the expected Fapi response status code """ dataObj = self.toObj.json_to_object(self.fapiResponse.content) self.fapi_log("Actual status code is: " + str(self.status.status_code(dataObj))) assert self.status.status_code(dataObj) == int(expectedCode) def fapi_status_should_not_be(self, expectedCode): """ Fapi response status code should not be equal to the `expectedCode` ``expectedCode`` is the not-expected Fapi response status code """ dataObj = self.toObj.json_to_object(self.fapiResponse.content) self.fapi_log("Actual status code is: " + str(self.status.status_code(dataObj))) assert self.status.status_code(dataObj) != int(expectedCode) def fapi_log_json(self, jsonStr): """ Log json strings to test report """ try: json_ = json.loads(jsonStr) isFirstLine = True for line in json.dumps(json_, indent=2, ensure_ascii=False).split('\n'): if isFirstLine: logger.write("JSON string is : " + line) isFirstLine = False else: logger.write(line) except Exception: pass def fapi_log(self, message, repr=False): """ Log `message` to test report """ logger.write(message) def _format_data_according_to_header(self, alias, ftParams, headers): bodyData = None params = None if self.requestBody is not None and ftParams: bodyData = self.requestBody params = ftParams elif self.requestBody is not None: bodyData = self.requestBody else: if headers is not None and 'Content-Type' in headers and \ headers['Content-Type'].find("application/json") != -1: bodyData = ftParams elif headers is not None and 'Content-Type' in headers and \ headers['Content-Type'].find("application/x-www-form-urlencoded") != -1: params = ftParams return bodyData, params
class UtfKeywords(object): ROBOT_LIBRARY_SCOPE = 'GLOBAL' ROBOT_LISTENER_API_VERSION = 2 def __init__(self): # disable requests warnings requests.packages.urllib3.disable_warnings() self.ROBOT_LIBRARY_LISTENER = self self.Request = RequestsKeywords() self.Request._utf8_urlencode = self._utf8_urlencode self.Request.__init__() self.alias = 'utfsession' def login(self, url, user, password): data = { 'fn': 'ajax_login', 'args[]': [user, password], } self.Request.create_session( self.alias, url, headers={}, cookies=None, auth=None, timeout=3, proxies=None, verify=False) # do not verify ssl session = self.Request._cache.switch(self.alias) if url == 'http://127.0.0.1': session.trust_env = False # do not use HTTP_PROXY from env if localhost response = self.Request.post_request(alias=self.alias, uri='/', data=data, allow_redirects=False) assert response.status_code == requests.codes.ok, \ 'Response code must be 200, actually ==> {error}'.format(error=response.status_code) good_token = re.compile("[0-9a-f]{64}") token = self._extract_result(response) if good_token.match(token): logged_cookie_val = {"name": 'user-session', "value": token, "domain": 'utf.avp.ru'} session.cookies.set(**logged_cookie_val) return else: raise AssertionError("Unable to obtain cookie token: {0} ".format(token)) def utf_login(self, url, user, password): self.login(url, user, password) def add_vm(self, esx_id, datastore, vm_name): data = { 'fn': 'ajax_add_vmachines', 'args[]': (esx_id, "[{1}] {0}/{0}.vmx".format(vm_name, datastore)), } response = self.Request.post_request(alias=self.alias, uri='/', data=data, allow_redirects=False) assert response.status_code == requests.codes.ok, \ 'Response code must be 200, actually ==> {error}'.format(error=response.status_code) if not self._extract_result(response) == '1': raise AssertionError("Unable to add {0} machine".format(vm_name)) def delete_vm(self, esx_id, vm_name): vm_id = self._get_vm_id_by_name(esx_id, vm_name) data = { 'fn': 'ajax_delete_vmachines', 'args[]': vm_id, } response = self.Request.post_request(alias=self.alias, uri='/', data=data, allow_redirects=False) assert response.status_code == requests.codes.ok, \ 'Response code must be 200, actually ==> {error}'.format(error=response.status_code) if not self._extract_result(response) == '1': raise AssertionError("Unable to delete {0} machine".format(vm_name)) def revert_vm(self, esx_id, vm_name): vm_id = self._get_vm_id_by_name(esx_id, vm_name) data = {'fn': 'ajax_revert_vmachines', 'args[]': vm_id} response = self.Request.post_request(alias=self.alias, uri='/', data=data, allow_redirects=False) assert response.status_code == requests.codes.ok, \ 'Response code must be 200, actually ==> {error}'.format(error=response.status_code) if not self._extract_result(response) == '1': raise AssertionError("Unable to revert {0} machine".format(vm_name)) def start_vm(self, esx_id, vm_name, params={}): vm_id = self._get_vm_id_by_name(esx_id, vm_name) args = list() args.append(params.get('clean_required', 1)) args.append(params.get('keep_busy', 0)) args.append(vm_id) data = { 'fn': 'ajax_start_vmachines', 'args[]': args, } response = self.Request.post_request(alias=self.alias, uri='/', data=data, allow_redirects=False) assert response.status_code == requests.codes.ok, \ 'Response code must be 200, actually ==> {error}'.format(error=response.status_code) if not self._extract_result(response) == '1': raise AssertionError("Unable to start {0} machine".format(vm_name)) def set_vm_variables(self, esx_id, vm_name, params={}): if not esx_id or not vm_name: raise AssertionError("Please provide required parameters: esx_id/vm_name") vm_id = self._get_vm_id_by_name(esx_id, vm_name) for key in params.iterkeys(): args = list() args.append(vm_id) args.append(key.encode('ascii')) args.append(params[key].encode('ascii')) data = { 'fn': 'ajax_set_vmachine_var', 'args[]': args } response = self.Request.post_request(alias=self.alias, uri='/', data=data, allow_redirects=False) assert response.status_code == requests.codes.ok, \ 'Response code must be 200, actually ==> {error}'.format(error=response.status_code) if not self._extract_result(response) == '1': raise AssertionError("Unable to add vm variable {0} to machine {1}".format(args, vm_name)) def get_vm_variables(self, esx_id, vm_name, params={}): if not esx_id or not vm_name: raise AssertionError("Please provide required parameters: esx_id/vm_name") vm_id = self._get_vm_id_by_name(esx_id, vm_name) args = list() args.append(vm_id) data = { 'fn': 'ajax_select_vmachine', 'args[]': args } response = self.Request.get_request(alias=self.alias, uri='/', params=data, allow_redirects=True) assert response.status_code == requests.codes.ok, \ 'Response code must be 200, actually ==> {error}'.format(error=response.status_code) options = re.findall(r"<option value.+?>(.+?)=\"(.+?)\"<\/option", response.json()['result'][3], re.MULTILINE) options_dict = dict() for k, i in options: options_dict[k] = i return options_dict def set_project_variables(self, project_id, params={}): if not project_id: raise AssertionError("Please provide project id") for key in params.iterkeys(): args = list() args.append(project_id) args.append(key.encode('ascii')) args.append(params[key].encode('ascii')) data = { 'fn': 'ajax_set_project_var', 'args[]': args } response = self.Request.post_request(alias=self.alias, uri='/', data=data, headers=None, files={}, allow_redirects=False) assert response.status_code == requests.codes.ok, \ 'Response code must be 200, actually ==> {error}'.format(error=response.status_code) if not self._extract_result(response) == '1': raise AssertionError("Unable to set project variable {0}".format(args)) def get_project_variables(self, project_id): if not project_id: raise AssertionError("Please provide project id") args = list() args.append(project_id) data = { 'fn': 'ajax_load_project_vars', 'args[]': args } response = self.Request.post_request(alias=self.alias, uri='/', data=data, allow_redirects=False) assert response.status_code == requests.codes.ok, \ 'Response code must be 200, actually ==> {error}'.format(error=response.status_code) response_str = self._extract_result(response) response_str = re.sub('<.*?>', '', response_str) param_list = re.findall(r"(.+?)=\"(.+?)\"", response_str, re.MULTILINE) param_dict = dict() for key, value in param_list: param_dict[key] = value return param_dict def set_vm_options(self, esx_id, vm_name, opts={}): if not esx_id or not vm_name: raise AssertionError("Please provide required parameters: esx_id/vm_name") opts_low = dict((k.lower(), v) for k, v in opts.iteritems()) for k in opts_low.iterkeys(): if k not in ('delay', 'static_ip'): raise AssertionError("Option {0} is unknown. Provide only delay and/or static_ip.".format(k)) vm_id = self._get_vm_id_by_name(esx_id, vm_name) args = [ vm_id, vm_name, '' if 'delay' not in opts else opts['delay'], '' if 'static_ip' not in opts else opts['static_ip'], ] data = { 'fn': 'ajax_apply_vmachine_opts', 'args[]': args } response = self.Request.post_request(alias=self.alias, uri='/', data=data, allow_redirects=False) assert response.status_code == requests.codes.ok, \ 'Response code must be 200, actually ==> {error}'.format(error=response.status_code) if not self._extract_result(response) == '1': raise AssertionError("Unable to set vm option {0} to machine {1}".format(opts, vm_name)) def run_testset(self, esx_id, testset_id, vm_name, params={}): caller = 'ajax_run_testset' return self._runner(caller, esx_id, testset_id, vm_name, params) def run_test(self, esx_id, test_id, vm_name, params={}): caller = 'ajax_run_test' return self._runner(caller, esx_id, test_id, vm_name, params) def _runner(self, caller, esx_id, t_id, vm_name, params={}): params = self._asciize(params) if not t_id or not vm_name: raise AssertionError("Please provide required parameters: test or test suite id or vm id") if 'env' in params: if not isinstance(params['env'], dict): raise AssertionError("'env' from your parameters should be a dictionary") else: env = dict({}, **params['env']) else: env = {} for k in params.iterkeys(): if k not in ('clean_required', 'manual', 'revert', 'force_revert', 'ignore_timeout', 'prepare_only', 'env'): self.builtin.log("Key: {0} not in specials, will be added as env".format(k), 'INFO') env[k] = params[k] vm_id = self._get_vm_id_by_name(esx_id, vm_name) args = [ t_id, params.get('clean_required', 0), # Clean machine is required (0 or 1), params.get('manual', 0), # Keep machine BUSY after this job (0 or 1) params.get('revert', 0), # Revert machine after this job (0 or 1) params.get('force_revert', 0), # Revert after test/testset fatal fail (0 or 1) params.get('ignore_timeout', 0), # Ignore execution timeouts (0 or 1) params.get('prepare_only', 0), # Just prepare test environment (0 or 1) ] if len(env) != 0: for key, value in env.iteritems(): args.append(key + "=" + value) else: args.append('') args.append(vm_id) data = { 'fn': caller, 'args[]': args } response = self.Request.post_request(alias=self.alias, uri='/', data=data, allow_redirects=False) assert response.status_code == requests.codes.ok,\ 'Response code must be 200, actually ==> {error}'.format(error=response.status_code) response_res = self._extract_result(response) if re.search(r'\d{5}', response_res): return response_res else: raise AssertionError("Unable to run testset #{0}: {1}".format(t_id, response_res)) def _get_vm_id_by_name(self, esx_id, vm_name): data = { 'fn': 'ajax_load_vmachines', 'args[]': esx_id } response = self.Request.post_request(alias=self.alias, uri='/', data=data, allow_redirects=False) assert response.status_code == requests.codes.ok,\ 'Response code must be 200, actually ==> {error}'.format(error=response.status_code) found = re.search('<option value=\"([\d]+)\"([^<>]+)>{0}<'.format(vm_name), self._extract_result(response)) if not found: raise AssertionError("Unable to find {0} machine".format(vm_name)) vm_id = found.group(1) return vm_id def vm_exists(self, esx_id, vm_name): try: self._get_vm_id_by_name(esx_id, vm_name) return True except AssertionError: return False def get_vm_ip_by_name(self, esx_id, vm_name): vm_id = self._get_vm_id_by_name(esx_id, vm_name) data = { 'fn': 'ajax_select_vmachine', 'args[]': vm_id } response = self.Request.post_request(alias=self.alias, uri='/', data=data, allow_redirects=False) assert response.status_code == requests.codes.ok,\ 'Response code must be 200, actually ==> {error}'.format(error=response.status_code) return response.json()['result'][1].encode('ascii', 'ignore') def _extract_result(self, response): return response.json()['result'][0].encode('ascii', 'ignore') def _asciize(self, d={}): for k in d.iterkeys(): if isinstance(d[k], dict): d[k] = self._asciize(d[k]) else: d[k] = str(d[k]).encode('ascii', 'ignore') return d def wait_for_job_result(self, job_id): data = { 'fn': 'ajax_get_job_batches', 'args[]': job_id, } while True: try: response = self.Request.post_request(alias=self.alias, uri='/', data=data, allow_redirects=False) assert response.status_code == requests.codes.ok,\ 'Response code must be 200, actually ==> {error}'.format(error=response.status_code) response_str = self._extract_result(response) result = re.search('.*<div id=\"batch-' + str(job_id) + '\".*<div class=\"batch-header job-([a-z]+)\">.*', response_str, re.S) if result: status = result.group(1) if all([status != 'queued', status != 'running']): return status.upper() else: return 'JOB NOT FOUND' time.sleep(5) except Exception as err: logging.debug('ERROR ==> {}'.format(err)) return 'EXCEPTION' def string_for_http_request(self, value=''): return re.compile('[^a-zA-Z0-9]').sub(lambda x: str(hex(ord(x.group()))).replace('0x', '%').upper(), value) def get_testset_name_by_id(self, project_id, testset_id): response = self.Request.get_request(alias=self.alias, uri='/?project=' + str(project_id) + '&page=tests') result = re.search('<option value=\"' + str(testset_id) + '\">(.+)</option>', response.text) if result is None: return 'TESTSET NOT FOUND' else: return result.group(1) def _utf8_urlencode(self, data): if type(data) is unicode: data = data.encode('utf-8') return data