def generate_transaction_and_attach(step, node): """ Creates a zero value transaction with the specified arguments. :param node: The node that the transaction will be generated on. :param step.hashes: A gherkin table present in the feature file specifying the arguments and the associated type. """ arg_list = step.hashes world.config['nodeId'] = node world.config['apiCall'] = 'attachToTangle' options = {} api = api_utils.prepare_api_call(node) api_utils.prepare_options(arg_list, options) transaction_args = {} for key in options: transaction_args[key] = options.get(key) api_utils.prepare_transaction_arguments(transaction_args) transaction = transactions.create_and_attach_transaction( api, transaction_args) api.broadcast_and_store(transaction.get('trytes')) assert len( transaction['trytes']) > 0, "Transaction was not created correctly" world.responses['attachToTangle'] = {} world.responses['attachToTangle'][node] = transaction logger.info('Transaction Sent') setattr(static, "TEST_STORE_TRANSACTION", transaction.get('trytes'))
def compare_thread_return(step, api_call): """ Compares the response of a particular asynchronous API call with the expected list of response keys. :param api_call: The API call that you would like to check the response of :param step.hashes: A gherkin table outlining any arguments that are expected to be in the response """ # Prepare response list for comparison response_list = world.responses[api_call][world.config['nodeId']] # Exclude duration from response list if 'duration' in response_list: del response_list['duration'] response_keys = response_list.keys() # Prepare expected values list for comparison expected_values = {} args = step.hashes api_utils.prepare_options(args,expected_values) keys = expected_values.keys() # Confirm that the lists are of equal length before comparing assert len(keys) == len(response_keys), "Response: {} does not contain""\ ""the same number of arguments: {}".format(keys,response_keys) for count in range(len(keys)): response_key = response_keys[count] response_value = response_list[response_key] expected_value = expected_values[response_key] assert response_value == expected_value, "Returned: {} does not match the expected"" \ ""value: {}".format(response_value, expected_value) logger.info('Responses match')
def compare_thread_return(step, api_call): """ Prepare response list for comparison. :param api_call: The API call you would like to find a response for :param step.hashes: A gherkin table present in the feature file specifying the values and the associated type to be found in the response. """ logger.debug(world.responses) future_results = world.config['future_results'][api_call] for result in future_results: response_list = pool.fetch_results(result, 5) # Exclude duration from response list if 'duration' in response_list: del response_list['duration'] if 'info' in response_list: del response_list['info'] response_keys = response_list.keys() expected_values = {} api_utils.prepare_options(step.hashes, expected_values) keys = expected_values.keys() # Confirm that the lists are of equal length before comparing assert len(keys) == len(response_keys), \ 'Response: {} does not contain the same number of arguments: {}'.format(keys, response_keys) for count in range(len(keys)): response_key = response_keys[count] response_value = response_list[response_key] expected_value = expected_values[response_key] assert response_value == expected_value, \ 'Returned: {} does not match the expected value: {}'.format(response_value, expected_value)
def threaded_call(step, apiCall, node): logger.info("Creating thread for {}".format(apiCall)) world.config['apiCall'] = apiCall world.config['nodeId'] = node arg_list = step.hashes options = {} api_utils.prepare_options(arg_list, options) api = api_utils.prepare_api_call(node) def make_call(node, arg_list): response = api_utils.fetch_call(apiCall, arg_list['api'], arg_list['options']) arg_list['responses'][apiCall] = {} arg_list['responses'][apiCall][node] = response return response args = { node: { 'api': api, 'options': options, 'responses': world.responses } } future_results = pool.start_pool(make_call, 1, args) if 'future_results' not in world.config: world.config['future_results'] = {} world.config['future_results'][apiCall] = future_results
def generate_transaction_and_attach(step, node): arg_list = step.hashes world.config['nodeId'] = node world.config['apiCall'] = 'attachToTangle' options = {} api = api_utils.prepare_api_call(node) api_utils.prepare_options(arg_list, options) addresses = options.get('address') value = options.get('value') transaction = ProposedTransaction(address=Address(addresses[0]), value=value) bundle = ProposedBundle() bundle.add_transaction(transaction) bundle.finalize() trytes = str(bundle[0].as_tryte_string()) gtta = api.get_transactions_to_approve(depth=3) branch = str(gtta['branchTransaction']) trunk = str(gtta['trunkTransaction']) sent = api.attach_to_tangle(trunk, branch, [trytes], 9) world.responses['attachToTangle'] = {} world.responses['attachToTangle'][node] = sent logger.info('Transaction Sent') setattr(static_vals, "TEST_STORE_TRANSACTION", sent.get('trytes'))
def compare_thread_return(step, apiCall): """Prepare response list for comparison""" logger.debug(world.responses) future_results = world.config['future_results'][apiCall] for result in future_results: response_list = pool.fetch_results(result, 1) # Exclude duration from response list if 'duration' in response_list: del response_list['duration'] response_keys = response_list.keys() expected_values = {} api_utils.prepare_options(step.hashes, expected_values) keys = expected_values.keys() # Confirm that the lists are of equal length before comparing assert len(keys) == len( response_keys ), 'Response: {} does not contain the same number of arguments: {}'.format( keys, response_keys) for count in range(len(keys)): response_key = response_keys[count] response_value = response_list[response_key] expected_value = expected_values[response_key] assert response_value == expected_value, \ 'Returned: {} does not match the expected value: {}'.format(response_value,expected_value) logger.info('Responses match')
def check_response_for_value(step, api_call): """ Compares the response of a particular API call with the expected list of response keys. :param api_call: The API call that you would like to check the response of :param step.hashes: A gherkin table outlining any arguments that are expected to be in the response """ response_values = world.responses[api_call][world.config['nodeId']] expected_values = {} args = step.hashes api_utils.prepare_options(args, expected_values) for expected_value_key in expected_values: if expected_value_key in response_values: expected_value = expected_values[expected_value_key] response_value = response_values[expected_value_key] if isinstance( response_value, list ) and api_call != 'getTrytes' and api_call != 'getInclusionStates': response_value = response_value[0] assert expected_value == response_value, "The expected value {} does not match" "\ " "the response value: {}".format( expected_value, response_value) logger.info('Response contained expected values')
def check_response_for_value(step, apiCall): response_values = world.responses[apiCall][world.config['nodeId']] expected_values = {} args = step.hashes api_utils.prepare_options(args, expected_values) for expected_value_key in expected_values: if expected_value_key in response_values: expected_value = expected_values[expected_value_key] response_value = response_values[expected_value_key] if type(response_value) is list: response_value = response_value[0] assert expected_value == response_value, \ "The expected value {} does not match the response value: {}".format(expected_value,response_value) logger.info('Response contained expected values')
def spam_call(step, api_call, num_tests, node): """ Spams an API call a number of times among the specified nodes in a cluster :param api_call: The API call you would like to make :param num_tests: The number of iterations you would like to run :param node: The node that the call will be sent to. This can be set to 'all nodes' and it will run the test on all the available nodes. :param step.hashes: A gherkin table present in the feature file specifying the arguments and the associated type. """ start = time() world.config['apiCall'] = api_call arg_list = step.hashes nodes = {} response_val = [] options = {} api_utils.prepare_options(arg_list, options) # See if call will be made on one node or all api_utils.assign_nodes(node, nodes) call_node = world.config['nodeId'] def run_call(node, args): logger.debug('Running Thread on {}'.format(node)) api = args['api'] options = args['options'] response = api_utils.fetch_call(api_call, api, options) return response for current_node in nodes: nodes[current_node]['options'] = options future_results = pool.start_pool(run_call, num_tests, nodes) responses.fetch_future_results(future_results, num_tests, response_val) world.responses[api_call] = {} world.responses[api_call][call_node] = response_val end = time() time_spent = end - start logger.info('Time spent on loop: {}'.format(time_spent))
def api_method_is_called(step, apiCall, nodeName): """ This is the general api calling function. There are 3 inputs :param apiCAll: The api call that will be requested :param nodeName: The name identifying the node you would like to make this request on :param table: A gherkin table outlining any arguments needed for the call (See tests/features/machine1/1_api+tests.feature for examples) The table parameter is unique in that there are several input types available depending on the call being made. :type string: Basic string argument, will be taken as is :type int: Basic integer argument, will be converted to int before call is made :type nodeAddress: Node name identifier, will create address from node configuration :type staticValue: Static name identifier, will fetch value from util/static_vals.py :type staticList: Same as staticValue, except it places the results into a list :type responseValue: Identifier for api call response value :type responseList: Same as responseValue, ecept it places the results into a list :type bool: Bool argument, returns True or False """ logger.info('%s is called on %s', apiCall, nodeName) world.config['apiCall'] = apiCall world.config['nodeId'] = nodeName arg_list = step.hashes options = {} api_utils.prepare_options(arg_list, options) api = api_utils.prepare_api_call(nodeName) response = api_utils.fetch_call(apiCall, api, options) assert type( response ) is dict, 'There may be something wrong with the response format: {}'.format( response) world.responses[apiCall] = {} world.responses[apiCall][nodeName] = response
def threaded_call(step, api_call, node): """ Makes an asynchronous API call on the specified node and stores the future result reference in the world.config variable. :param api_call: The API call you would like to make. :param node: The identifier for the node you would like to run the call on. :param step.hashes: A gherkin table present in the feature file specifying the arguments and the associated type. """ logger.info("Creating thread for {}".format(api_call)) world.config['apiCall'] = api_call world.config['nodeId'] = node arg_list = step.hashes options = {} api_utils.prepare_options(arg_list, options) api = api_utils.prepare_api_call(node) def make_call(node, arg_list): response = api_utils.fetch_call(api_call, arg_list['api'], arg_list['options']) arg_list['responses'][api_call] = {} arg_list['responses'][api_call][node] = response return response args = { node: { 'api': api, 'options': options, 'responses': world.responses } } future_results = pool.start_pool(make_call, 1, args) if 'future_results' not in world.config: world.config['future_results'] = {} world.config['future_results'][api_call] = future_results
def read_ls_metadata(step, node): """ Uses an ixi module to check the current snapshot state of the node. It cycles through a provided list of addresses to make sure the snapshot state contains them. :param step.hashes: A pointer to the list of milestone hashes that should be present in the snapshot metadata :param node: The node that the IXI request will be made on """ arg_list = step.hashes options = {} api_utils.prepare_options(arg_list, options) command = {"command": "LocalSnapshots.getMetaData"} request_return = api_utils.send_ixi_request(node, command) assert 'ixi' in request_return, "Error: {}".format(request_return['error']) node_metadata = request_return['ixi']['metaData'] hashes = options['hashes'] for hash_val in hashes: assert hash_val in node_metadata, "Provided hash: {} was not found in the snapshot metadata".format( hash_val) logger.info("Snapshot Metadata contains the provided hashes.")
def read_ls_state(step, node): """ Uses an ixi module to check the current snapshot state of the node. It cycles through a provided list of addresses to make sure the snapshot state contains them. :param step.hashes: A pointer to the list of addresses that should be present in the snapshot state :param node: The node that the IXI request will be made on """ arg_list = step.hashes options = {} api_utils.prepare_options(arg_list, options) command = {"command": "LocalSnapshots.getState"} request_return = api_utils.send_ixi_request(node, command) assert 'ixi' in request_return, "Error: {}".format(request_return['error']) node_state = request_return['ixi']['state'] addresses_with_value = options['address'] for address in addresses_with_value: assert address in node_state, "Provided address: {} was not found in the snapshot state".format( address) logger.info("Snapshot State contains the provided addresses.")
def fetch_transaction_from_list(args, node): """ Fetches a reference transaction from either a static value list in the ../staticValues.py file, or from the response for a previous "findTransactions" call. :param args: The step argument list :param node: The current working node :return: The transaction to be used as a reference """ options = {} api_utils.prepare_options(args, options) if args[0]['type'] == 'responseValue': transaction_list = value_fetch.fetch_response(args[0]['values']) reference_transaction = transaction_list[node][len(transaction_list) - 1] elif args[0]['type'] == 'staticValue': transaction_list = options['transactions'] reference_transaction = transaction_list[len(transaction_list) - 1] assert reference_transaction, "No reference transaction found (Possibly incorrect argument type, check gherkin file" return reference_transaction
def evaluate_and_send(api, seed, arg_list): """ Prepares a transaction for sending. If the provided seed isn't empty, it changes the bool value to be passed on to to the create_and_attach_transaction() function to instruct it to look for an available balance. :param api: The api target you would like to make the call to :param seed: The seed associated with the given api (This can be an empty string if none is used) :param arg_list: The argument list (dictionary) for the transaction :return: The transaction object created in the create_and_attach_transaction() function """ is_value_transaction = False if seed != "": is_value_transaction = True options = {} api_utils.prepare_options(arg_list, options) api_utils.prepare_transaction_arguments(options) transaction = create_and_attach_transaction(api, is_value_transaction, options) api.broadcast_and_store(transaction.get('trytes')) return transaction