diff --git a/Dockerfile-py3.5 b/Dockerfile-py3.5 new file mode 100644 index 0000000..e6ad758 --- /dev/null +++ b/Dockerfile-py3.5 @@ -0,0 +1,98 @@ +# vim:set ft=dockerfile: +FROM debian:jessie + +# See https://github.com/docker-library/python/blob/master/3.5/Dockerfile + +# ensure local python is preferred over distribution python +ENV PATH /usr/local/bin:$PATH + +# http://bugs.python.org/issue19846 +# > At the moment, setting "LANG=C" on a Linux system *fundamentally breaks Python 3*, and that's not OK. +ENV LANG C.UTF-8 + +# runtime dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + wget \ + autoconf \ + automake \ + bzip2 \ + file \ + g++ \ + gcc \ + libssl-dev \ + libtool \ + make \ + patch \ + xz-utils \ + tcl \ + tk \ + && rm -rf /var/lib/apt/lists/* + +ENV GPG_KEY 97FC712E4C024BBEA48A61ED3A5CA953F73C700D +ENV PYTHON_VERSION 3.5.3 + +# if this is called "PIP_VERSION", pip explodes with "ValueError: invalid truth value ''" +ENV PYTHON_PIP_VERSION 9.0.1 + +RUN set -ex \ + && buildDeps=' \ + tcl-dev \ + tk-dev \ + ' \ + && apt-get update && apt-get install -y $buildDeps --no-install-recommends && rm -rf /var/lib/apt/lists/* \ + \ + && wget -O python.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz" \ + && wget -O python.tar.xz.asc "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc" \ + && export GNUPGHOME="$(mktemp -d)" \ + && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$GPG_KEY" \ + && gpg --batch --verify python.tar.xz.asc python.tar.xz \ + && rm -r "$GNUPGHOME" python.tar.xz.asc \ + && mkdir -p /usr/src/python \ + && tar -xJC /usr/src/python --strip-components=1 -f python.tar.xz \ + && rm python.tar.xz \ + \ + && cd /usr/src/python \ + && ./configure \ + --enable-loadable-sqlite-extensions \ + --enable-shared \ + && make -j$(nproc) \ + && make install \ + && ldconfig \ + \ +# explicit path to "pip3" to ensure distribution-provided "pip3" cannot interfere + && if [ ! -e /usr/local/bin/pip3 ]; then : \ + && wget -O /tmp/get-pip.py 'https://bootstrap.pypa.io/get-pip.py' \ + && python3 /tmp/get-pip.py "pip==$PYTHON_PIP_VERSION" \ + && rm /tmp/get-pip.py \ + ; fi \ +# we use "--force-reinstall" for the case where the version of pip we're trying to install is the same as the version bundled with Python +# ("Requirement already up-to-date: pip==8.1.2 in /usr/local/lib/python3.6/site-packages") +# https://github.com/docker-library/python/pull/143#issuecomment-241032683 + && pip3 install --no-cache-dir --upgrade --force-reinstall "pip==$PYTHON_PIP_VERSION" \ +# then we use "pip list" to ensure we don't have more than one pip version installed +# https://github.com/docker-library/python/pull/100 + && [ "$(pip list |tac|tac| awk -F '[ ()]+' '$1 == "pip" { print $2; exit }')" = "$PYTHON_PIP_VERSION" ] \ + \ + && find /usr/local -depth \ + \( \ + \( -type d -a -name test -o -name tests \) \ + -o \ + \( -type f -a -name '*.pyc' -o -name '*.pyo' \) \ + \) -exec rm -rf '{}' + \ + && apt-get purge -y --auto-remove $buildDeps \ + && rm -rf /usr/src/python ~/.cache + +# make some useful symlinks that are expected to exist +RUN cd /usr/local/bin \ + && { [ -e easy_install ] || ln -s easy_install-* easy_install; } \ + && ln -s idle3 idle \ + && ln -s pydoc3 pydoc \ + && ln -s python3 python \ + && ln -s python3-config python-config + +ADD . /opt/B2HANDLE + +WORKDIR /opt/B2HANDLE + +RUN python setup.py install diff --git a/MANIFEST.in b/MANIFEST.in index bb3ec5f..c61871e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,4 @@ include README.md +include b2handle/tests/resources/*PUBLIC.json +include b2handle/tests/testcredentials/*_PUBLIC.json +include b2handle/tests/testcredentials/fake_certs_and_keys/*.pem \ No newline at end of file diff --git a/README.md b/README.md index 26b18b5..e1d6938 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ The b2handle Python library is a client library for interaction with a [Handle System](https://handle.net) server, using the native REST interface introduced in Handle System 8. The library offers methods to create, update and delete Handles as well as advanced functionality such as searching over Handles using an additional search servlet and managing multiple location entries per Handle. -The library currently supports Python 2.6 and Python 2.7 and requires at least a Handle System server 8.1. +The library currently supports Python 2.6, 2.7 and 3.5, and requires at least a Handle System server 8.1. The library requires OpenSSL v1.0.1 or higher. # Test Coverage and Continuous Integration diff --git a/b2handle/__init__.py b/b2handle/__init__.py index 48a6fe3..7195dc1 100644 --- a/b2handle/__init__.py +++ b/b2handle/__init__.py @@ -1,4 +1,11 @@ -# -# The version as used in setup.py and docs/source/conf.py -__version__ = "1.0.3" +__version__ = "1.1.0" + +# The version as used in setup.py and docs/source/conf.py. + +# IMPORTANT +# Please put no comment above the __version__ variable, +# as they would be printed in the help(b2handle) output! +# (The first comment - empty or not - would be appended to +# the package name, the next non-empty comment would be +# printed as description). diff --git a/b2handle/clientcredentials.py b/b2handle/clientcredentials.py index c7dcce3..a545e83 100644 --- a/b2handle/clientcredentials.py +++ b/b2handle/clientcredentials.py @@ -11,7 +11,7 @@ import os import logging import b2handle -from b2handle.handleexceptions import CredentialsFormatError +from b2handle.handleexceptions import CredentialsFormatError, HandleSyntaxError import b2handle.util as util LOGGER = logging.getLogger(__name__) @@ -50,8 +50,10 @@ def load_from_JSON(json_filename): :raises: :exc:`~b2handle.handleexceptions.HandleSyntaxError` :return: An instance. ''' - - jsonfilecontent = json.loads(open(json_filename, 'r').read()) + try: + jsonfilecontent = json.loads(open(json_filename, 'r').read()) + except ValueError as exc: + raise CredentialsFormatError(msg="Invalid JSON syntax: "+str(exc)) instance = PIDClientCredentials(credentials_filename=json_filename,**jsonfilecontent) return instance @@ -202,7 +204,7 @@ def __get_path_and_check_file_existence(self, path): path = util.get_absolute_path(path, self.__credentials_filename) except ValueError: # not a valid path - thisdir = utilsget_this_directory(self.__credentials_filename) + thisdir = util.get_this_directory(self.__credentials_filename) msg = ('Please provide an absolute path or a path relative to ' 'the location of the credentials file\'s location (%s), ' 'starting with %s.' % (thisdir, os.path.curdir)) diff --git a/b2handle/compatibility_helper.py b/b2handle/compatibility_helper.py new file mode 100644 index 0000000..3d4c221 --- /dev/null +++ b/b2handle/compatibility_helper.py @@ -0,0 +1,22 @@ +import sys +from six import string_types +''' +This module provides some helper functions to ease porting the codebase + to Python 3.5.. + +''' +def check_response_content_type(response): + if isinstance(response.content, string_types): + return True + +def decoded_response(response): + if (check_response_content_type(response)): + return response.content + return response.content.decode('utf-8') + +def set_encoding_variable(): + if sys.version_info > (3, 0): + encoding_value = 'unicode' + else: + encoding_value = 'utf-8' + return encoding_value diff --git a/b2handle/handleclient.py b/b2handle/handleclient.py index 6d83d31..4606806 100644 --- a/b2handle/handleclient.py +++ b/b2handle/handleclient.py @@ -5,7 +5,8 @@ Author: Merret Buurman (DKRZ), 2015-2016 ''' - +from __future__ import absolute_import +from past.builtins import xrange # pylint handleclient.py --method-rgx="[a-z_][a-zA-Z0-9_]{2,30}$" --max-line-length=250 --variable-rgx="[a-z_][a-zA-Z0-9_]{2,30}$" --attr-rgx="[a-z_][a-zA-Z0-9_]{2,30}$" --argument-rgx="[a-z_][a-zA-Z0-9_]{2,30}$" import json @@ -13,28 +14,30 @@ import uuid import logging import datetime -import utilhandle -import util -import requests # This import is needed for mocking in unit tests. -from handleexceptions import HandleNotFoundException -from handleexceptions import GenericHandleError -from handleexceptions import BrokenHandleRecordException -from handleexceptions import HandleAlreadyExistsException -from handleexceptions import IllegalOperationException -from handlesystemconnector import HandleSystemConnector -from searcher import Searcher -import hsresponses - +from . import utilhandle +import requests # This import is needed for mocking in unit tests. +from .handleexceptions import HandleNotFoundException +from .handleexceptions import GenericHandleError +from .handleexceptions import BrokenHandleRecordException +from .handleexceptions import HandleAlreadyExistsException +from .handleexceptions import IllegalOperationException +from .handlesystemconnector import HandleSystemConnector +from .searcher import Searcher +from . import hsresponses +from . import util +from b2handle.compatibility_helper import decoded_response, set_encoding_variable + + # parameters for debugging -#LOG_FILENAME = 'example.log' -#logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG) +# LOG_FILENAME = 'example.log' +# logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG) LOGGER = logging.getLogger(__name__) LOGGER.addHandler(util.NullHandler()) REQUESTLOGGER = logging.getLogger('log_all_requests_of_testcases_to_file') REQUESTLOGGER.propagate = False REQUESTLOGGER.addHandler(util.NullHandler()) - +encoding_value = set_encoding_variable() class EUDATHandleClient(object): ''' B2Handle main client class. @@ -95,9 +98,9 @@ def __init__(self, handle_server_url=None, **args): certificate and private key, used for authentication in write mode. ''' - util.log_instantiation(LOGGER, 'EUDATHandleClient', args, ['password','reverselookup_password'], with_date=True) + util.log_instantiation(LOGGER, 'EUDATHandleClient', args, ['password', 'reverselookup_password'], with_date=True) - LOGGER.debug('\n'+60*'*'+'\nInstantiation of EUDATHandleClient\n'+60*'*') + LOGGER.debug('\n' + 60 * '*' + '\nInstantiation of EUDATHandleClient\n' + 60 * '*') args['handle_server_url'] = handle_server_url @@ -113,7 +116,7 @@ def __init__(self, handle_server_url=None, **args): # Defaults: defaults = { - 'HS_ADMIN_permissions':'011111110011', # default from hdl-admintool + 'HS_ADMIN_permissions':'011111110011', # default from hdl-admintool 'modify_HS_ADMIN': False } @@ -129,32 +132,32 @@ def __store_args_or_set_to_defaults(self, args, defaults): if 'HS_ADMIN_permissions' in args.keys(): self.__HS_ADMIN_permissions = args['HS_ADMIN_permissions'] - LOGGER.info(' - HS_ADMIN_permissions set to: '+self.__HS_ADMIN_permissions) + LOGGER.info(' - HS_ADMIN_permissions set to: ' + self.__HS_ADMIN_permissions) else: self.__HS_ADMIN_permissions = defaults['HS_ADMIN_permissions'] - LOGGER.info(' - HS_ADMIN_permissions set to default: '+self.__HS_ADMIN_permissions) + LOGGER.info(' - HS_ADMIN_permissions set to default: ' + self.__HS_ADMIN_permissions) if '10320LOC_chooseby' in args.keys(): self.__10320LOC_chooseby = args['10320LOC_chooseby'] - LOGGER.info(' - 10320LOC_chooseby set to: '+self.__10320LOC_chooseby) + LOGGER.info(' - 10320LOC_chooseby set to: ' + self.__10320LOC_chooseby) else: LOGGER.info(' - 10320LOC_chooseby: No default.') if 'modify_HS_ADMIN' in args.keys(): self.__modify_HS_ADMIN = args['modify_HS_ADMIN'] - LOGGER.info(' - modify_HS_ADMIN set to: '+str(self.__modify_HS_ADMIN)) + LOGGER.info(' - modify_HS_ADMIN set to: ' + str(self.__modify_HS_ADMIN)) else: self.__modify_HS_ADMIN = defaults['modify_HS_ADMIN'] - LOGGER.info(' - modify_HS_ADMIN set to default: '+str(self.__modify_HS_ADMIN)) + LOGGER.info(' - modify_HS_ADMIN set to default: ' + str(self.__modify_HS_ADMIN)) # Handle owner: The user name to be written into HS_ADMIN. # Can be specified in json credentials file (optionally): if ('handleowner' in args.keys()) and (args['handleowner'] is not None): self.__handleowner = args['handleowner'] - LOGGER.info(' - handleowner set to: '+self.__handleowner) + LOGGER.info(' - handleowner set to: ' + self.__handleowner) else: self.__handleowner = None LOGGER.info(' - handleowner: Will be set to default for each created handle separately.') @@ -194,7 +197,7 @@ def instantiate_for_read_and_search(handle_server_url, reverselookup_username, r ''' if handle_server_url is None and 'reverselookup_baseuri' not in config.keys(): - raise TypeError('You must specify either "handle_server_url" or "reverselookup_baseuri".'+\ + raise TypeError('You must specify either "handle_server_url" or "reverselookup_baseuri".' + \ ' Searching not possible without the URL of a search servlet.') inst = EUDATHandleClient( @@ -246,7 +249,7 @@ def instantiate_with_credentials(credentials, **config): key_value_pairs = credentials.get_all_args() if config is not None: - key_value_pairs.update(**config) # passed config overrides json file + key_value_pairs.update(**config) # passed config overrides json file inst = EUDATHandleClient(**key_value_pairs) return inst @@ -269,11 +272,16 @@ def retrieve_handle_record_json(self, handle): LOGGER.debug('retrieve_handle_record_json...') utilhandle.check_handle_syntax(handle) + # pdb.set_trace() response = self.__send_handle_get_request(handle) + response_content = decoded_response(response) + + # pdb.set_trace() if hsresponses.handle_not_found(response): return None elif hsresponses.does_handle_exist(response): - handlerecord_json = json.loads(response.content) + + handlerecord_json = json.loads(response_content) if not handlerecord_json['handle'] == handle: raise GenericHandleError( operation='retrieving handle record', @@ -283,7 +291,7 @@ def retrieve_handle_record_json(self, handle): ) return handlerecord_json elif hsresponses.is_handle_empty(response): - handlerecord_json = json.loads(response.content) + handlerecord_json = json.loads(response_content) return handlerecord_json else: raise GenericHandleError( @@ -291,7 +299,8 @@ def retrieve_handle_record_json(self, handle): handle=handle, response=response ) - + + def retrieve_handle_record(self, handle, handlerecord_json=None): ''' Retrieve a handle record from the Handle server as a dict. If there @@ -311,7 +320,7 @@ def retrieve_handle_record(self, handle, handlerecord_json=None): handlerecord_json = self.__get_handle_record_if_necessary(handle, handlerecord_json) if handlerecord_json is None: - return None # Instead of HandleNotFoundException! + return None # Instead of HandleNotFoundException! list_of_entries = handlerecord_json['values'] record_as_dict = {} @@ -350,8 +359,8 @@ def get_value_from_handle(self, handle, key, handlerecord_json=None): return None else: if len(indices) > 1: - LOGGER.debug('get_value_from_handle: The handle '+handle+\ - ' contains several entries of type "'+key+\ + LOGGER.debug('get_value_from_handle: The handle ' + handle + \ + ' contains several entries of type "' + key + \ '". Only the first one is returned.') return list_of_entries[indices[0]]['data']['value'] @@ -510,38 +519,38 @@ def modify_handle_value(self, handle, ttl=None, add_if_not_exist=True, **kvpairs new_list_of_entries = [] list_of_old_and_new_entries = list_of_entries[:] keys = kvpairs.keys() - for key, newval in kvpairs.iteritems(): + for key, newval in kvpairs.items(): # Change existing entry: changed = False for i in xrange(len(list_of_entries)): if list_of_entries[i]['type'] == key: if not changed: list_of_entries[i]['data'] = newval - list_of_entries[i].pop('timestamp') # will be ignored anyway + list_of_entries[i].pop('timestamp') # will be ignored anyway if key == 'HS_ADMIN': newval['permissions'] = self.__HS_ADMIN_permissions - list_of_entries[i].pop('timestamp') # will be ignored anyway + list_of_entries[i].pop('timestamp') # will be ignored anyway list_of_entries[i]['data'] = { 'format':'admin', 'value':newval } - LOGGER.info('Modified'+\ - ' "HS_ADMIN" of handle '+handle) + LOGGER.info('Modified' + \ + ' "HS_ADMIN" of handle ' + handle) changed = True nothingchanged = False new_list_of_entries.append(list_of_entries[i]) list_of_old_and_new_entries.append(list_of_entries[i]) else: - msg = 'There is several entries of type "'+key+'".'+\ - ' This can lead to unexpected behaviour.'+\ + msg = 'There is several entries of type "' + key + '".' + \ + ' This can lead to unexpected behaviour.' + \ ' Please clean up before modifying the record.' raise BrokenHandleRecordException(handle=handle, msg=msg) # If the entry doesn't exist yet, add it: if not changed: if add_if_not_exist: - LOGGER.debug('modify_handle_value: Adding entry "'+key+'"'+\ - ' to handle '+handle) + LOGGER.debug('modify_handle_value: Adding entry "' + key + '"' + \ + ' to handle ' + handle) index = self.__make_another_index(list_of_old_and_new_entries) entry_to_add = self.__create_entry(key, newval, index, ttl) new_list_of_entries.append(entry_to_add) @@ -556,8 +565,8 @@ def modify_handle_value(self, handle, ttl=None, add_if_not_exist=True, **kvpairs # append to the old record: if nothingchanged: - LOGGER.debug('modify_handle_value: There was no entries '+\ - str(kvpairs.keys())+' to be modified (handle '+handle+').'+\ + LOGGER.debug('modify_handle_value: There was no entries ' + \ + str(kvpairs.keys()) + ' to be modified (handle ' + handle + ').' + \ ' To add them, set add_if_not_exist = True') else: op = 'modifying handle values' @@ -568,9 +577,9 @@ def modify_handle_value(self, handle, ttl=None, add_if_not_exist=True, **kvpairs overwrite=True, op=op) if hsresponses.handle_success(resp): - LOGGER.info('Handle modified: '+handle) + LOGGER.info('Handle modified: ' + handle) else: - msg = 'Values: '+str(kvpairs) + msg = 'Values: ' + str(kvpairs) raise GenericHandleError( operation=op, handle=handle, @@ -617,20 +626,20 @@ def delete_handle_value(self, handle, key): if key not in keys_done: indices_onekey = self.get_handlerecord_indices_for_key(key, list_of_entries) - indices = indices+indices_onekey + indices = indices + indices_onekey keys_done.append(key) # Important: If key not found, do not continue, as deleting without indices would delete the entire handle!! if not len(indices) > 0: - LOGGER.debug('delete_handle_value: No values for key(s) '+str(keys)) + LOGGER.debug('delete_handle_value: No values for key(s) ' + str(keys)) return None else: # delete and process response: - op = 'deleting "'+str(keys)+'"' + op = 'deleting "' + str(keys) + '"' resp = self.__send_handle_delete_request(handle, indices=indices, op=op) if hsresponses.handle_success(resp): - LOGGER.debug("delete_handle_value: Deleted handle values "+str(keys)+"of handle "+handle) + LOGGER.debug("delete_handle_value: Deleted handle values " + str(keys) + "of handle " + handle) elif hsresponses.values_not_found(resp): pass else: @@ -659,17 +668,17 @@ def delete_handle(self, handle, *other): # deleting handle values (not entire handle) by specifying more # parameters. if len(other) > 0: - message = 'You specified more than one argument. If you wanted'+\ - ' to delete just some values from a handle, please use the'+\ + message = 'You specified more than one argument. If you wanted' + \ + ' to delete just some values from a handle, please use the' + \ ' new method "delete_handle_value()".' raise TypeError(message) op = 'deleting handle' resp = self.__send_handle_delete_request(handle, op=op) if hsresponses.handle_success(resp): - LOGGER.info('Handle '+handle+' deleted.') + LOGGER.info('Handle ' + handle + ' deleted.') elif hsresponses.handle_not_found(resp): - msg = ('delete_handle: Handle '+handle+' did not exist, ' + msg = ('delete_handle: Handle ' + handle + ' did not exist, ' 'so it could not be deleted.') LOGGER.debug(msg) else: @@ -711,7 +720,7 @@ def exchange_additional_URL(self, handle, old, new): if hsresponses.handle_success(resp): pass else: - msg = 'Could not exchange URL '+str(old)+' against '+str(new) + msg = 'Could not exchange URL ' + str(old) + ' against ' + str(new) raise GenericHandleError( operation=op, handle=handle, @@ -763,7 +772,7 @@ def add_additional_URL(self, handle, *urls, **attributes): if hsresponses.handle_success(resp): pass else: - msg = 'Could not add URLs '+str(urls) + msg = 'Could not add URLs ' + str(urls) raise GenericHandleError( operation=op, handle=handle, @@ -806,8 +815,8 @@ def remove_additional_URL(self, handle, *urls): if hsresponses.handle_success(resp): pass else: - op = 'removing "'+str(urls)+'"' - msg = 'Could not remove URLs '+str(urls) + op = 'removing "' + str(urls) + '"' + msg = 'Could not remove URLs ' + str(urls) raise GenericHandleError( operation=op, handle=handle, @@ -844,7 +853,7 @@ def register_handle(self, handle, location, checksum=None, additional_URLs=None, handlerecord_json = self.retrieve_handle_record_json(handle) if handlerecord_json is not None: msg = 'Could not register handle' - LOGGER.error(msg+', as it already exists.') + LOGGER.error(msg + ', as it already exists.') raise HandleAlreadyExistsException(handle=handle, msg=msg) # Create admin entry @@ -872,7 +881,7 @@ def register_handle(self, handle, location, checksum=None, additional_URLs=None, ) list_of_entries.append(entryChecksum) if extratypes is not None: - for key, value in extratypes.iteritems(): + for key, value in extratypes.items(): entry = self.__create_entry( key, value, @@ -891,10 +900,10 @@ def register_handle(self, handle, location, checksum=None, additional_URLs=None, overwrite=overwrite, op=op ) - + resp_content = decoded_response(resp) if hsresponses.was_handle_created(resp) or hsresponses.handle_success(resp): - LOGGER.info("Handle registered: "+handle) - return json.loads(resp.content)['handle'] + LOGGER.info("Handle registered: " + handle) + return json.loads(resp_content)['handle'] elif hsresponses.is_temporary_redirect(resp): oldurl = resp.url newurl = resp.headers['location'] @@ -903,7 +912,7 @@ def register_handle(self, handle, location, checksum=None, additional_URLs=None, handle=handle, response=resp, payload=put_payload, - msg='Temporary redirect from '+oldurl+' to '+newurl+'.' + msg='Temporary redirect from ' + oldurl + ' to ' + newurl + '.' ) elif hsresponses.handle_not_found(resp): raise GenericHandleError( @@ -980,7 +989,7 @@ def generate_PID_name(self, prefix=None): randomuuid = uuid.uuid4() if prefix is not None: - return prefix+'/'+str(randomuuid) + return prefix + '/' + str(randomuuid) else: return str(randomuuid) @@ -1108,10 +1117,10 @@ def __make_another_index(self, list_of_entries, url=False, hs_admin=False): prohibited_indices = reserved_for_url | reserved_for_admin if url: - prohibited_indices = prohibited_indices-reserved_for_url + prohibited_indices = prohibited_indices - reserved_for_url start = 1 elif hs_admin: - prohibited_indices = prohibited_indices-reserved_for_admin + prohibited_indices = prohibited_indices - reserved_for_admin start = 100 # existing indices @@ -1122,7 +1131,7 @@ def __make_another_index(self, list_of_entries, url=False, hs_admin=False): # find new index: all_prohibited_indices = existing_indices | prohibited_indices - searchmax = max(start, max(all_prohibited_indices))+2 + searchmax = max(start, max(all_prohibited_indices)) + 2 for index in xrange(start, searchmax): if index not in all_prohibited_indices: return index @@ -1178,7 +1187,7 @@ def __create_admin_entry(self, handleowner, permissions, index, handle, ttl=None if handleowner is None: adminindex = '200' prefix = handle.split('/')[0] - adminhandle = '0.NA/'+prefix + adminhandle = '0.NA/' + prefix else: adminindex, adminhandle = utilhandle.remove_index_from_handle(handleowner) @@ -1239,7 +1248,7 @@ def __exchange_URL_in_13020loc(self, oldurl, newurl, list_of_entries, handle): if len(python_indices) > 0: if len(python_indices) > 1: - msg = str(len(python_indices))+' entries of type "10320/LOC".' + msg = str(len(python_indices)) + ' entries of type "10320/LOC".' raise BrokenHandleRecordException(handle=handle, msg=msg) for index in python_indices: @@ -1248,17 +1257,17 @@ def __exchange_URL_in_13020loc(self, oldurl, newurl, list_of_entries, handle): all_URL_elements = xmlroot.findall('location') for element in all_URL_elements: if element.get('href') == oldurl: - LOGGER.debug('__exchange_URL_in_13020loc: Exchanging URL '+oldurl +' from 10320/LOC.') + LOGGER.debug('__exchange_URL_in_13020loc: Exchanging URL ' + oldurl + ' from 10320/LOC.') num_exchanged += 1 element.set('href', newurl) - entry['data']['value'] = ET.tostring(xmlroot) + entry['data']['value'] = ET.tostring(xmlroot, encoding=encoding_value) list_of_entries.append(entry) if num_exchanged == 0: LOGGER.debug('__exchange_URL_in_13020loc: No URLs exchanged.') else: - message = '__exchange_URL_in_13020loc: The URL "'+oldurl+'" was exchanged '+str(num_exchanged)+\ - ' times against the new url "'+newurl+'" in 10320/LOC.' + message = '__exchange_URL_in_13020loc: The URL "' + oldurl + '" was exchanged ' + str(num_exchanged) + \ + ' times against the new url "' + newurl + '" in 10320/LOC.' message = message.replace('1 times', 'once') LOGGER.debug(message) @@ -1287,7 +1296,7 @@ def __remove_URL_from_10320LOC(self, url, list_of_entries, handle): if len(python_indices) > 0: if len(python_indices) > 1: - msg = str(len(python_indices))+' entries of type "10320/LOC".' + msg = str(len(python_indices)) + ' entries of type "10320/LOC".' raise BrokenHandleRecordException(handle=handle, msg=msg) for index in python_indices: @@ -1296,7 +1305,7 @@ def __remove_URL_from_10320LOC(self, url, list_of_entries, handle): all_URL_elements = xmlroot.findall('location') for element in all_URL_elements: if element.get('href') == url: - LOGGER.debug('__remove_URL_from_10320LOC: Removing URL '+url+'.') + LOGGER.debug('__remove_URL_from_10320LOC: Removing URL ' + url + '.') num_removed += 1 xmlroot.remove(element) remaining_URL_elements = xmlroot.findall('location') @@ -1306,15 +1315,15 @@ def __remove_URL_from_10320LOC(self, url, list_of_entries, handle): # index (instead of overwriting the entire one), be careful # to delete the ones that became empty! else: - entry['data']['value'] = ET.tostring(xmlroot) - LOGGER.debug('__remove_URL_from_10320LOC: '+str(len(remaining_URL_elements))+' URLs'+\ + entry['data']['value'] = ET.tostring(xmlroot, encoding=encoding_value) + LOGGER.debug('__remove_URL_from_10320LOC: ' + str(len(remaining_URL_elements)) + ' URLs' + \ ' left after removal operation.') list_of_entries.append(entry) if num_removed == 0: LOGGER.debug('__remove_URL_from_10320LOC: No URLs removed.') else: - message = '__remove_URL_from_10320LOC: The URL "'+url+'" was removed '\ - +str(num_removed)+' times.' + message = '__remove_URL_from_10320LOC: The URL "' + url + '" was removed '\ + + str(num_removed) + ' times.' message = message.replace('1 times', 'once') LOGGER.debug(message) @@ -1356,7 +1365,7 @@ def __add_URL_to_10320LOC(self, url, list_of_entries, handle=None, weight=None, makenew = True else: if len(indices) > 1: - msg = 'There is '+str(len(indices))+' 10320/LOC entries.' + msg = 'There is ' + str(len(indices)) + ' 10320/LOC entries.' raise BrokenHandleRecordException(handle=handle, msg=msg) ind = indices[0] entry = list_of_entries.pop(ind) @@ -1372,7 +1381,7 @@ def __add_URL_to_10320LOC(self, url, list_of_entries, handle=None, weight=None, xmlroot = ET.fromstring(entry['data']['value']) except TypeError: xmlroot = ET.fromstring(entry['data']) - LOGGER.debug("__add_URL_to_10320LOC: xmlroot is (1) "+ET.tostring(xmlroot)) + LOGGER.debug("__add_URL_to_10320LOC: xmlroot is (1) " + ET.tostring(xmlroot, encoding=encoding_value)) # Check if URL already there... location_element = None @@ -1394,18 +1403,18 @@ def __add_URL_to_10320LOC(self, url, list_of_entries, handle=None, weight=None, if location_id == existing_id: location_id += 1 location_element = ET.SubElement(xmlroot, 'location') - LOGGER.debug("__add_URL_to_10320LOC: location_element is (1) "+ET.tostring(location_element)+', now add id '+str(location_id)) + LOGGER.debug("__add_URL_to_10320LOC: location_element is (1) " + ET.tostring(location_element, encoding=encoding_value) + ', now add id ' + str(location_id)) location_element.set('id', str(location_id)) - LOGGER.debug("__add_URL_to_10320LOC: location_element is (2) "+ET.tostring(location_element)+', now add url '+str(url)) + LOGGER.debug("__add_URL_to_10320LOC: location_element is (2) " + ET.tostring(location_element, encoding=encoding_value) + ', now add url ' + str(url)) location_element.set('href', url) - LOGGER.debug("__add_URL_to_10320LOC: location_element is (3) "+ET.tostring(location_element)) + LOGGER.debug("__add_URL_to_10320LOC: location_element is (3) " + ET.tostring(location_element, encoding=encoding_value)) self.__set_or_adapt_10320LOC_attributes(location_element, weight, http_role, **kvpairs) # FIXME: If we start adapting the Handle Record by index (instead of # overwriting the entire one), be careful to add and/or overwrite! # (Re-)Add entire 10320 to entry, add entry to list of entries: - LOGGER.debug("__add_URL_to_10320LOC: xmlroot is (2) "+ET.tostring(xmlroot)) - entry['data'] = ET.tostring(xmlroot) + LOGGER.debug("__add_URL_to_10320LOC: xmlroot is (2) " + ET.tostring(xmlroot, encoding=encoding_value)) + entry['data'] = ET.tostring(xmlroot, encoding=encoding_value) list_of_entries.append(entry) def __set_or_adapt_10320LOC_attributes(self, locelement, weight=None, http_role=None, **kvpairs): @@ -1426,12 +1435,12 @@ def __set_or_adapt_10320LOC_attributes(self, locelement, weight=None, http_role= ''' if weight is not None: - LOGGER.debug('__set_or_adapt_10320LOC_attributes: weight ('+str(type(weight))+'): '+str(weight)) + LOGGER.debug('__set_or_adapt_10320LOC_attributes: weight (' + str(type(weight)) + '): ' + str(weight)) weight = float(weight) if weight < 0 or weight > 1: default = 1 - LOGGER.debug('__set_or_adapt_10320LOC_attributes: Invalid weight ('+str(weight)+\ - '), using default value ('+str(default)+') instead.') + LOGGER.debug('__set_or_adapt_10320LOC_attributes: Invalid weight (' + str(weight) + \ + '), using default value (' + str(default) + ') instead.') weight = default weight = str(weight) locelement.set('weight', weight) @@ -1439,7 +1448,7 @@ def __set_or_adapt_10320LOC_attributes(self, locelement, weight=None, http_role= if http_role is not None: locelement.set('http_role', http_role) - for key, value in kvpairs.iteritems(): + for key, value in kvpairs.items(): locelement.set(key, str(value)) def __log_request_response_to_file(self, **args): diff --git a/b2handle/handleexceptions.py b/b2handle/handleexceptions.py index 29f85e1..45c4d81 100644 --- a/b2handle/handleexceptions.py +++ b/b2handle/handleexceptions.py @@ -5,10 +5,10 @@ Author: Merret Buurman (DKRZ), 2015-2016 ''' - +from __future__ import absolute_import import json import re -from util import add_missing_optional_args_with_value_none +from .util import add_missing_optional_args_with_value_none class BrokenHandleRecordException(Exception): diff --git a/b2handle/handlesystemconnector.py b/b2handle/handlesystemconnector.py index a730d8b..cd6ca8d 100644 --- a/b2handle/handlesystemconnector.py +++ b/b2handle/handlesystemconnector.py @@ -13,7 +13,7 @@ import os import b2handle from b2handle.handleexceptions import HandleNotFoundException, GenericHandleError, HandleAuthenticationError, CredentialsFormatError - +from b2handle.compatibility_helper import decoded_response LOGGER = logging.getLogger(__name__) LOGGER.addHandler(b2handle.util.NullHandler()) REQUESTLOGGER = logging.getLogger('log_all_requests_of_testcases_to_file') @@ -462,8 +462,9 @@ def check_if_username_exists(self, username): _, handle = b2handle.utilhandle.remove_index_from_handle(username) resp = self.send_handle_get_request(handle) + resp_content = decoded_response(resp) if b2handle.hsresponses.does_handle_exist(resp): - handlerecord_json = json.loads(resp.content) + handlerecord_json = json.loads(resp_content) if not handlerecord_json['handle'] == handle: raise GenericHandleError( operation='Checking if username exists', diff --git a/b2handle/hsresponses.py b/b2handle/hsresponses.py index 8d38dc8..7917c71 100644 --- a/b2handle/hsresponses.py +++ b/b2handle/hsresponses.py @@ -9,6 +9,7 @@ ''' import json +from b2handle.compatibility_helper import decoded_response def is_redirect_from_http_to_https(response): if response.status_code == 302: @@ -24,7 +25,8 @@ def is_temporary_redirect(response): return False def handle_success(response): - if (response.status_code == 200 or response.status_code == 201) and json.loads(response.content)["responseCode"] == 1: + response_content = decoded_response(response) + if (response.status_code == 200 or response.status_code == 201) and json.loads(response_content)["responseCode"] == 1: return True return False @@ -34,35 +36,41 @@ def does_handle_exist(response): return False def is_handle_empty(response): - if response.status_code == 200 and json.loads(response.content)["responseCode"] == 200: + response_content = decoded_response(response) + if response.status_code == 200 and json.loads(response_content)["responseCode"] == 200: return True return False def was_handle_created(response): - if response.status_code == 201 and json.loads(response.content)["responseCode"] == 1: + response_content = decoded_response(response) + if response.status_code == 201 and json.loads(response_content)["responseCode"] == 1: return True return False def handle_not_found(response): - if response.status_code == 404 and json.loads(response.content)["responseCode"] == 100: + response_content = decoded_response(response) + if response.status_code == 404 and json.loads(response_content)["responseCode"] == 100: return True return False def not_authenticated(response): + response_content = decoded_response(response) try: - if response.status_code == 401 or json.loads(response.content)["responseCode"] == 402: + if response.status_code == 401 or json.loads(response_content)["responseCode"] == 402: # need to put 'OR' because the HS responseCode is not always received! return True - except ValueError as e: # If there is no JSON response. + except ValueError as e: # If there is no JSON response. pass return False def values_not_found(response): - if response.status_code == 400 and json.loads(response.content)["responseCode"] == 200: + response_content = decoded_response(response) + if response.status_code == 400 and json.loads(response_content)["responseCode"] == 200: return True return False def handle_already_exists(response): - if response.status_code == 409 & json.loads(response.content)["responseCode"] == 101: + response_content = decoded_response(response) + if response.status_code == 409 & json.loads(response_content)["responseCode"] == 101: return True return False diff --git a/b2handle/searcher.py b/b2handle/searcher.py index 46ff790..6a91107 100644 --- a/b2handle/searcher.py +++ b/b2handle/searcher.py @@ -12,6 +12,7 @@ import requests import json import b2handle +from past.builtins import xrange from b2handle.handleexceptions import ReverseLookupException LOGGER = logging.getLogger(__name__) @@ -383,7 +384,7 @@ def create_revlookup_query(self, *fulltext_searchterms, **keyvalue_searchterms): counter = 0 query = '?' - for key, value in keyvalue_searchterms.iteritems(): + for key, value in keyvalue_searchterms.items(): if only_search_for_allowed_keys and key not in allowed_search_keys: msg = 'Cannot search for key "'+key+'". Only searches '+\ diff --git a/b2handle/tests/Dockerfile-py3.5 b/b2handle/tests/Dockerfile-py3.5 new file mode 100644 index 0000000..efbc32e --- /dev/null +++ b/b2handle/tests/Dockerfile-py3.5 @@ -0,0 +1,17 @@ +# vim:set ft=dockerfile: +FROM eudat-b2handle:py3.5 + +RUN pip3 install \ + mock \ + coverage \ + nose + +VOLUME /opt/B2HANDLE/b2handle/tests + +WORKDIR /opt/B2HANDLE/b2handle/tests + +COPY docker-entrypoint.sh ./ + +ENTRYPOINT ["./docker-entrypoint.sh"] + +CMD ["coverage"] diff --git a/b2handle/tests/README.md b/b2handle/tests/README.md index c8bc49d..ec88d24 100644 --- a/b2handle/tests/README.md +++ b/b2handle/tests/README.md @@ -1,10 +1,18 @@ # B2HANDLE Testing +## Python 3 support + +As of version 1.1.0 the B2HANDLE library supports Python 3. +Only for Python 3 users, the value of PYTHONHASHSEED variable should be set to 0 before running the tests. +This can be simply performed by: + + export PYTHONHASHSEED=0 + ## Testing with plain unittest/unittest2 Simply run: - python main_test_script.py + python -m b2handle.tests.main_test_script ## Testing with nose and/or coverage @@ -24,9 +32,14 @@ The above will generate test results in the standard XUnit XML format and also p To generate test coverage reports without `nose`, run: coverage erase - coverage run --branch main_test_script.py + coverage run --branch -m b2handle.tests.main_test_script coverage xml -i +Alternatively you may run tests with nose and generate coverage reports as follows: + + python setup.py test + +To configure the nosetests command see also nosetests section in setup.cfg. ### Notes for older versions of nose and coverage @@ -41,6 +54,8 @@ To generate test coverage reports without `nose`, run: nosetests --with-coverage --cover-erase --cover-inclusive main_test_script.py + + ## Testing with Docker The [Dockerfile](Dockerfile) contains instructions for building a [Docker](https://www.docker.com/) image for running the B2HANDLE test suites. diff --git a/b2handle/tests/main_test_script.py b/b2handle/tests/main_test_script.py index 6c4b0de..381892a 100644 --- a/b2handle/tests/main_test_script.py +++ b/b2handle/tests/main_test_script.py @@ -1,3 +1,5 @@ +from __future__ import print_function +from __future__ import absolute_import import unittest import argparse import logging @@ -5,17 +7,19 @@ import b2handle import b2handle.tests.testcases as testcases + # Unit tests: -from testcases.handleclient_unit_test import EUDATHandleClientNoaccessTestCase -from testcases.handleconnector_unit_test import EUDATHandleConnectorNoaccessTestCase -from testcases.handleclient_read_patched_unit_test import EUDATHandleClientReadaccessFakedTestCase -from testcases.handleclient_2_read_patched_unit_test import EUDATHandleClientReadaccessPatchedTestCase -from testcases.handleclient_write_patched_unit_test import EUDATHandleClientWriteaccessPatchedTestCase -from testcases.handleclient_10320loc_read_patched_unit_test import EUDATHandleClientReadaccessFaked10320LOCTestCase -from testcases.clientcredentials_unit_test import PIDClientCredentialsTestCase -from testcases.handleclient_search_unit_test import EUDATHandleClientSearchNoAccessTestCase -from testcases.handleconnector_patched_unit_test import EUDATHandleConnectorAccessPatchedTestCase -from testcases.utilconfig_unit_test import UtilConfigTestCase +from .testcases.handleclient_unit_test import EUDATHandleClientNoaccessTestCase +from .testcases.handleconnector_unit_test import EUDATHandleConnectorNoaccessTestCase +from .testcases.handleclient_read_patched_unit_test import EUDATHandleClientReadaccessFakedTestCase +from .testcases.handleclient_2_read_patched_unit_test import EUDATHandleClientReadaccessPatchedTestCase +from .testcases.handleclient_write_patched_unit_test import EUDATHandleClientWriteaccessPatchedTestCase +from .testcases.handleclient_10320loc_read_patched_unit_test import EUDATHandleClientReadaccessFaked10320LOCTestCase +from .testcases.clientcredentials_unit_test import PIDClientCredentialsTestCase +from .testcases.handleclient_search_unit_test import EUDATHandleClientSearchNoAccessTestCase +from .testcases.handleconnector_patched_unit_test import EUDATHandleConnectorAccessPatchedTestCase +from .testcases.utilconfig_unit_test import UtilConfigTestCase + # Integration tests: # Imports below! @@ -25,16 +29,16 @@ if log_b2handle == True: LOGGER = logging.getLogger() LOGGER.setLevel(logging.DEBUG) - file_handler = logging.FileHandler('logs_b2handle'+time.strftime("%Y-%m-%d_%H-%M")+'.txt', mode='a+') + file_handler = logging.FileHandler('logs_b2handle' + time.strftime("%Y-%m-%d_%H-%M") + '.txt', mode='a+') file_handler.setFormatter(logging.Formatter('%(levelname)s:%(module)s:%(message)s')) LOGGER.addHandler(file_handler) if __name__ == '__main__': - desc = 'Test script for b2handle library, including unit tests and integration '+\ - 'tests. The integration tests need either read access to a handle server '+\ - 'containing some specific test handle record (read), write access to a '+\ + desc = 'Test script for b2handle library, including unit tests and integration ' + \ + 'tests. The integration tests need either read access to a handle server ' + \ + 'containing some specific test handle record (read), write access to a ' + \ ' handle server (write) or read access to a search servlet (search).' parser = argparse.ArgumentParser(description=desc) @@ -44,7 +48,7 @@ help='a test type to run (unit, read, write, and/or search)', default=["unit"], action="store") param = parser.parse_args() - print("Specified test types: "+str(param.testtype)) + print("Specified test types: " + str(param.testtype)) write_access = False search_access = False @@ -57,7 +61,7 @@ mocked_access = True if 'read' in param.testtype: read_access = True - from testcases.handleclient_read_integration_test import EUDATHandleClientReadaccessTestCase + from .testcases.handleclient_read_integration_test import EUDATHandleClientReadaccessTestCase if 'write' in param.testtype: write_access = True import logging @@ -66,21 +70,21 @@ REQUESTLOGGER.setLevel("INFO") REQUESTLOGGER.addHandler( logging.FileHandler( - 'logged_http_requests_'+time.strftime("%Y-%m-%d_%H-%M")+'.txt', mode='a+' + 'logged_http_requests_' + time.strftime("%Y-%m-%d_%H-%M") + '.txt', mode='a+' ) ) - from testcases.handleclient_write_integration_test import EUDATHandleClientWriteaccessTestCase - from testcases.handleclient_10320loc_write_integration_test import EUDATHandleClientWriteaccess10320LOCTestCase + from .testcases.handleclient_write_integration_test import EUDATHandleClientWriteaccessTestCase + from .testcases.handleclient_10320loc_write_integration_test import EUDATHandleClientWriteaccess10320LOCTestCase if 'search' in param.testtype: search_access = True - from testcases.handleclient_search_integration_test import EUDATHandleClientSearchTestCase + from .testcases.handleclient_search_integration_test import EUDATHandleClientSearchTestCase # Collection tests: verbosity = 5 descriptions = 0 - print '\nCollecting tests:' + print('\nCollecting tests:') tests_to_run = [] numtests = 0 @@ -90,31 +94,31 @@ tests_to_run.append(utilconfig_testcase) n = utilconfig_testcase.countTestCases() numtests += utilconfig_testcase.countTestCases() - print 'Number of tests for utilconfig (no access required):\t\t\t\t'+str(n) + print('Number of tests for utilconfig (no access required):\t\t\t\t' + str(n)) noaccess = unittest.TestLoader().loadTestsFromTestCase(EUDATHandleClientNoaccessTestCase) tests_to_run.append(noaccess) n = noaccess.countTestCases() numtests += n - print 'Number of tests for client (no access required):\t\t\t\t'+str(n) + print('Number of tests for client (no access required):\t\t\t\t' + str(n)) noaccess_connector = unittest.TestLoader().loadTestsFromTestCase(EUDATHandleConnectorNoaccessTestCase) tests_to_run.append(noaccess_connector) n = noaccess_connector.countTestCases() numtests += n - print 'Number of tests for handle system connector (no access required):\t\t\t\t'+str(n) + print('Number of tests for handle system connector (no access required):\t\t\t\t' + str(n)) credentials = unittest.TestLoader().loadTestsFromTestCase(PIDClientCredentialsTestCase) tests_to_run.append(credentials) n = credentials.countTestCases() numtests += n - print 'Number of tests for PIDClientCredentials:\t\t\t\t\t'+str(n) + print('Number of tests for PIDClientCredentials:\t\t\t\t\t' + str(n)) search_noaccess = unittest.TestLoader().loadTestsFromTestCase(EUDATHandleClientSearchNoAccessTestCase) tests_to_run.append(search_noaccess) n = search_noaccess.countTestCases() numtests += n - print 'Number of tests for searching (without server access):\t\t\t\t'+str(n) + print('Number of tests for searching (without server access):\t\t\t\t' + str(n)) if mocked_access: @@ -122,31 +126,31 @@ tests_to_run.append(mocked_read) n = mocked_read.countTestCases() numtests += n - print 'Number of tests for client (faked read access):\t\t\t\t\t'+str(n) + print('Number of tests for client (faked read access):\t\t\t\t\t' + str(n)) mocked_read_10320LOC = unittest.TestLoader().loadTestsFromTestCase(EUDATHandleClientReadaccessFaked10320LOCTestCase) tests_to_run.append(mocked_read_10320LOC) n = mocked_read_10320LOC.countTestCases() numtests += n - print 'Number of tests for client\'s 10320/LOC (faked read access):\t\t\t'+str(n) + print('Number of tests for client\'s 10320/LOC (faked read access):\t\t\t' + str(n)) patched_read = unittest.TestLoader().loadTestsFromTestCase(EUDATHandleClientReadaccessPatchedTestCase) tests_to_run.append(patched_read) - n=patched_read.countTestCases() + n = patched_read.countTestCases() numtests += n - print 'Number of tests for patched read access:\t\t\t\t\t'+str(n) + print('Number of tests for patched read access:\t\t\t\t\t' + str(n)) patched_write = unittest.TestLoader().loadTestsFromTestCase(EUDATHandleClientWriteaccessPatchedTestCase) tests_to_run.append(patched_write) - n=patched_write.countTestCases() + n = patched_write.countTestCases() numtests += n - print 'Number of tests for patched write access:\t\t\t\t\t'+str(n) + print('Number of tests for patched write access:\t\t\t\t\t' + str(n)) patched_conn = unittest.TestLoader().loadTestsFromTestCase(EUDATHandleConnectorAccessPatchedTestCase) tests_to_run.append(patched_conn) - n=patched_conn.countTestCases() + n = patched_conn.countTestCases() numtests += n - print 'Number of tests for patched access (connector):\t\t\t\t\t'+str(n) + print('Number of tests for patched access (connector):\t\t\t\t\t' + str(n)) if read_access: @@ -155,7 +159,7 @@ tests_to_run.append(read) n = read.countTestCases() numtests += n - print 'Number of integration tests for client (read access required):\t\t\t'+str(n) + print('Number of integration tests for client (read access required):\t\t\t' + str(n)) if write_access: @@ -163,13 +167,13 @@ tests_to_run.append(write) n = write.countTestCases() numtests += n - print 'Number of integration tests for client (write access required):\t\t\t'+str(n) + print('Number of integration tests for client (write access required):\t\t\t' + str(n)) write_10320LOC = unittest.TestLoader().loadTestsFromTestCase(EUDATHandleClientWriteaccess10320LOCTestCase) tests_to_run.append(write_10320LOC) n = write_10320LOC.countTestCases() numtests += n - print 'Number of integration tests for 10320/LOC (write access required):\t\t'+str(n) + print('Number of integration tests for 10320/LOC (write access required):\t\t' + str(n)) if search_access: @@ -177,12 +181,12 @@ tests_to_run.append(search) n = search.countTestCases() numtests += n - print 'Number of integration tests for searching (search servlet access required):\t'+str(n) + print('Number of integration tests for searching (search servlet access required):\t' + str(n)) # Run them - print 'Run '+str(numtests)+' tests.' + print('Run ' + str(numtests) + ' tests.') test_suites = unittest.TestSuite(tests_to_run) - print '\nStarting tests:' + print('\nStarting tests:') unittest.TextTestRunner(descriptions=descriptions, verbosity=verbosity).run(test_suites) # Run with: diff --git a/b2handle/tests/resources/handlerecord_for_reading.json b/b2handle/tests/resources/handlerecord_for_reading_PUBLIC.json similarity index 100% rename from b2handle/tests/resources/handlerecord_for_reading.json rename to b2handle/tests/resources/handlerecord_for_reading_PUBLIC.json diff --git a/b2handle/tests/resources/handlerecord_with_10320LOC.json b/b2handle/tests/resources/handlerecord_with_10320LOC_PUBLIC.json similarity index 100% rename from b2handle/tests/resources/handlerecord_with_10320LOC.json rename to b2handle/tests/resources/handlerecord_with_10320LOC_PUBLIC.json diff --git a/b2handle/tests/resources/handlerecord_with_empty_10320LOC.json b/b2handle/tests/resources/handlerecord_with_empty_10320LOC_PUBLIC.json similarity index 100% rename from b2handle/tests/resources/handlerecord_with_empty_10320LOC.json rename to b2handle/tests/resources/handlerecord_with_empty_10320LOC_PUBLIC.json diff --git a/b2handle/tests/resources/handlerecord_without_10320LOC.json b/b2handle/tests/resources/handlerecord_without_10320LOC_PUBLIC.json similarity index 100% rename from b2handle/tests/resources/handlerecord_without_10320LOC.json rename to b2handle/tests/resources/handlerecord_without_10320LOC_PUBLIC.json diff --git a/b2handle/tests/resources/testvalues_for_integration_tests_template.json b/b2handle/tests/resources/testvalues_for_integration_tests_template_PUBLIC.json similarity index 100% rename from b2handle/tests/resources/testvalues_for_integration_tests_template.json rename to b2handle/tests/resources/testvalues_for_integration_tests_template_PUBLIC.json diff --git a/b2handle/tests/testcases/clientcredentials_unit_test.py b/b2handle/tests/testcases/clientcredentials_unit_test.py index 15c2811..e038200 100644 --- a/b2handle/tests/testcases/clientcredentials_unit_test.py +++ b/b2handle/tests/testcases/clientcredentials_unit_test.py @@ -91,6 +91,12 @@ def test_credentials_from_json(self): path_to_json_credentials = PATH_CRED+'/credentials_correct_PUBLIC.json' inst = PIDClientCredentials.load_from_JSON(path_to_json_credentials) self.assertIsInstance(inst, PIDClientCredentials) + + def test_credentials_from_json_broken_syntax(self): + """""" + path_to_json_credentials = PATH_CRED+'/credentials_brokensyntax_PUBLIC.json' + with self.assertRaises(CredentialsFormatError): + _inst = PIDClientCredentials.load_from_JSON(path_to_json_credentials) def test_credentials_from_json_username_without_index(self): """Exception occurs if user name in json file does not have an index.""" diff --git a/b2handle/tests/testcases/handleclient_10320loc_read_patched_unit_test.py b/b2handle/tests/testcases/handleclient_10320loc_read_patched_unit_test.py index ef26037..8452876 100644 --- a/b2handle/tests/testcases/handleclient_10320loc_read_patched_unit_test.py +++ b/b2handle/tests/testcases/handleclient_10320loc_read_patched_unit_test.py @@ -13,9 +13,9 @@ # Load some data that is needed for testing PATH_RES = b2handle.util.get_neighbour_directory(__file__, 'resources') -RECORD_WITH = json.load(open(PATH_RES+'/handlerecord_with_10320LOC.json')) -RECORD_WITHOUT = json.load(open(PATH_RES+'/handlerecord_without_10320LOC.json')) -RECORD_WITH_EMPTY = json.load(open(PATH_RES+'/handlerecord_with_empty_10320LOC.json')) +RECORD_WITH = json.load(open(PATH_RES+'/handlerecord_with_10320LOC_PUBLIC.json')) +RECORD_WITHOUT = json.load(open(PATH_RES+'/handlerecord_without_10320LOC_PUBLIC.json')) +RECORD_WITH_EMPTY = json.load(open(PATH_RES+'/handlerecord_with_empty_10320LOC_PUBLIC.json')) class EUDATHandleClientReadaccessFaked10320LOCTestCase(unittest.TestCase): '''Testing methods that read the 10320/LOC entry.''' diff --git a/b2handle/tests/testcases/handleclient_2_read_patched_unit_test.py b/b2handle/tests/testcases/handleclient_2_read_patched_unit_test.py index 3a8023f..05722db 100644 --- a/b2handle/tests/testcases/handleclient_2_read_patched_unit_test.py +++ b/b2handle/tests/testcases/handleclient_2_read_patched_unit_test.py @@ -16,7 +16,7 @@ # Load some data that is needed for testing PATH_RES = b2handle.util.get_neighbour_directory(__file__, 'resources') -RECORD = open(PATH_RES+'/handlerecord_for_reading.json').read() +RECORD = open(PATH_RES+'/handlerecord_for_reading_PUBLIC.json').read() class EUDATHandleClientReadaccessPatchedTestCase(unittest.TestCase): '''Testing methods that read the 10320/loc entry.''' diff --git a/b2handle/tests/testcases/handleclient_read_patched_unit_test.py b/b2handle/tests/testcases/handleclient_read_patched_unit_test.py index 5e9dec0..59d4e63 100644 --- a/b2handle/tests/testcases/handleclient_read_patched_unit_test.py +++ b/b2handle/tests/testcases/handleclient_read_patched_unit_test.py @@ -11,13 +11,13 @@ import b2handle from b2handle.handleclient import EUDATHandleClient from b2handle.utilhandle import check_handle_syntax - +from past.builtins import long # Load some data that is needed for testing PATH_RES = b2handle.util.get_neighbour_directory(__file__, 'resources') -RECORD = json.load(open(PATH_RES+'/handlerecord_for_reading.json')) -RECORD_WITH = json.load(open(PATH_RES+'/handlerecord_with_10320LOC.json')) -RECORD_WITHOUT = json.load(open(PATH_RES+'/handlerecord_without_10320LOC.json')) -RECORD_WITH_EMPTY = json.load(open(PATH_RES+'/handlerecord_with_empty_10320LOC.json')) +RECORD = json.load(open(PATH_RES+'/handlerecord_for_reading_PUBLIC.json')) +RECORD_WITH = json.load(open(PATH_RES+'/handlerecord_with_10320LOC_PUBLIC.json')) +RECORD_WITHOUT = json.load(open(PATH_RES+'/handlerecord_without_10320LOC_PUBLIC.json')) +RECORD_WITH_EMPTY = json.load(open(PATH_RES+'/handlerecord_with_empty_10320LOC_PUBLIC.json')) class EUDATHandleClientReadaccessFakedTestCase(unittest.TestCase): '''Testing methods for retrieving values and indices.''' diff --git a/b2handle/tests/testcases/handleclient_write_patched_unit_test.py b/b2handle/tests/testcases/handleclient_write_patched_unit_test.py index 2829875..1247f0c 100644 --- a/b2handle/tests/testcases/handleclient_write_patched_unit_test.py +++ b/b2handle/tests/testcases/handleclient_write_patched_unit_test.py @@ -50,7 +50,7 @@ def tearDown(self): def get_payload_headers_from_mockresponse(self, putpatch): # For help, please see: http://www.voidspace.org.uk/python/mock/examples.html#checking-multiple-calls-with-mock - kwargs_passed_to_put = putpatch.call_args_list[len(putpatch.call_args_list)-1][1] + kwargs_passed_to_put = putpatch.call_args_list[len(putpatch.call_args_list) - 1][1] passed_payload = json.loads(kwargs_passed_to_put['data']) replace_timestamps(passed_payload) passed_headers = kwargs_passed_to_put['headers'] @@ -87,7 +87,7 @@ def test_register_handle(self, getpatch, putpatch): # Check if the PUT request was sent exactly once: self.assertEqual(putpatch.call_count, 1, - 'The method "requests.put" was not called once, but '+str(putpatch.call_count)+' times.') + 'The method "requests.put" was not called once, but ' + str(putpatch.call_count) + ' times.') # Get the payload+headers passed to "requests.put" passed_payload, _ = self.get_payload_headers_from_mockresponse(putpatch) @@ -95,7 +95,7 @@ def test_register_handle(self, getpatch, putpatch): # Compare with expected payload: expected_payload = {"values": [{"index": 100, "type": "HS_ADMIN", "data": {"value": {"index": "200", "handle": "0.NA/my", "permissions": "011111110011"}, "format": "admin"}}, {"index": 1, "type": "URL", "data": "http://foo.bar"}, {"index": 2, "type": "CHECKSUM", "data": "123456"}, {"index": 3, "type": "FOO", "data": "foo"}, {"index": 4, "type": "BAR", "data": "bar"}, {"index": 5, "type": "10320/LOC", "data": ""}]} replace_timestamps(expected_payload) - self.assertEqual(passed_payload, expected_payload, + self.assertEqual(sort_lists(passed_payload), sort_lists(expected_payload), failure_message(expected=expected_payload, passed=passed_payload, methodname='register_handle')) @mock.patch('b2handle.handlesystemconnector.HandleSystemConnector.check_if_username_exists') @@ -140,7 +140,7 @@ def test_register_handle_different_owner(self, getpatch, putpatch, username_chec # Check if the PUT request was sent exactly once: self.assertEqual(putpatch.call_count, 1, - 'The method "requests.put" was not called once, but '+str(putpatch.call_count)+' times.') + 'The method "requests.put" was not called once, but ' + str(putpatch.call_count) + ' times.') # Get the payload+headers passed to "requests.put" passed_payload, _ = self.get_payload_headers_from_mockresponse(putpatch) @@ -148,7 +148,7 @@ def test_register_handle_different_owner(self, getpatch, putpatch, username_chec # Compare with expected payload: expected_payload = {"values": [{"index": 100, "type": "HS_ADMIN", "data": {"value": {"index": 300, "handle": "handle/owner", "permissions": "011111110011"}, "format": "admin"}}, {"index": 1, "type": "URL", "data": "http://foo.bar"}, {"index": 2, "type": "CHECKSUM", "data": "123456"}, {"index": 3, "type": "FOO", "data": "foo"}, {"index": 4, "type": "BAR", "data": "bar"}, {"index": 5, "type": "10320/LOC", "data": ""}]} replace_timestamps(expected_payload) - self.assertEqual(passed_payload, expected_payload, + self.assertEqual(sort_lists(passed_payload), sort_lists(expected_payload), failure_message(expected=expected_payload, passed=passed_payload, methodname='register_handle')) @mock.patch('b2handle.handlesystemconnector.requests.Session.put') @@ -169,7 +169,7 @@ def test_register_handle_already_exists(self, getpatch, putpatch): # Check if nothing was changed (PUT should not have been called): self.assertEqual(putpatch.call_count, 0, - 'The method "requests.put" was called! ('+str(putpatch.call_count)+' times). It should NOT have been called.') + 'The method "requests.put" was called! (' + str(putpatch.call_count) + ' times). It should NOT have been called.') @mock.patch('b2handle.handlesystemconnector.requests.Session.put') @mock.patch('b2handle.handlesystemconnector.requests.Session.get') @@ -200,7 +200,7 @@ def test_register_handle_already_exists_overwrite(self, getpatch, putpatch): # Check if the PUT request was sent exactly once: self.assertEqual(putpatch.call_count, 1, - 'The method "requests.put" was not called once, but '+str(putpatch.call_count)+' times.') + 'The method "requests.put" was not called once, but ' + str(putpatch.call_count) + ' times.') # Get the payload+headers passed to "requests.put" passed_payload, passed_headers = self.get_payload_headers_from_mockresponse(putpatch) @@ -208,12 +208,12 @@ def test_register_handle_already_exists_overwrite(self, getpatch, putpatch): # Compare with expected payload: expected_payload = {"values": [{"index": 100, "type": "HS_ADMIN", "data": {"value": {"index": "200", "handle": "0.NA/my", "permissions": "011111110011"}, "format": "admin"}}, {"index": 1, "type": "URL", "data": "http://foo.bar"}, {"index": 2, "type": "CHECKSUM", "data": "123456"}, {"index": 3, "type": "FOO", "data": "foo"}, {"index": 4, "type": "BAR", "data": "bar"}, {"index": 5, "type": "10320/LOC", "data": ""}]} replace_timestamps(expected_payload) - self.assertEqual(passed_payload, expected_payload, + self.assertEqual(sort_lists(passed_payload), sort_lists(expected_payload), failure_message(expected=expected_payload, passed=passed_payload, methodname='register_handle')) # Check if requests.put received an authorization header: self.assertIn('Authorization', passed_headers, - 'Authorization header not passed: '+str(passed_headers)) + 'Authorization header not passed: ' + str(passed_headers)) # generate_and_register_handle @@ -239,7 +239,7 @@ def test_generate_and_register_handle(self, getpatch, putpatch): # Check if the PUT request was sent exactly once: self.assertEqual(putpatch.call_count, 1, - 'The method "requests.put" was not called once, but '+str(putpatch.call_count)+' times.') + 'The method "requests.put" was not called once, but ' + str(putpatch.call_count) + ' times.') # Get the payload+headers passed to "requests.put" passed_payload, _ = self.get_payload_headers_from_mockresponse(putpatch) @@ -247,7 +247,7 @@ def test_generate_and_register_handle(self, getpatch, putpatch): # Compare with expected payload: expected_payload = {"values": [{"index": 100, "type": "HS_ADMIN", "data": {"value": {"index": "200", "handle": "0.NA/my", "permissions": "011111110011"}, "format": "admin"}}, {"index": 1, "type": "URL", "data": "http://foo.bar"}, {"index": 2, "type": "CHECKSUM", "data": "123456"}]} replace_timestamps(expected_payload) - self.assertEqual(passed_payload, expected_payload, + self.assertEqual(sort_lists(passed_payload), sort_lists(expected_payload), failure_message(expected=expected_payload, passed=passed_payload, methodname='generate_and_register_handle')) # modify_handle_value @@ -258,12 +258,12 @@ def test_modify_handle_value_one(self, getpatch, putpatch): """Test modifying one existing handle value.""" # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":111,"type": "TEST1","data":{"format":"string","value":"val1"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"},{"index":2222,"type": "TEST2","data":{"format":"string","value":"val2"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"},{"index":333,"type": "TEST3","data":{"format":"string","value":"val3"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"},{"index":4,"type": "TEST4","data":{"format":"string","value":"val4"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":111, "type": "TEST1", "data":{"format":"string", "value":"val1"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}, {"index":2222, "type": "TEST2", "data":{"format":"string", "value":"val2"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}, {"index":333, "type": "TEST3", "data":{"format":"string", "value":"val3"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}, {"index":4, "type": "TEST4", "data":{"format":"string", "value":"val4"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get # Define the replacement for the patched requests.put method: - cont = {"responseCode":1,"handle":"my/testhandle"} + cont = {"responseCode":1, "handle":"my/testhandle"} mock_response_put = MockResponse(status_code=201, content=json.dumps(cont)) putpatch.return_value = mock_response_put @@ -273,7 +273,7 @@ def test_modify_handle_value_one(self, getpatch, putpatch): # Check if the PUT request was sent exactly once: self.assertEqual(putpatch.call_count, 1, - 'The method "requests.put" was not called once, but '+str(putpatch.call_count)+' times.') + 'The method "requests.put" was not called once, but ' + str(putpatch.call_count) + ' times.') # Get the payload passed to "requests.put" passed_payload, _ = self.get_payload_headers_from_mockresponse(putpatch) @@ -305,7 +305,7 @@ def test_modify_handle_value_several(self, getpatch, putpatch): }, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z" - },{ + }, { "index":2222, "type": "TEST2", "data":{ @@ -314,7 +314,7 @@ def test_modify_handle_value_several(self, getpatch, putpatch): }, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z" - },{ + }, { "index":333, "type": "TEST3", "data":{ @@ -323,7 +323,7 @@ def test_modify_handle_value_several(self, getpatch, putpatch): }, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z" - },{ + }, { "index":4, "type": "TEST4", "data":{ @@ -352,26 +352,28 @@ def test_modify_handle_value_several(self, getpatch, putpatch): # Check if the PUT request was sent exactly once: self.assertEqual(putpatch.call_count, 1, - 'The method "requests.put" was not called once, but '+str(putpatch.call_count)+' times.') + 'The method "requests.put" was not called once, but ' + str(putpatch.call_count) + ' times.') # Get the payload passed to "requests.put" passed_payload, _ = self.get_payload_headers_from_mockresponse(putpatch) - sort_lists(passed_payload) - + # Compare with expected payload: expected_payload = { "values":[ { + "index":333, + "type": "TEST3", + "data":"new3", + "ttl":86400, + + + }, { + "index":2222, "type": "TEST2", "data":"new2", "ttl":86400, - },{ - "index":333, - "type": "TEST3", - "data":"new3", - "ttl":86400, - },{ + }, { "index":4, "type": "TEST4", "data":"new4", @@ -379,8 +381,7 @@ def test_modify_handle_value_several(self, getpatch, putpatch): }] } replace_timestamps(expected_payload) - sort_lists(expected_payload) - self.assertEqual(passed_payload, expected_payload, + self.assertEqual(sort_lists(passed_payload), sort_lists(expected_payload), failure_message(expected=expected_payload, passed=passed_payload, methodname='modify_handle_value')) @@ -391,12 +392,12 @@ def test_modify_handle_value_corrupted(self, getpatch, putpatch): """Test exception when trying to modify corrupted handle record.""" # Define the replacement for the patched GET method (getting a corrupted record): - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":111,"type": "TEST1","data":{"format":"string","value":"val1"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":2222,"type": "TEST2","data":{"format":"string","value":"val2"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":333,"type": "TEST2","data":{"format":"string","value":"val3"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":4,"type": "TEST4","data":{"format":"string","value":"val4"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":111, "type": "TEST1", "data":{"format":"string", "value":"val1"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":2222, "type": "TEST2", "data":{"format":"string", "value":"val2"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":333, "type": "TEST2", "data":{"format":"string", "value":"val3"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":4, "type": "TEST4", "data":{"format":"string", "value":"val4"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get # Define the replacement for the patched requests.put method: - cont = {"responseCode":1,"handle":"my/testhandle"} + cont = {"responseCode":1, "handle":"my/testhandle"} mock_response_put = MockResponse(status_code=201, content=json.dumps(cont)) putpatch.return_value = mock_response_put @@ -409,7 +410,7 @@ def test_modify_handle_value_corrupted(self, getpatch, putpatch): # Check if PUT was called (PUT should not have been called): self.assertEqual(putpatch.call_count, 0, - 'The method "requests.put" was called! ('+str(putpatch.call_count)+' times). It should NOT have been called.') + 'The method "requests.put" was called! (' + str(putpatch.call_count) + ' times). It should NOT have been called.') @mock.patch('b2handle.handlesystemconnector.requests.Session.delete') @mock.patch('b2handle.handlesystemconnector.requests.Session.get') @@ -417,7 +418,7 @@ def test_modify_handle_value_without_authentication(self, getpatch, putpatch): """Test if exception when not authenticated.""" # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":111,"type": "TEST1","data":{"format":"string","value":"val1"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"},{"index":2222,"type": "TEST2","data":{"format":"string","value":"val2"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"},{"index":333,"type": "TEST3","data":{"format":"string","value":"val3"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"},{"index":4,"type": "TEST4","data":{"format":"string","value":"val4"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":111, "type": "TEST1", "data":{"format":"string", "value":"val1"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}, {"index":2222, "type": "TEST2", "data":{"format":"string", "value":"val2"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}, {"index":333, "type": "TEST3", "data":{"format":"string", "value":"val3"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}, {"index":4, "type": "TEST4", "data":{"format":"string", "value":"val4"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get @@ -439,7 +440,7 @@ def test_modify_handle_value_several_inexistent(self, getpatch, putpatch): """Test modifying several existing handle values, one of them inexistent.""" # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":111,"type": "TEST1","data":{"format":"string","value":"val1"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"},{"index":2222,"type": "TEST2","data":{"format":"string","value":"val2"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"},{"index":333,"type": "TEST3","data":{"format":"string","value":"val3"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"},{"index":4,"type": "TEST4","data":{"format":"string","value":"val4"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":111, "type": "TEST1", "data":{"format":"string", "value":"val1"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}, {"index":2222, "type": "TEST2", "data":{"format":"string", "value":"val2"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}, {"index":333, "type": "TEST3", "data":{"format":"string", "value":"val3"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}, {"index":4, "type": "TEST4", "data":{"format":"string", "value":"val4"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get @@ -458,15 +459,18 @@ def test_modify_handle_value_several_inexistent(self, getpatch, putpatch): # Check if the PUT request was sent exactly once: self.assertEqual(putpatch.call_count, 1, - 'The method "requests.put" was not called once, but '+str(putpatch.call_count)+' times.') + 'The method "requests.put" was not called once, but ' + str(putpatch.call_count) + ' times.') # Get the payload passed to "requests.put" passed_payload, _ = self.get_payload_headers_from_mockresponse(putpatch) - + passed_payload.get('values', {}) + # sort_lists(passed_payload) # Compare with expected payload: expected_payload = {"values": [{"index": 2, "type": "TEST100", "data": "new100"}, {"index": 2222, "ttl": 86400, "type": "TEST2", "data": "new2"}, {"index": 4, "ttl": 86400, "type": "TEST4", "data": "new4"}]} + expected_payload.get('values', {}) replace_timestamps(expected_payload) - self.assertEqual(passed_payload, expected_payload, + # sort_lists(expected_payload) + self.assertEqual(sort_lists(passed_payload), sort_lists(expected_payload), failure_message(expected=expected_payload, passed=passed_payload, methodname='modify_handle_value')) @@ -478,7 +482,7 @@ def test_modify_handle_value_several_inexistent_2(self, getpatch, putpatch): """Test modifying several existing handle values, SEVERAL of them inexistent.""" # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":111,"type": "TEST1","data":{"format":"string","value":"val1"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"},{"index":2222,"type": "TEST2","data":{"format":"string","value":"val2"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"},{"index":333,"type": "TEST3","data":{"format":"string","value":"val3"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"},{"index":4,"type": "TEST4","data":{"format":"string","value":"val4"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":111, "type": "TEST1", "data":{"format":"string", "value":"val1"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}, {"index":2222, "type": "TEST2", "data":{"format":"string", "value":"val2"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}, {"index":333, "type": "TEST3", "data":{"format":"string", "value":"val3"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}, {"index":4, "type": "TEST4", "data":{"format":"string", "value":"val4"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get @@ -498,15 +502,19 @@ def test_modify_handle_value_several_inexistent_2(self, getpatch, putpatch): # Check if the PUT request was sent exactly once: self.assertEqual(putpatch.call_count, 1, - 'The method "requests.put" was not called once, but '+str(putpatch.call_count)+' times.') + 'The method "requests.put" was not called once, but ' + str(putpatch.call_count) + ' times.') # Get the payload passed to "requests.put" passed_payload, _ = self.get_payload_headers_from_mockresponse(putpatch) - + # Compare with expected payload: expected_payload = {'values': [{'index': 2, 'type': 'TEST100', 'data': 'new100'}, {'index': 2222, 'ttl': 86400, 'type': 'TEST2', 'data': 'new2'}, {'index': 4, 'ttl': 86400, 'type': 'TEST4', 'data': 'new4'}, {'index': 3, 'type': 'TEST101', 'data': 'new101'}]} + expected_payload.get('values', {}) replace_timestamps(expected_payload) - self.assertEqual(passed_payload, expected_payload, + sort_lists(expected_payload) + + replace_timestamps(expected_payload) + self.assertEqual(sort_lists(passed_payload), sort_lists(expected_payload), failure_message(expected=expected_payload, passed=passed_payload, methodname='modify_handle_value')) @@ -517,7 +525,7 @@ def test_modify_handle_value_HS_ADMIN(self, getpatch, putpatch): """Test exception when trying to modify HS_ADMIN.""" # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":111,"type": "TEST1","data":{"format":"string","value":"val1"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"},{"index":2222,"type": "TEST2","data":{"format":"string","value":"val2"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"},{"index":333,"type": "TEST3","data":{"format":"string","value":"val3"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"},{"index":4,"type": "TEST4","data":{"format":"string","value":"val4"},"ttl":86400,"timestamp":"2015-09-29T15:51:08Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":111, "type": "TEST1", "data":{"format":"string", "value":"val1"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}, {"index":2222, "type": "TEST2", "data":{"format":"string", "value":"val2"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}, {"index":333, "type": "TEST3", "data":{"format":"string", "value":"val3"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}, {"index":4, "type": "TEST4", "data":{"format":"string", "value":"val4"}, "ttl":86400, "timestamp":"2015-09-29T15:51:08Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get @@ -541,7 +549,7 @@ def test_delete_handle_value_one_entry(self, getpatch, deletepatch): """Test deleting one entry from a record.""" # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":111,"type": "TEST1","data":{"format":"string","value":"val1"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":2222,"type": "TEST2","data":{"format":"string","value":"val2"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":333,"type": "TEST2","data":{"format":"string","value":"val3"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":4,"type": "TEST4","data":{"format":"string","value":"val4"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":111, "type": "TEST1", "data":{"format":"string", "value":"val1"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":2222, "type": "TEST2", "data":{"format":"string", "value":"val2"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":333, "type": "TEST2", "data":{"format":"string", "value":"val3"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":4, "type": "TEST4", "data":{"format":"string", "value":"val4"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get @@ -554,12 +562,12 @@ def test_delete_handle_value_one_entry(self, getpatch, deletepatch): # Get the args passed to "requests.delete" # For help, please see: http://www.voidspace.org.uk/python/mock/examples.html#checking-multiple-calls-with-mock - positional_args_passed_to_delete = deletepatch.call_args_list[len(deletepatch.call_args_list)-1][0] + positional_args_passed_to_delete = deletepatch.call_args_list[len(deletepatch.call_args_list) - 1][0] passed_url = positional_args_passed_to_delete[0] # Compare with expected URL: - self.assertIn('?index=111',passed_url, - 'The index 111 is not specified in the URL '+passed_url+'. This is serious!') + self.assertIn('?index=111', passed_url, + 'The index 111 is not specified in the URL ' + passed_url + '. This is serious!') @mock.patch('b2handle.handlesystemconnector.requests.Session.delete') @mock.patch('b2handle.handlesystemconnector.requests.Session.get') @@ -570,7 +578,7 @@ def test_delete_handle_value_several_entries(self, getpatch, deletepatch): testhandle = 'my/testhandle' # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":testhandle,"values":[{"index":111,"type": "TEST1","data":{"format":"string","value":"val1"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":2222,"type": "TEST2","data":{"format":"string","value":"val2"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":333,"type": "TEST2","data":{"format":"string","value":"val3"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":4,"type": "TEST4","data":{"format":"string","value":"val4"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"}]} + cont = {"responseCode":1, "handle":testhandle, "values":[{"index":111, "type": "TEST1", "data":{"format":"string", "value":"val1"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":2222, "type": "TEST2", "data":{"format":"string", "value":"val2"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":333, "type": "TEST2", "data":{"format":"string", "value":"val3"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":4, "type": "TEST4", "data":{"format":"string", "value":"val4"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get @@ -583,14 +591,14 @@ def test_delete_handle_value_several_entries(self, getpatch, deletepatch): # Get the args passed to "requests.delete" # For help, please see: http://www.voidspace.org.uk/python/mock/examples.html#checking-multiple-calls-with-mock - positional_args_passed_to_delete = deletepatch.call_args_list[len(deletepatch.call_args_list)-1][0] + positional_args_passed_to_delete = deletepatch.call_args_list[len(deletepatch.call_args_list) - 1][0] passed_url = positional_args_passed_to_delete[0] # Compare with expected URL: - self.assertIn('index=111',passed_url, - 'The index 111 is not specified in the URL '+passed_url+'. This may be serious!') - self.assertIn('index=222',passed_url, - 'The index 2222 is not specified in the URL '+passed_url+'. This may be serious!') + self.assertIn('index=111', passed_url, + 'The index 111 is not specified in the URL ' + passed_url + '. This may be serious!') + self.assertIn('index=222', passed_url, + 'The index 2222 is not specified in the URL ' + passed_url + '. This may be serious!') @mock.patch('b2handle.handlesystemconnector.requests.Session.delete') @mock.patch('b2handle.handlesystemconnector.requests.Session.get') @@ -601,7 +609,7 @@ def test_delete_handle_value_inexistent_entry(self, getpatch, deletepatch): testhandle = 'my/testhandle' # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":testhandle,"values":[{"index":111,"type": "TEST1","data":{"format":"string","value":"val1"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":2222,"type": "TEST2","data":{"format":"string","value":"val2"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":333,"type": "TEST2","data":{"format":"string","value":"val3"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":4,"type": "TEST4","data":{"format":"string","value":"val4"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"}]} + cont = {"responseCode":1, "handle":testhandle, "values":[{"index":111, "type": "TEST1", "data":{"format":"string", "value":"val1"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":2222, "type": "TEST2", "data":{"format":"string", "value":"val2"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":333, "type": "TEST2", "data":{"format":"string", "value":"val3"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":4, "type": "TEST4", "data":{"format":"string", "value":"val4"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get @@ -614,7 +622,7 @@ def test_delete_handle_value_inexistent_entry(self, getpatch, deletepatch): # Check if PUT was called (PUT should not have been called): self.assertEqual(deletepatch.call_count, 0, - 'The method "requests.put" was called! ('+str(deletepatch.call_count)+' times). It should NOT have been called.') + 'The method "requests.put" was called! (' + str(deletepatch.call_count) + ' times). It should NOT have been called.') @mock.patch('b2handle.handlesystemconnector.requests.Session.delete') @mock.patch('b2handle.handlesystemconnector.requests.Session.get') @@ -625,7 +633,7 @@ def test_delete_handle_value_several_entries_one_nonexistent(self, getpatch, del testhandle = 'my/testhandle' # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":testhandle,"values":[{"index":111,"type": "TEST1","data":{"format":"string","value":"val1"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":2222,"type": "TEST2","data":{"format":"string","value":"val2"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":333,"type": "TEST2","data":{"format":"string","value":"val3"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":4,"type": "TEST4","data":{"format":"string","value":"val4"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"}]} + cont = {"responseCode":1, "handle":testhandle, "values":[{"index":111, "type": "TEST1", "data":{"format":"string", "value":"val1"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":2222, "type": "TEST2", "data":{"format":"string", "value":"val2"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":333, "type": "TEST2", "data":{"format":"string", "value":"val3"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":4, "type": "TEST4", "data":{"format":"string", "value":"val4"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get @@ -638,14 +646,14 @@ def test_delete_handle_value_several_entries_one_nonexistent(self, getpatch, del # Get the args passed to "requests.delete" # For help, please see: http://www.voidspace.org.uk/python/mock/examples.html#checking-multiple-calls-with-mock - positional_args_passed_to_delete = deletepatch.call_args_list[len(deletepatch.call_args_list)-1][0] + positional_args_passed_to_delete = deletepatch.call_args_list[len(deletepatch.call_args_list) - 1][0] passed_url = positional_args_passed_to_delete[0] # Compare with expected URL: - self.assertIn('index=111',passed_url, - 'The index 111 is not specified in the URL '+passed_url+'. This may be serious!') - self.assertNotIn('&index=',passed_url, - 'A second index was specified in the URL '+passed_url+'. This may be serious!') + self.assertIn('index=111', passed_url, + 'The index 111 is not specified in the URL ' + passed_url + '. This may be serious!') + self.assertNotIn('&index=', passed_url, + 'A second index was specified in the URL ' + passed_url + '. This may be serious!') @mock.patch('b2handle.handlesystemconnector.requests.Session.delete') @mock.patch('b2handle.handlesystemconnector.requests.Session.get') @@ -653,7 +661,7 @@ def test_delete_handle_value_several_occurrences(self, getpatch, deletepatch): """Test trying to delete from a corrupted handle record.""" # Define the replacement for the patched GET method (getting a corrupted record): - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":111,"type": "TEST1","data":{"format":"string","value":"val1"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":2222,"type": "TEST2","data":{"format":"string","value":"val2"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":333,"type": "TEST2","data":{"format":"string","value":"val3"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"},{"index":4,"type": "TEST4","data":{"format":"string","value":"val4"},"ttl":86400,"timestamp":"2015-09-30T15:08:49Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":111, "type": "TEST1", "data":{"format":"string", "value":"val1"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":2222, "type": "TEST2", "data":{"format":"string", "value":"val2"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":333, "type": "TEST2", "data":{"format":"string", "value":"val3"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}, {"index":4, "type": "TEST4", "data":{"format":"string", "value":"val4"}, "ttl":86400, "timestamp":"2015-09-30T15:08:49Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get @@ -666,18 +674,18 @@ def test_delete_handle_value_several_occurrences(self, getpatch, deletepatch): # Get the args passed to "requests.delete" # For help, please see: http://www.voidspace.org.uk/python/mock/examples.html#checking-multiple-calls-with-mock - positional_args_passed_to_delete = deletepatch.call_args_list[len(deletepatch.call_args_list)-1][0] + positional_args_passed_to_delete = deletepatch.call_args_list[len(deletepatch.call_args_list) - 1][0] passed_url = positional_args_passed_to_delete[0] # Compare with expected URL: - self.assertIn('index=2222',passed_url, - 'The index 2222 is not specified in the URL '+passed_url+'. This may be serious!') - self.assertIn('index=333',passed_url, - 'The index 333 is not specified in the URL '+passed_url+'. This may be serious!') + self.assertIn('index=2222', passed_url, + 'The index 2222 is not specified in the URL ' + passed_url + '. This may be serious!') + self.assertIn('index=333', passed_url, + 'The index 333 is not specified in the URL ' + passed_url + '. This may be serious!') # Check if PUT was called once: self.assertEqual(deletepatch.call_count, 1, - 'The method "requests.put" was not called once, but '+str(deletepatch.call_count)+' times.') + 'The method "requests.put" was not called once, but ' + str(deletepatch.call_count) + ' times.') # delete_handle: @@ -693,11 +701,11 @@ def test_delete_handle(self, deletepatch): # Get the args passed to "requests.delete" # For help, please see: http://www.voidspace.org.uk/python/mock/examples.html#checking-multiple-calls-with-mock - positional_args_passed_to_delete = deletepatch.call_args_list[len(deletepatch.call_args_list)-1][0] + positional_args_passed_to_delete = deletepatch.call_args_list[len(deletepatch.call_args_list) - 1][0] passed_url = positional_args_passed_to_delete[0] # Compare with expected URL: - self.assertNotIn('index=',passed_url, + self.assertNotIn('index=', passed_url, 'Indices were passed to the delete method.') @mock.patch('b2handle.handlesystemconnector.requests.Session.delete') @@ -717,7 +725,7 @@ def test_delete_handle_inexistent(self, getpatch, deletepatch): # Check if response is ok: self.assertIsNone(resp, - 'The response code when deleting inexistent handle should be None but is: '+str(resp)) + 'The response code when deleting inexistent handle should be None but is: ' + str(resp)) def test_delete_handle_too_many_args(self): @@ -735,12 +743,12 @@ def test_remove_additional_URL(self, getpatch, putpatch): """Test normal removal of additional URL from 10320/LOC.""" # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":1,"type":"URL","data":{"format":"string","value":"www.url.foo"},"ttl":86400,"timestamp":"2015-09-30T15:54:32Z"},{"index":2,"type":"10320/LOC","data":{"format":"string","value":" "},"ttl":86400,"timestamp":"2015-09-30T15:54:32Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":1, "type":"URL", "data":{"format":"string", "value":"www.url.foo"}, "ttl":86400, "timestamp":"2015-09-30T15:54:32Z"}, {"index":2, "type":"10320/LOC", "data":{"format":"string", "value":" "}, "ttl":86400, "timestamp":"2015-09-30T15:54:32Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get # Define the replacement for the patched requests.put method: - cont = {"responseCode":1,"handle":"my/testhandle"} + cont = {"responseCode":1, "handle":"my/testhandle"} mock_response_put = MockResponse(status_code=200, content=json.dumps(cont)) putpatch.return_value = mock_response_put @@ -751,14 +759,21 @@ def test_remove_additional_URL(self, getpatch, putpatch): # Check if the PUT request was sent exactly once: self.assertEqual(putpatch.call_count, 1, - 'The method "requests.put" was not called once, but '+str(putpatch.call_count)+' times.') + 'The method "requests.put" was not called once, but ' + str(putpatch.call_count) + ' times.') # Get the payload passed to "requests.put" # For help, please see: http://www.voidspace.org.uk/python/mock/examples.html#checking-multiple-calls-with-mock - passed_payload, _ = self.get_payload_headers_from_mockresponse(putpatch) + try: + passed_payload, _ = self.get_payload_headers_from_mockresponse(putpatch) + except Exception as exc: + import pdb; pdb.set_trace() # breakpoint 6579fbc8x // # Compare with expected payload: expected_payload = {"values": [{"index": 1, "ttl": 86400, "type": "URL", "timestamp": "2015-09-30T15:54:32Z", "data": {"value": "www.url.foo", "format": "string"}}, {"index": 2, "ttl": 86400, "type": "10320/LOC", "timestamp": "2015-09-30T15:54:32Z", "data": {"value": "", "format": "string"}}]} + expected_payload.get('values', {}) + replace_timestamps(expected_payload) + sort_lists(expected_payload) + replace_timestamps(expected_payload) self.assertEqual(passed_payload, expected_payload, failure_message(expected=expected_payload, @@ -771,12 +786,12 @@ def test_remove_additional_URL_toempty(self, getpatch, putpatch): """Test removing all URL, which should remove the whole 10320/LOC attribute.""" # Define the replacement for the patched GET method (a record with one additional URL in it): - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":1,"type":"URL","data":{"format":"string","value":"www.url.foo"},"ttl":86400,"timestamp":"2015-09-30T15:54:33Z"},{"index":2,"type":"10320/LOC","data":{"format":"string","value":""},"ttl":86400,"timestamp":"2015-09-30T15:54:33Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":1, "type":"URL", "data":{"format":"string", "value":"www.url.foo"}, "ttl":86400, "timestamp":"2015-09-30T15:54:33Z"}, {"index":2, "type":"10320/LOC", "data":{"format":"string", "value":""}, "ttl":86400, "timestamp":"2015-09-30T15:54:33Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get # Define the replacement for the patched requests.put method: - cont = {"responseCode":1,"handle":"my/testhandle"} + cont = {"responseCode":1, "handle":"my/testhandle"} mock_response_put = MockResponse(status_code=200, content=json.dumps(cont)) putpatch.return_value = mock_response_put @@ -787,13 +802,17 @@ def test_remove_additional_URL_toempty(self, getpatch, putpatch): # Check if the PUT request was sent exactly once: self.assertEqual(putpatch.call_count, 1, - 'The method "requests.put" was not called once, but '+str(putpatch.call_count)+' times.') + 'The method "requests.put" was not called once, but ' + str(putpatch.call_count) + ' times.') # Get the payload passed to "requests.put" passed_payload, _ = self.get_payload_headers_from_mockresponse(putpatch) # Compare with expected payload: expected_payload = {"values": [{"index": 1, "ttl": 86400, "type": "URL", "timestamp": "2015-09-30T15:54:33Z", "data": {"value": "www.url.foo", "format": "string"}}]} + expected_payload.get('values', {}) + replace_timestamps(expected_payload) + sort_lists(expected_payload) + replace_timestamps(expected_payload) self.assertEqual(passed_payload, expected_payload, failure_message(expected=expected_payload, @@ -807,16 +826,16 @@ def test_remove_additional_URL_several(self, getpatch, putpatch): # Test variables testhandle = 'my/testhandle' - url1 = 'http://first.foo' + url1 = 'http://first.foo' url2 = 'http://second.foo' # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":testhandle,"values":[{"index":1,"type":"URL","data":{"format":"string","value":"www.url.foo"},"ttl":86400,"timestamp":"2015-09-30T15:54:32Z"},{"index":2,"type":"10320/LOC","data":{"format":"string","value":" "},"ttl":86400,"timestamp":"2015-09-30T15:54:32Z"}]} + cont = {"responseCode":1, "handle":testhandle, "values":[{"index":1, "type":"URL", "data":{"format":"string", "value":"www.url.foo"}, "ttl":86400, "timestamp":"2015-09-30T15:54:32Z"}, {"index":2, "type":"10320/LOC", "data":{"format":"string", "value":" "}, "ttl":86400, "timestamp":"2015-09-30T15:54:32Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get # Define the replacement for the patched requests.put method: - cont = {"responseCode":1,"handle":testhandle} + cont = {"responseCode":1, "handle":testhandle} mock_response_put = MockResponse(status_code=200, content=json.dumps(cont)) putpatch.return_value = mock_response_put @@ -825,7 +844,7 @@ def test_remove_additional_URL_several(self, getpatch, putpatch): # Check if the PUT request was sent exactly once: self.assertEqual(putpatch.call_count, 1, - 'The method "requests.put" was not called once, but '+str(putpatch.call_count)+' times.') + 'The method "requests.put" was not called once, but ' + str(putpatch.call_count) + ' times.') # Get the payload passed to "requests.put" # For help, please see: http://www.voidspace.org.uk/python/mock/examples.html#checking-multiple-calls-with-mock @@ -870,12 +889,12 @@ def test_exchange_additional_URL_normal(self, getpatch, putpatch): """Test replacing an URL.""" # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":1,"type":"URL","data":{"format":"string","value":"www.url.foo"},"ttl":86400,"timestamp":"2015-09-30T15:54:32Z"},{"index":2,"type":"10320/LOC","data":{"format":"string","value":" "},"ttl":86400,"timestamp":"2015-09-30T15:54:32Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":1, "type":"URL", "data":{"format":"string", "value":"www.url.foo"}, "ttl":86400, "timestamp":"2015-09-30T15:54:32Z"}, {"index":2, "type":"10320/LOC", "data":{"format":"string", "value":" "}, "ttl":86400, "timestamp":"2015-09-30T15:54:32Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get # Define the replacement for the patched requests.put method: - cont = {"responseCode":1,"handle":"my/testhandle"} + cont = {"responseCode":1, "handle":"my/testhandle"} mock_response_put = MockResponse(status_code=200, content=json.dumps(cont)) putpatch.return_value = mock_response_put @@ -888,7 +907,7 @@ def test_exchange_additional_URL_normal(self, getpatch, putpatch): # Check if the PUT request was sent exactly once: self.assertEqual(putpatch.call_count, 1, - 'The method "requests.put" was not called once, but '+str(putpatch.call_count)+' times.') + 'The method "requests.put" was not called once, but ' + str(putpatch.call_count) + ' times.') # Get the payload passed to "requests.put" passed_payload, _ = self.get_payload_headers_from_mockresponse(putpatch) @@ -907,12 +926,12 @@ def test_exchange_additional_URL_doesnotexist(self, getpatch, putpatch): """Test if replacing an inexistent URL has any effect.""" # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":1,"type":"URL","data":{"format":"string","value":"www.url.foo"},"ttl":86400,"timestamp":"2015-09-30T15:54:32Z"},{"index":2,"type":"10320/LOC","data":{"format":"string","value":" "},"ttl":86400,"timestamp":"2015-09-30T15:54:32Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":1, "type":"URL", "data":{"format":"string", "value":"www.url.foo"}, "ttl":86400, "timestamp":"2015-09-30T15:54:32Z"}, {"index":2, "type":"10320/LOC", "data":{"format":"string", "value":" "}, "ttl":86400, "timestamp":"2015-09-30T15:54:32Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get # Define the replacement for the patched requests.put method: - cont = {"responseCode":1,"handle":"my/testhandle"} + cont = {"responseCode":1, "handle":"my/testhandle"} mock_response_put = MockResponse(status_code=200, content=json.dumps(cont)) putpatch.return_value = mock_response_put @@ -925,7 +944,7 @@ def test_exchange_additional_URL_doesnotexist(self, getpatch, putpatch): # Check if the PUT request was sent: self.assertEqual(putpatch.call_count, 0, - 'The method "requests.put" was called '+str(putpatch.call_count)+' times - it should not be called at all.') + 'The method "requests.put" was called ' + str(putpatch.call_count) + ' times - it should not be called at all.') @mock.patch('b2handle.handlesystemconnector.requests.Session.put') @mock.patch('b2handle.handlesystemconnector.requests.Session.get') @@ -933,12 +952,12 @@ def test_exchange_additional_URL_no10320loc(self, getpatch, putpatch): """Test if replacing an URL has any effect if there is no 10320/LOC.""" # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":1,"type":"URL","data":{"format":"string","value":"www.url.foo"},"ttl":86400,"timestamp":"2015-09-30T15:54:32Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":1, "type":"URL", "data":{"format":"string", "value":"www.url.foo"}, "ttl":86400, "timestamp":"2015-09-30T15:54:32Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get # Define the replacement for the patched requests.put method: - cont = {"responseCode":1,"handle":"my/testhandle"} + cont = {"responseCode":1, "handle":"my/testhandle"} mock_response_put = MockResponse(status_code=200, content=json.dumps(cont)) putpatch.return_value = mock_response_put @@ -951,7 +970,7 @@ def test_exchange_additional_URL_no10320loc(self, getpatch, putpatch): # Check if the PUT request was sent: self.assertEqual(putpatch.call_count, 0, - 'The method "requests.put" was called '+str(putpatch.call_count)+' times - it should not be called at all.') + 'The method "requests.put" was called ' + str(putpatch.call_count) + ' times - it should not be called at all.') # add_additional_URL @@ -961,12 +980,12 @@ def test_add_additional_URL_first(self, getpatch, putpatch): """Test adding the first additional URL'(created the 10320/LOC entry).""" # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":1,"type":"URL","data":{"format":"string","value":"www.url.foo"},"ttl":86400,"timestamp":"2015-09-30T15:54:32Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":1, "type":"URL", "data":{"format":"string", "value":"www.url.foo"}, "ttl":86400, "timestamp":"2015-09-30T15:54:32Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get # Define the replacement for the patched requests.put method: - cont = {"responseCode":1,"handle":"my/testhandle"} + cont = {"responseCode":1, "handle":"my/testhandle"} mock_response_put = MockResponse(status_code=200, content=json.dumps(cont)) putpatch.return_value = mock_response_put @@ -976,7 +995,7 @@ def test_add_additional_URL_first(self, getpatch, putpatch): # Check if the PUT request was sent exactly once: self.assertEqual(putpatch.call_count, 1, - 'The method "requests.put" was not called once, but '+str(putpatch.call_count)+' times.') + 'The method "requests.put" was not called once, but ' + str(putpatch.call_count) + ' times.') # Get the payload+headers passed to "requests.put" passed_payload, _ = self.get_payload_headers_from_mockresponse(putpatch) @@ -993,12 +1012,12 @@ def test_add_additional_URL_another(self, getpatch, putpatch): """Test adding an additional URL.""" # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":1,"type":"URL","data":{"format":"string","value":"www.url.foo"},"ttl":86400,"timestamp":"2015-09-30T15:54:30Z"},{"index":2,"type":"10320/LOC","data":{"format":"string","value":" "},"ttl":86400,"timestamp":"2015-09-30T15:54:30Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":1, "type":"URL", "data":{"format":"string", "value":"www.url.foo"}, "ttl":86400, "timestamp":"2015-09-30T15:54:30Z"}, {"index":2, "type":"10320/LOC", "data":{"format":"string", "value":" "}, "ttl":86400, "timestamp":"2015-09-30T15:54:30Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get # Define the replacement for the patched requests.put method: - cont = {"responseCode":1,"handle":"my/testhandle"} + cont = {"responseCode":1, "handle":"my/testhandle"} mock_response_put = MockResponse(status_code=200, content=json.dumps(cont)) putpatch.return_value = mock_response_put @@ -1022,7 +1041,7 @@ def test_add_additional_URL_several(self, getpatch, putpatch): # Define the replacement for the patched GET method: cont = { - "responseCode":1,"handle":"my/testhandle", + "responseCode":1, "handle":"my/testhandle", "values":[ { "index":1, @@ -1030,15 +1049,15 @@ def test_add_additional_URL_several(self, getpatch, putpatch): "data":{ "format":"string", "value":"www.url.foo" - },"ttl":86400, + }, "ttl":86400, "timestamp":"2015-09-30T15:54:31Z" - },{ + }, { "index":2, "type":"10320/LOC", "data":{ "format":"string", "value":" " - }, "ttl":86400,"timestamp":"2015-09-30T15:54:31Z" + }, "ttl":86400, "timestamp":"2015-09-30T15:54:31Z" } ] } @@ -1046,7 +1065,7 @@ def test_add_additional_URL_several(self, getpatch, putpatch): getpatch.return_value = mock_response_get # Define the replacement for the patched requests.put method: - cont = {"responseCode":1,"handle":"my/testhandle"} + cont = {"responseCode":1, "handle":"my/testhandle"} mock_response_put = MockResponse(status_code=200, content=json.dumps(cont)) putpatch.return_value = mock_response_put @@ -1085,7 +1104,7 @@ def test_add_additional_URL_to_inexistent_handle(self, getpatch, putpatch): # Check if the PUT request was sent: self.assertEqual(putpatch.call_count, 0, - 'The method "requests.put" was called '+str(putpatch.call_count)+' times - it should not be called at all.') + 'The method "requests.put" was called ' + str(putpatch.call_count) + ' times - it should not be called at all.') @mock.patch('b2handle.handlesystemconnector.requests.Session.put') @mock.patch('b2handle.handlesystemconnector.requests.Session.get') @@ -1093,12 +1112,12 @@ def test_add_additional_URL_alreadythere(self, getpatch, putpatch): """Test adding an URL that is already there.""" # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":"my/testhandle","values":[{"index":1,"type":"URL","data":{"format":"string","value":"www.url.foo"},"ttl":86400,"timestamp":"2015-09-30T15:54:30Z"},{"index":2,"type":"10320/LOC","data":{"format":"string","value":" "},"ttl":86400,"timestamp":"2015-09-30T15:54:30Z"}]} + cont = {"responseCode":1, "handle":"my/testhandle", "values":[{"index":1, "type":"URL", "data":{"format":"string", "value":"www.url.foo"}, "ttl":86400, "timestamp":"2015-09-30T15:54:30Z"}, {"index":2, "type":"10320/LOC", "data":{"format":"string", "value":" "}, "ttl":86400, "timestamp":"2015-09-30T15:54:30Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get # Define the replacement for the patched requests.put method: - cont = {"responseCode":1,"handle":"my/testhandle"} + cont = {"responseCode":1, "handle":"my/testhandle"} mock_response_put = MockResponse(status_code=200, content=json.dumps(cont)) putpatch.return_value = mock_response_put @@ -1108,7 +1127,7 @@ def test_add_additional_URL_alreadythere(self, getpatch, putpatch): # Check if the PUT request was sent exactly once: self.assertEqual(putpatch.call_count, 0, - 'The method "requests.put" was called '+str(putpatch.call_count)+' times (should be 0).') + 'The method "requests.put" was called ' + str(putpatch.call_count) + ' times (should be 0).') @mock.patch('b2handle.handlesystemconnector.requests.Session.put') @mock.patch('b2handle.handlesystemconnector.requests.Session.get') @@ -1121,7 +1140,7 @@ def test_GenericHandleError(self, getpatch, putpatch): """ # Define the replacement for the patched GET method: - cont = {"responseCode":1,"handle":"not/me","values":[{"index":1,"type":"URL","data":{"format":"string","value":"www.url.foo"},"ttl":86400,"timestamp":"2015-09-30T15:54:30Z"},{"index":2,"type":"10320/LOC","data":{"format":"string","value":" "},"ttl":86400,"timestamp":"2015-09-30T15:54:30Z"}]} + cont = {"responseCode":1, "handle":"not/me", "values":[{"index":1, "type":"URL", "data":{"format":"string", "value":"www.url.foo"}, "ttl":86400, "timestamp":"2015-09-30T15:54:30Z"}, {"index":2, "type":"10320/LOC", "data":{"format":"string", "value":" "}, "ttl":86400, "timestamp":"2015-09-30T15:54:30Z"}]} mock_response_get = MockResponse(status_code=200, content=json.dumps(cont)) getpatch.return_value = mock_response_get @@ -1135,7 +1154,7 @@ def test_GenericHandleError(self, getpatch, putpatch): # Check if the PUT request was sent exactly once: self.assertEqual(putpatch.call_count, 0, - 'The method "requests.put" was called '+str(putpatch.call_count)+' times. It should not have been called at all.') + 'The method "requests.put" was called ' + str(putpatch.call_count) + ' times. It should not have been called at all.') @mock.patch('b2handle.handlesystemconnector.requests.Session.put') @@ -1170,7 +1189,7 @@ def test_add_additional_URL_several_toempty(self, getpatch, putpatch): getpatch.return_value = mock_response_get # Define the replacement for the patched requests.put method: - cont = {"responseCode":1,"handle":testhandle} + cont = {"responseCode":1, "handle":testhandle} mock_response_put = MockResponse(status_code=200, content=json.dumps(cont)) putpatch.return_value = mock_response_put @@ -1250,7 +1269,7 @@ def test_search_handle(self, getpatch): val = self.inst.search_handle(URL='*') # Check desired outcome: - self.assertEqual(type(val),type([]), + self.assertEqual(type(val), type([]), '') self.assertTrue(len(val) > 0, '') @@ -1269,9 +1288,9 @@ def test_search_handle_emptylist(self, getpatch): val = self.inst.search_handle(URL='noturldoesnotexist') # Check desired outcome: - self.assertEqual(type(val),type([]), + self.assertEqual(type(val), type([]), '') - self.assertEqual(len(val),0, + self.assertEqual(len(val), 0, '') @mock.patch('b2handle.handlesystemconnector.requests.Session.get') @@ -1286,14 +1305,14 @@ def test_search_handle_for_url(self, getpatch): val = self.inst.search_handle(URL='*dkrz*') # Check desired outcome: - self.assertEqual(type(val),type([]), + self.assertEqual(type(val), type([]), '') # Run code to be tested: val = self.inst.search_handle('*dkrz*') # Check desired outcome: - self.assertEqual(type(val),type([]), + self.assertEqual(type(val), type([]), '') if False: @@ -1310,14 +1329,14 @@ def test_search_handle_for_url_and_checksum(self, getpatch): val = self.inst.search_handle('*dkrz*', CHECKSUM='*123*') # Check desired outcome: - self.assertEqual(type(val),type([]), + self.assertEqual(type(val), type([]), '') # Run code to be tested: val = self.inst.search_handle(URL='*dkrz*', CHECKSUM='*123*') # Check desired outcome: - self.assertEqual(type(val),type([]), + self.assertEqual(type(val), type([]), '') @mock.patch('b2handle.handlesystemconnector.requests.Session.get') @@ -1334,7 +1353,7 @@ def test_search_handle_prefixfilter(self, getpatch): val = self.inst.search_handle(URL='*dkrz*', prefix=prefix) # Check desired outcome: - self.assertEqual(type(val),type([]), + self.assertEqual(type(val), type([]), '') for item in val: self.assertEqual(item.split('/')[0], prefix) @@ -1353,7 +1372,7 @@ def test_search_handle_prefixfilter_realprefix(self, getpatch): val = self.inst.search_handle(URL='*dkrz*', prefix=prefix) # Check desired outcome: - self.assertEqual(type(val),type([]), + self.assertEqual(type(val), type([]), '') for item in val: self.assertEqual(item.split('/')[0], prefix) @@ -1370,5 +1389,5 @@ def test_search_handle_fulltext(self, getpatch): # Run code to be tested + check exception: with self.assertRaises(ReverseLookupException): - self.inst.search_handle(URL='*dkrz*', searchterms=['foo','bar']) + self.inst.search_handle(URL='*dkrz*', searchterms=['foo', 'bar']) diff --git a/b2handle/tests/testcases/handleconnector_patched_unit_test.py b/b2handle/tests/testcases/handleconnector_patched_unit_test.py index 104fd87..3054568 100644 --- a/b2handle/tests/testcases/handleconnector_patched_unit_test.py +++ b/b2handle/tests/testcases/handleconnector_patched_unit_test.py @@ -19,7 +19,7 @@ PATH_CRED = b2handle.util.get_neighbour_directory(__file__, 'testcredentials') CRED_FILE = PATH_CRED+'/fake_certs_and_keys/fake_certi_and_bothkeys.pem' PATH_RES = b2handle.util.get_neighbour_directory(__file__, 'resources') -RECORD = open(PATH_RES+'/handlerecord_for_reading.json').read() +RECORD = open(PATH_RES+'/handlerecord_for_reading_PUBLIC.json').read() class EUDATHandleConnectorAccessPatchedTestCase(unittest.TestCase): diff --git a/b2handle/tests/testcredentials/credentials_brokensyntax_PUBLIC.json b/b2handle/tests/testcredentials/credentials_brokensyntax_PUBLIC.json new file mode 100644 index 0000000..ce93bab --- /dev/null +++ b/b2handle/tests/testcredentials/credentials_brokensyntax_PUBLIC.json @@ -0,0 +1,7 @@ +{ + "handle_server_url": "https://foo.bar", + "username": "999:foo/bar", + "password": "foobar", + "prefix": "myprefix" + "handleowner": "999:handle/owner" +} \ No newline at end of file diff --git a/b2handle/tests/utilities.py b/b2handle/tests/utilities.py index c8f6ab5..2c84170 100644 --- a/b2handle/tests/utilities.py +++ b/b2handle/tests/utilities.py @@ -1,6 +1,9 @@ import logging import os - +import collections +import operator +from functools import cmp_to_key +from future.utils import viewitems class NullHandler(logging.Handler): """ For backward-compatibility with Python 2.6, a local class definition @@ -14,7 +17,7 @@ def emit(self, record): REQUESTLOGGER.addHandler(NullHandler()) def failure_message(expected, passed, methodname): - msg = 'The PUT request payload that the method "'+methodname+ '" assembled differs from the expected. This does not necessarily mean that it is wrong, it might just be a different way to talking to the Handle Server. Please run an integration test to check this and update the exptected PUT request accordingly.\nCreated: '+str(passed)+'\nExpected: '+str(expected) + msg = 'The PUT request payload that the method "' + methodname + '" assembled differs from the expected. This does not necessarily mean that it is wrong, it might just be a different way to talking to the Handle Server. Please run an integration test to check this and update the exptected PUT request accordingly.\nCreated: ' + str(passed) + '\nExpected: ' + str(expected) return msg def replace_timestamps(jsonobject): @@ -27,28 +30,28 @@ def replace_timestamps(jsonobject): # Recursion: if type(jsonobject) == type({'b':2}): - for item in jsonobject.iteritems(): + for item in jsonobject.items(): replace_timestamps(item) - elif type(jsonobject) == type([2,2]) or type(jsonobject) == type((2,2)): + elif type(jsonobject) == type([2, 2]) or type(jsonobject) == type((2, 2)): for item in jsonobject: replace_timestamps(item) def log_new_case(name): - REQUESTLOGGER.info('\n'+60*'*'+'\n*** '+name+'\n'+60*'*'+'\n') + REQUESTLOGGER.info('\n' + 60 * '*' + '\n*** ' + name + '\n' + 60 * '*' + '\n') def log_request_response_to_file(op, handle, url, head, veri, resp, payload=None): space = '\n ' message = '' - message += op+' '+handle - message += space+'URL: '+url - message += space+'HEADERS: '+str(head) - message += space+'VERIFY: '+str(veri) + message += op + ' ' + handle + message += space + 'URL: ' + url + message += space + 'HEADERS: ' + str(head) + message += space + 'VERIFY: ' + str(veri) if payload is not None: - message += space+'PAYLOAD:'+space+str(payload) - message += space+'RESPONSECODE: '+str(resp.status_code) - message += space+'RESPONSE:'+space+str(resp.content) + message += space + 'PAYLOAD:' + space + str(payload) + message += space + 'RESPONSECODE: ' + str(resp.status_code) + message += space + 'RESPONSE:' + space + str(resp.content) REQUESTLOGGER.info(message) def log_start_test_code(): @@ -61,13 +64,13 @@ def sort_lists(jsonobject): # Sort: if type(jsonobject) == type([]): - jsonobject.sort() - + sorted(jsonobject, key=lambda x:sorted(x.keys())) + # Recursion: if type(jsonobject) == type({'b':2}): - for item in jsonobject.iteritems(): + for item in jsonobject.items(): sort_lists(item) - elif type(jsonobject) == type([2,2]) or type(jsonobject) == type((2,2)): + elif type(jsonobject) == type([2, 2]) or type(jsonobject) == type((2, 2)): for item in jsonobject: sort_lists(item) diff --git a/b2handle/util/allutils.py b/b2handle/util/allutils.py index 324e18a..0e92352 100644 --- a/b2handle/util/allutils.py +++ b/b2handle/util/allutils.py @@ -1,7 +1,8 @@ +from __future__ import absolute_import -from utilconfig import * -from pathutils import * -from logutils import * -from argsutils import * \ No newline at end of file +from .utilconfig import * +from .pathutils import * +from .logutils import * +from .argsutils import * diff --git a/b2handle/util/argsutils.py b/b2handle/util/argsutils.py index 0fad451..cbb3355 100644 --- a/b2handle/util/argsutils.py +++ b/b2handle/util/argsutils.py @@ -1,4 +1,5 @@ +from past.builtins import xrange def add_missing_optional_args_with_value_none(args, optional_args): ''' Adds key-value pairs to the passed dictionary, so that @@ -47,7 +48,7 @@ def check_presence_of_mandatory_args(args, mandatory_args): def return_keys_of_value_none(dictionary): isnone = [] - for key,value in dictionary.iteritems(): + for key,value in dictionary.items(): if value is None: isnone.append(key) return isnone diff --git a/b2handle/utilhandle.py b/b2handle/utilhandle.py index 551135d..78fcc7c 100644 --- a/b2handle/utilhandle.py +++ b/b2handle/utilhandle.py @@ -3,11 +3,13 @@ that are needed across various modules of the b2handle library. ''' - +from __future__ import absolute_import import base64 -import urllib -import handleexceptions -import util +from future.standard_library import install_aliases +install_aliases() +from urllib.parse import quote +from . import handleexceptions +from . import util def remove_index_from_handle(handle_with_index): ''' @@ -108,11 +110,11 @@ def create_authentication_string(username, password): username_utf8 = username.encode('utf-8') userpw_utf8 = password.encode('utf-8') - username_perc = urllib.quote(username_utf8) - userpw_perc = urllib.quote(userpw_utf8) + username_perc = quote(username_utf8) + userpw_perc = quote(userpw_utf8) authinfostring = username_perc + ':' + userpw_perc - authinfostring_base64 = base64.b64encode(authinfostring) + authinfostring_base64 = base64.b64encode(authinfostring.encode('utf-8')).decode('utf-8') return authinfostring_base64 def make_request_log_message(**args): diff --git a/docs/source/creatingclientcertificates.rst b/docs/source/creatingclientcertificates.rst index 4b803ed..819471b 100644 --- a/docs/source/creatingclientcertificates.rst +++ b/docs/source/creatingclientcertificates.rst @@ -80,27 +80,32 @@ Transforming the binary private key (.bin) to a .pem file Creating the certificate file ============================= - * This can be done using openssl without specifying a subject: +This can be done in 2 ways: + +Case 1: Using openssl with specifying a subject. .. code:: json - - openssl req -pubkey -x509 -new -key /.../301_foo_bar_privkey.pem - -out /.../301_certificate_and_publickey.pem -sha256 + openssl req -pubkey -x509 -new -sha256 -subj "/CN=301:foo\/bar" -days 3652 + -key /.../301_foo_bar_privkey.pem + -out /.../301_certificate_and_publickey.pem + +Done! - * This can be done using openssl with specifying a subject: +Case 2: Using openssl without specifying a subject: .. code:: json + + openssl req -pubkey -x509 -new -key /.../301_foo_bar_privkey.pem -days 3652 + -out /.../301_certificate_and_publickey.pem -sha256 + - openssl req -pubkey -x509 -new -sha256 -subj "/CN=301:foo\/bar" - -key /.../301_foo_bar_privkey.pem - -out /.../301_certificate_and_publickey.pem - * The tool is then going to prompt for some information if you don not specify a subject. For the first 5 prompts, it does not matter what you enter- the entries are going to be ignored by the Handle Server. - However, it is very important to enter the username as Common Name and *leave the Email address blank*, as it is going to be appended to the username otherwise. This will look like - this: +The tool is then going to prompt for some information if you do not specify a subject. For the first 5 prompts, it does not matter what you enter- the entries are going to be ignored by the Handle Server. + +However, it is very important to enter the username as Common Name and *leave the Email address blank*, as it is going to be appended to the username otherwise. This will look like this: .. code-block:: none :emphasize-lines: 13,14 @@ -120,6 +125,7 @@ Creating the certificate file Common Name (eg, your name or your server's hostname) []:300:foo/bar Email Address []: +Done! .. _step5: diff --git a/setup.cfg b/setup.cfg index 99cda8c..4cf225b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,5 @@ -#universal = 1 # commented out, as the library is not tested on python 3.* +[bdist_wheel] +universal=1 [nosetests] verbosity=3 diff --git a/setup.py b/setup.py index 775d367..1a74e4a 100644 --- a/setup.py +++ b/setup.py @@ -77,6 +77,8 @@ def find_version(*file_paths): 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', 'License :: OSI Approved :: Apache Software License', 'Intended Audience :: Developers', 'Topic :: Software Development :: Libraries :: Python Modules', @@ -92,7 +94,10 @@ def find_version(*file_paths): install_requires=[ 'requests', 'datetime', + 'future', ], tests_require=test_dependencies, + python_requires='>=2.6,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,<3.6', cmdclass={'test': NoseTestCommand}, + include_package_data=True )