def get_modification_acc(mod_elem): mass_delta = structures.CaseInsensitiveDict( mod_elem.attrib).get("monoisotopicMassDelta") for subelem in list(mod_elem): attribs = structures.CaseInsensitiveDict(subelem.attrib) if attribs.get("cvRef") == "UNIMOD" or attribs.get("cvRef") == "MOD": return attribs.get("accession") else: return get_modification_acc_from_mass_delta(mass_delta) raise Exception("Fail to get modification accession from %s, %s" % (str(mod_elem.tag), str(mod_elem.attrib)))
def response(status_code=200, content='', headers=None, reason=None, elapsed=0, request=None): res = requests.Response() res.status_code = status_code if isinstance(content, (dict, list)): if sys.version_info[0] == 3: content = bytes(json.dumps(content), 'utf-8') else: content = json.dumps(content) res._content = content res._content_consumed = content res.headers = structures.CaseInsensitiveDict(headers or {}) res.reason = reason res.elapsed = datetime.timedelta(elapsed) res.request = request if hasattr(request, 'url'): res.url = request.url if isinstance(request.url, bytes): res.url = request.url.decode('utf-8') if 'set-cookie' in res.headers: res.cookies.extract_cookies(cookies.MockResponse(Headers(res)), cookies.MockRequest(request)) # normally this closes the underlying connection, # but we have nothing to free. res.close = lambda *args, **kwargs: None return res
def _mk_response(self, status, content=None): reasons = {200: 'OK', 204: 'No Content', 401: 'Unauthorized'} # Create a Response object, that will serve as a mock return value my_response = req_mod.Response() my_response.status_code = status my_response.reason = reasons[status] clen = '0' if status == 200 and content: clen = str(len(content)) dict_headers = { 'content-length': clen, 'x-powered-by': 'Servlet/3.0', 'set-cookie': 'JSESSIONID=0000a41BnJsGTNQvBGERA' + '3wR1nj:759878cb-4f9a-4b05-a09a-3357abfea3b4; ' + 'Path=/; Secure; HttpOnly, CCFWSESSION=E4C0FFBE9' + '130431DBF1864171ECC6A6E; Path=/; Secure; HttpOnly', 'expires': 'Thu, 01 Dec 1994 16:00:00 GMT', 'x-transaction-id': 'XT10000073', 'cache-control': 'no-cache="set-cookie, ' + 'set-cookie2"', 'date': 'Wed, 23 Jul 2014 21:51:10 GMT', 'content-type': 'application/vnd.ibm.powervm' } my_response.headers = req_struct.CaseInsensitiveDict(dict_headers) my_response._content = content return my_response
def add_request(self, method, relative_url, headers, body, source_ip, module_name=None, version=None, instance_id=None): requests_headers = structures.CaseInsensitiveDict() for k, v in headers: requests_headers[six.ensure_str(k)] = v requests_headers['X-AppEngine-User-IP'] = source_ip requests_headers['X-AppEngine-Fake-Is-Admin'] = '1' if 'host' in requests_headers: hostname = requests_headers['host'] else: hostname = self.get_hostname(module_name, version) response = requests.request(method, 'http://' + hostname + six.ensure_str(relative_url), headers=requests_headers, data=body) return request_info.ResponseTuple(response.status_code, response.headers, response.content)
def response(status_code=200, content='', headers=None, reason=None, elapsed=0, request=None, stream=False, http_vsn=11): res = requests.Response() res.status_code = status_code if isinstance(content, (dict, list)): content = json.dumps(content).encode('utf-8') if isinstance(content, text_type): content = content.encode('utf-8') res._content = content res._content_consumed = content res.headers = structures.CaseInsensitiveDict(headers or {}) res.encoding = utils.get_encoding_from_headers(res.headers) res.reason = reason res.elapsed = datetime.timedelta(elapsed) res.request = request if hasattr(request, 'url'): res.url = request.url if isinstance(request.url, bytes): res.url = request.url.decode('utf-8') if 'set-cookie' in res.headers: res.cookies.extract_cookies(cookies.MockResponse(Headers(res)), cookies.MockRequest(request)) if stream: res.raw = BytesIO(content) else: res.raw = BytesIO(b'') res.raw.version = http_vsn # normally this closes the underlying connection, # but we have nothing to free. res.close = lambda *args, **kwargs: None return res
def response(self, status_code=200, content='', headers=None, reason=None, elapsed=0, request=None, stream=False): res = requests.Response() res.status_code = status_code if isinstance(content, (dict, list)): content = json.dumps(content).encode('utf-8') if isinstance(content, str): content = content.encode('utf-8') res._content = content res._content_consumed = content res.headers = structures.CaseInsensitiveDict(headers or {}) res.encoding = utils.get_encoding_from_headers(res.headers) res.reason = reason res.request = request if hasattr(request, 'url'): res.url = request.url if isinstance(request.url, bytes): res.url = request.url.decode('utf-8') if stream: res.raw = BytesIO(content) else: res.raw = BytesIO(b'') # normally this closes the underlying connection, # but we have nothing to free. res.close = lambda *args, **kwargs: None return res
def get_case_insensitive_dict(original): """Given a dict with string keys returns new CaseInsensitiveDict. Raises ValueError if there are duplicate keys. """ normalized = structures.CaseInsensitiveDict(original or {}) if len(normalized) != len(original): raise ValueError('Duplicate keys in: %s' % repr(original)) return normalized
def __do_register_uris(self, uri_mock_list=None): for to_mock in uri_mock_list: kw_params = { k: to_mock.pop(k) for k in ('request_headers', 'complete_qs', '_real_http') if k in to_mock } method = to_mock.pop('method') uri = to_mock.pop('uri') # NOTE(notmorgan): make sure the delimiter is non-url-safe, in this # case "|" is used so that the split can be a bit easier on # maintainers of this code. key = '{method}|{uri}|{params}'.format(method=method, uri=uri, params=kw_params) validate = to_mock.pop('validate', {}) valid_keys = set(['json', 'headers', 'params', 'data']) invalid_keys = set(validate.keys()) - valid_keys if invalid_keys: raise TypeError( "Invalid values passed to validate: {keys}".format( keys=invalid_keys)) headers = structures.CaseInsensitiveDict(to_mock.pop( 'headers', {})) if 'content-type' not in headers: headers[u'content-type'] = 'application/json' if 'exc' not in to_mock: to_mock['headers'] = headers self.calls += [dict(method=method, url=uri, **validate)] self._uri_registry.setdefault(key, { 'response_list': [], 'kw_params': kw_params }) if self._uri_registry[key]['kw_params'] != kw_params: raise AssertionError( 'PROGRAMMING ERROR: key-word-params ' 'should be part of the uri_key and cannot change, ' 'it will affect the matcher in requests_mock. ' '%(old)r != %(new)r' % { 'old': self._uri_registry[key]['kw_params'], 'new': kw_params }) self._uri_registry[key]['response_list'].append(to_mock) for mocked, params in self._uri_registry.items(): mock_method, mock_uri, _ignored = mocked.split('|', 2) self.adapter.register_uri(mock_method, mock_uri, params['response_list'], **params['kw_params'])
def register_uris(self, uri_mock_list): """Mock a list of URIs and responses via requests mock. This method may be called only once per test-case to avoid odd and difficult to debug interactions. Discovery and Auth request mocking happens separately from this method. :param uri_mock_list: List of dictionaries that template out what is passed to requests_mock fixture's `register_uri`. Format is: {'method': <HTTP_METHOD>, 'uri': <URI to be mocked>, ... } Common keys to pass in the dictionary: * json: the json response (dict) * status_code: the HTTP status (int) * validate: The request body (dict) to validate with assert_calls all key-word arguments that are valid to send to requests_mock are supported. This list should be in the order in which calls are made. When `assert_calls` is executed, order here will be validated. Duplicate URIs and Methods are allowed and will be collapsed into a single matcher. Each response will be returned in order as the URI+Method is hit. :return: None """ assert not self.__register_uris_called for to_mock in uri_mock_list: method = to_mock.pop('method') uri = to_mock.pop('uri') key = '{method}:{uri}'.format(method=method, uri=uri) validate = to_mock.pop('validate', {}) headers = structures.CaseInsensitiveDict(to_mock.pop( 'headers', {})) if 'content-type' not in headers: headers[u'content-type'] = 'application/json' to_mock['headers'] = headers self.calls += [dict(method=method, url=uri, **validate)] self._uri_registry.setdefault(key, []).append(to_mock) for mock_method_uri, params in self._uri_registry.items(): mock_method, mock_uri = mock_method_uri.split(':', 1) self.adapter.register_uri(mock_method, mock_uri, params) self.__register_uris_called = True
def register_uri(self, method, uri, **kwargs): validate = kwargs.pop('validate', {}) key = '{method}:{uri}'.format(method=method, uri=uri) headers = structures.CaseInsensitiveDict(kwargs.pop('headers', {})) if 'content-type' not in headers: headers[u'content-type'] = 'application/json' kwargs['headers'] = headers if key in self._uri_registry: self._uri_registry[key].append(kwargs) self.adapter.register_uri(method, uri, self._uri_registry[key]) else: self._uri_registry[key] = [kwargs] self.adapter.register_uri(method, uri, **kwargs) self.calls += [dict(method=method, url=uri, **validate)]
def response(status_code=200, content='', headers=None, reason=None, elapsed=0, request=None): res = requests.Response() res.status_code = status_code if isinstance(content, dict): if sys.version_info[0] == 3: content = bytes(json.dumps(content), 'utf-8') else: content = json.dumps(content) res._content = content res.headers = structures.CaseInsensitiveDict(headers or {}) res.reason = reason res.elapsed = datetime.timedelta(elapsed) if 'set-cookie' in res.headers: res.cookies.extract_cookies(cookies.MockResponse(Headers(res)), cookies.MockRequest(request)) return res
def test_traits_into_wrappers(self, mock_request): # Note traits param is None, which reflects the real value of # self.traits during _logon's request. httpresp = req_mod.Response() httpresp._content = _logon_response_text httpresp.status_code = 200 httpresp.headers = req_struct.CaseInsensitiveDict({ 'X-MC-Type': 'PVM', 'content-type': 'application/vnd.ibm.powervm.web+xml; type=LogonResponse' }) mock_request.return_value = httpresp sess = adp.Session() self.assertEqual('PVM', sess.mc_type) self.assertIsNotNone(sess.traits) self.assertTrue(sess.traits.local_api) self.assertFalse(sess.traits._is_hmc) adapter = adp.Adapter(sess) self.assertEqual(sess.traits, adapter.traits) # Response => Feed => Entrys => EntryWrappers => sub-ElementWrappers httpresp._content = _feed_file resp = adapter.read('NetworkBridge') self.assertEqual(sess.traits, resp.adapter.traits) nblist = net.NetBridge.wrap(resp) for nb in nblist: self.assertIsInstance(nb, net.NetBridge) self.assertEqual(sess.traits, nb.traits) seas = nblist[0].seas for sea in seas: self.assertIsInstance(sea, net.SEA) self.assertEqual(sess.traits, sea.traits) trunk = seas[0].primary_adpt self.assertIsInstance(trunk, net.TrunkAdapter) self.assertEqual(sess.traits, trunk.traits) # Response => Entry => EntryWrapper => sub-EntryWrappers # => sub-sub-ElementWrapper httpresp._content = _entry_file resp = adapter.read('VolumeGroup', root_id='abc123') self.assertEqual(sess.traits, resp.adapter.traits) vgent = stor.VG.wrap(resp) self.assertIsInstance(vgent, stor.VG) self.assertEqual(sess.traits, vgent.traits) pvs = vgent.phys_vols for pvent in pvs: self.assertIsInstance(pvent, stor.PV) self.assertEqual(sess.traits, pvent.traits) # Building raw wrappers from scratch class MyEntryWrapper(ewrap.EntryWrapper): schema_type = 'SomeObject' @classmethod def bld(cls, adpt): return super(MyEntryWrapper, cls)._bld(adpt) mew = MyEntryWrapper.bld(adapter) self.assertIsInstance(mew, MyEntryWrapper) self.assertEqual(sess.traits, mew.traits) class MyElementWrapper(ewrap.ElementWrapper): schema_type = 'SomeObject' @classmethod def bld(cls, adpt): return super(MyElementWrapper, cls)._bld(adpt) mew = MyElementWrapper.bld(adapter) self.assertIsInstance(mew, MyElementWrapper) self.assertEqual(sess.traits, mew.traits)
def parser_mzident2(filename, score_field, title_field=None, fdr=0.01, larger_score_is_better=False, decoy_string="DECOY", include_decoy=False): """ A general parsing function for mzIdentML files. Several exporters of mzIdentML do not report the correct spectrum indexes. X!Tandem, for example, uses the spectrum's title as "id" instead of the correct "index=N" format for MGF files. Therefore, it is possible to supply the index_field and title_field separately. Later, missing indexes will be resolved through the titles. :param filename: The path to the mzIdentML file :param score_field: The name of the score's field (**Important**: do not supply the accession but only the name) :param title_field: The name of the field supplying the spectrum's title (in SpectrumIdentificationResult). :param fdr: Target FDR (default 0.01). If set to "2" the original cut-off is used. :param larger_score_is_better: Logical indicating whether better scores mean a more reliable result. Default is False as most search engines report probabilities :param decoy_string: String used to identify decoy proteins. :param include_decoy: If set to True decoy hits are also returned. :return: A list of PSM objects """ mzid_psms = list() # load all PSMs from the file # with mzid.read(filename, use_index=False) as object_reader: tree = etree.parse(filename) root = tree.getroot() namespace = get_namespace(root) xpath_tag_str = ".//*[translate(local-name(), \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\", \"abcdefghijklmnopqrstuvwxyz\")=$tagname]" peptide_elem_list = root.xpath(xpath_tag_str, tagname="peptide", namespaces={'ns': namespace[1:-1]}) print("len of pep elem list %d" % len(peptide_elem_list)) peptide_ref_ids = dict() for peptide_elem in peptide_elem_list: peptide = dict() peptide_ref_id = structures.CaseInsensitiveDict( peptide_elem.attrib).get('id') peptide["seq"] = get_para_value_from_subelem( peptide_elem, namespace + "PeptideSequence") mods = "" for subelem in peptide_elem: if subelem.tag.lower() == "%smodification" % namespace.lower(): mods = "" + structures.CaseInsensitiveDict(subelem.attrib).get( "location") + "-" + get_modification_acc(subelem) + "," if len(mods) > 1: mods = mods[:-1] #remove the last "," peptide["mods"] = mods peptide_ref_ids[peptide_ref_id] = peptide print("got %d peptide ref identifications" % len(peptide_ref_ids)) decoy_peps = list() for pep_evid_elem in root.xpath(xpath_tag_str, tagname="peptideevidence", namespaces={'ns': namespace[1:-1]}): is_decoy = False evid_attribs = structures.CaseInsensitiveDict(pep_evid_elem.attrib) if evid_attribs.get("isDecoy") == "true" or evid_attribs.get( "isDecoy".lower()) == "true": is_decoy = True elif "accession" in evid_attribs.keys(): is_decoy = is_decoy or decoy_string in evid_attribs.get( "accession") elif "protein description" in evid_attribs.keys(): is_decoy = is_decoy or decoy_string in evid_attribs.get( "protein description") if is_decoy: peptide_ref_id = evid_attribs.get("peptide_ref") decoy_peps.append(peptide_ref_id) print("got %d decoy identifications" % len(decoy_peps)) analysis_data = root.xpath(xpath_tag_str, tagname="analysisdata", namespaces={'ns': namespace[1:-1]})[0] ident_list = analysis_data.xpath(xpath_tag_str, tagname="spectrumidentificationlist", namespaces={'ns': namespace[1:-1]})[0] for spec_ref in ident_list.xpath(xpath_tag_str, tagname="spectrumidentificationresult", namespaces={'ns': namespace[1:-1]}): spec_ref_attribs = structures.CaseInsensitiveDict(spec_ref.attrib) for spec_ident in spec_ref.xpath(xpath_tag_str, tagname="spectrumidentificationitem", namespaces={'ns': namespace[1:-1]}): spec_ident_attribs = structures.CaseInsensitiveDict( spec_ident.attrib) # filter based on original FDR if set right away if fdr == 2 and not spec_ident.get("passThreshold"): continue # only use rank 1 ids if int(spec_ident.get("rank")) > 1: continue spec_ident_all_attrib = "" for subelem in list(spec_ident): spec_ident_all_attrib += str(subelem.attrib).lower() if score_field.lower() not in spec_ident_all_attrib: raise Exception("Failed to find supplied score field '" + score_field + "' in mzIdentML file.") if title_field is not None and title_field.lower not in str( spec_ref.attrib).lower(): raise Exception("Failed to find supplied title field '" + title_field + "' in mzIdentML file.") mzid_psm = dict() score = get_score_from_spec_ident(spec_ident, score_field) if not score: raise Exception( "Failed to find supplied score from spec_ident") mzid_psm["score"] = score # the index should be used as id if spec_ref_attribs.get("spectrumID")[:6] == "index=": mzid_psm["index"] = int(spec_ref_attribs.get("spectrumID")[6:]) elif "scan number(s)" in get_sub_attrib(spec_ref): # TODO: This has only been tested for X!Tandem mzid_psm["index"] = int( get_scan_num_from_xtanem_spec_ref(spec_ref)) - 1 else: mzid_psm["index"] = Psm.MISSING_INDEX mzid_psm['charge'] = spec_ident_attribs.get( 'chargeState', 'NA') #for missing charge in peak file mzid_psm['prec_mz'] = spec_ident_attribs.get( 'experimentalMassToCharge', 'NA') # for double check is correct spec # spectrum title is optional in mzIdentML if title_field is not None: mzid_psm["title"] = spec_ref_attribs.get(title_field).strip() elif "spectrum title" in str(spec_ref.attrib).lower(): mzid_psm["title"] = spec_ref_attribs.get( "spectrum title").strip() if "peptide_ref" not in str(spec_ident.attrib).lower(): raise Exception("Error, can not found peptide_ref in %s" % (spec_ident.attrib)) peptide_ref_id = spec_ident_attribs.get("peptide_ref") peptide = peptide_ref_ids.get(peptide_ref_id) mzid_psm["sequence"] = peptide.get("seq") # if "Modification" in spec_ident: # mzid_psm["ptms"] = convert_mzid_modifications(spec_ident["Modification"]) if peptide.get("mods"): mzid_psm["ptms"] = peptide.get("mods") is_decoy = False if peptide_ref_id in decoy_peps: is_decoy = True mzid_psm["is_decoy"] = is_decoy mzid_psms.append(mzid_psm) # sort the psms based on probability mzid_psms.sort(key=operator.itemgetter('score'), reverse=larger_score_is_better) # filter decoys filtered_psms = list() n_target = 0 n_decoy = 0 for mzid_psm in mzid_psms: # only filter if the FDR wasn't set to 2 if fdr != 2: if mzid_psm["is_decoy"]: n_decoy += 1 else: n_target += 1 current_fdr = n_decoy * 2 / (n_target + n_decoy) if current_fdr > fdr: break if not mzid_psm["is_decoy"] or include_decoy: filtered_psms.append(mzid_psm) # filtered_psms.append(Psm(mzid_psm["index"], mzid_psm["sequence"], mzid_psm.get("title",None), # is_decoy=mzid_psm["is_decoy"], ptms=mzid_psm.get("ptms", None))) return filtered_psms
def test_match_empty_dict(self): expected = MatcherContainsKeyCaseInsensitive({}, {}, {}) got = structures.CaseInsensitiveDict({}) assert expected.match(got)
def test_basic(self): expected = MatcherContainsKeyCaseInsensitive({'a': 1}, {}, {}) got = structures.CaseInsensitiveDict({'A': 1}) assert expected.match(got)
def test_match_when_not_subset(self): expected = MatcherContainsKeyCaseInsensitive({'a': 2}, {}, {}) got = structures.CaseInsensitiveDict({'A': 1, 'b': 2}) assert not expected.match(got)
def read_url(url, data=None, timeout=None, retries=0, headers=None, ssl_details=None, check_status=True, allow_redirects=True): """Fetch a url (or post to one) with the given options. :param url: url to fetch :param data: any data to POST (this switches the request method to POST instead of GET) :param timeout: the timeout (in seconds) to wait for a response :param headers: any headers to provide (and send along) in the request :param ssl_details: a dictionary containing any ssl settings, cert_file, ca_certs and verify are valid entries (and they are only used when the url provided is https) :param check_status: checks that the response status is OK after fetching (this ensures a exception is raised on non-OK status codes) :param allow_redirects: enables redirects (or disables them) :param retries: maximum number of retries to attempt when fetching the url and the fetch fails """ url = _clean_url(url) request_args = { 'url': url, } request_args.update(_get_ssl_args(url, ssl_details)) request_args['allow_redirects'] = allow_redirects request_args['method'] = 'GET' if timeout is not None: request_args['timeout'] = max(float(timeout), 0) if data: request_args['method'] = 'POST' request_args['data'] = data if not headers: headers = structures.CaseInsensitiveDict() else: headers = structures.CaseInsensitiveDict(headers) if 'User-Agent' not in headers: headers['User-Agent'] = 'Cloud-Init/%s' % (version.version_string()) request_args['headers'] = headers session = requests.Session() if retries: retry = _Retry(total=max(int(retries), 0), raise_on_redirect=not allow_redirects) session.mount(_get_base_url(url), adapters.HTTPAdapter(max_retries=retry)) try: with session: response = session.request(**request_args) if check_status: response.raise_for_status() except exceptions.RequestException as e: if e.response is not None: raise UrlError(e, code=e.response.status_code, headers=e.response.headers) else: raise UrlError(e) else: LOG.debug("Read from %s (%s, %sb)", url, response.status_code, len(response.content)) return RequestsResponse(response)
def get_score_from_spec_ident(spec_ident, score_field): for subelem in list(spec_ident): if score_field.lower() in str(subelem.attrib).lower(): return structures.CaseInsensitiveDict(subelem.attrib).get('value') return None
def get_scan_num_from_xtanem_spec_ref(spec_ref): for subelem in list(spec_ref): if "scan number(s)" in str(subelem.attrib).lower(): return structures.CaseInsensitiveDict(subelem.attrib).get('value') return None
def test_logon(self, mock_session, mock_validate_cert): """Ensure a Session can be created and log on to PowerVM.""" # Init test data host = '0.0.0.0' user = '******' pwd = 'pwd' auditmemento = 'audit' # Create a Response object, that will serve as a mock return value my_response = req_mod.Response() my_response.status_code = 200 my_response.reason = 'OK' dict_headers = {'content-length': '576', 'x-powered-by': 'Servlet/3.0', 'set-cookie': 'JSESSIONID=0000a41BnJsGTNQvBGERA3wR1nj:' '759878cb-4f9a-4b05-a09a-3357abfea3b4; P' 'ath=/; Secure; HttpOnly, CCFWSESSION=E4' 'C0FFBE9130431DBF1864171ECC6A6E; Path=/;' ' Secure; HttpOnly', 'expires': 'Thu, 01 Dec 1994 16:00:00 GMT', 'x-transaction-id': 'XT10000073', 'cache-control': 'no-cache="set-cookie, set-cookie2"', 'date': 'Wed, 23 Jul 2014 21:51:10 GMT', 'content-type': 'application/vnd.ibm.powervm.web+xml; ' 'type=LogonResponse'} my_response.headers = req_struct.CaseInsensitiveDict(dict_headers) my_response._content = _logon_response_password # Mock out the method and class we are not currently testing session = mock_session.return_value session.request.return_value = my_response # Run the actual test result = adp.Session(host, user, pwd, auditmemento=auditmemento) # Verify the result self.assertTrue(result._logged_in) self.assertEqual('PUIoR6x0kP6fQqA7qZ8sLZQJ8MLx9JHfLCYzT4oGFSE2WaGIhaFX' 'IyQYvbqdKNS8QagjBpPi9NP7YR_h61SOJ3krS_RvKAp-oCf2p8x8' 'uvQrrDv-dUzc17IT5DkR7_jv2qc8iUD7DJ6Rw53a17rY0p63KqPg' '9oUGd6Bn3fNDLiEwaBR4WICftVxUFj-tfWMOyZZY2hWEtN2K8ScX' 'vyFMe-w3SleyRbGnlR34jb0A99s=', result._sessToken) self.assertEqual(1, mock_validate_cert.call_count) # No X-MC-Type header => 'HMC' is assumed. self.assertEqual('HMC', result.mc_type) # Now test file-based authentication and X-MC-Type my_response._content = _logon_response_file # Local/HMC is bad self.assertRaises(pvmex.Error, adp.Session) my_response.headers['X-MC-Type'] = 'PVM' result = adp.Session() # Verify the result. self.assertTrue(result._logged_in) # Token read from token_file, as indicated by logon_file.xml response. self.assertEqual('file-based-auth-token', result._sessToken) # validate_certificate should not have been called again self.assertEqual(1, mock_validate_cert.call_count) self.assertEqual('PVM', result.mc_type)