def test_credential_refresh_failure(self): """Verify that a useful error message results when the Odilo bearer token cannot be refreshed, since this is the most likely point of failure on a new setup. """ self.api.access_token_response = MockRequestsResponse( 200, {"Content-Type": "text/html"}, "Hi, this is the website, not the API.") credential = self.api.credential_object(lambda x: x) with pytest.raises(BadResponseException) as excinfo: self.api.refresh_creds(credential) assert "Bad response from " in str(excinfo.value) assert ( "may not be the right base URL. Response document was: 'Hi, this is the website, not the API.'" in str(excinfo.value)) # Also test a 400 response code. self.api.access_token_response = MockRequestsResponse( 400, {"Content-Type": "application/json"}, json.dumps(dict(errors=[dict(description="Oops")])), ) with pytest.raises(BadResponseException) as excinfo: self.api.refresh_creds(credential) assert "Bad response from" in str(excinfo.value) assert "Oops" in str(excinfo.value) # If there's a 400 response but no error information, # the generic error message is used. self.api.access_token_response = MockRequestsResponse( 400, {"Content-Type": "application/json"}, json.dumps(dict())) with pytest.raises(BadResponseException) as excinfo: self.api.refresh_creds(credential) assert "Bad response from" in str(excinfo.value) assert "may not be the right base URL." in str(excinfo.value)
def test_process_debuggable_response(self): """Test a method that gives more detailed information when a problem happens. """ m = HTTP.process_debuggable_response success = MockRequestsResponse(200, content="Success!") assert success == m("url", success) success = MockRequestsResponse(302, content="Success!") assert success == m("url", success) # An error is turned into a detailed ProblemDetail error = MockRequestsResponse(500, content="Error!") problem = m("url", error) assert isinstance(problem, ProblemDetail) assert INTEGRATION_ERROR.uri == problem.uri assert "500 response from integration server: 'Error!'" == problem.detail content, status_code, headers = INVALID_INPUT.response error = MockRequestsResponse(status_code, headers, content) problem = m("url", error) assert isinstance(problem, ProblemDetail) assert INTEGRATION_ERROR.uri == problem.uri assert ( "Remote service returned a problem detail document: %r" % content == problem.detail ) assert content == problem.debug_message # You can force a response to be treated as successful by # passing in its response code as allowed_response_codes. assert error == m("url", error, allowed_response_codes=[400]) assert error == m("url", error, allowed_response_codes=["400"]) assert error == m("url", error, allowed_response_codes=["4xx"])
def test__send_registration_request(self): class Mock(object): def __init__(self, response): self.response = response def do_post(self, url, payload, **kwargs): self.called_with = (url, payload, kwargs) return self.response # If everything goes well, the return value of do_post is # passed through. mock = Mock(MockRequestsResponse(200, content="all good")) url = "url" payload = "payload" headers = "headers" m = Registration._send_registration_request result = m(url, headers, payload, mock.do_post) assert mock.response == result called_with = mock.called_with assert called_with == ( url, payload, dict( headers=headers, timeout=60, allowed_response_codes=["2xx", "3xx", "400", "401"], ), ) # Most error handling is expected to be handled by do_post # raising an exception, but certain responses get special # treatment: # The remote sends a 401 response with a problem detail. mock = Mock( MockRequestsResponse( 401, {"Content-Type": PROBLEM_DETAIL_JSON_MEDIA_TYPE}, content=json.dumps(dict(detail="this is a problem detail")), )) result = m(url, headers, payload, mock.do_post) assert isinstance(result, ProblemDetail) assert REMOTE_INTEGRATION_FAILED.uri == result.uri assert 'Remote service returned: "this is a problem detail"' == result.detail # The remote sends some other kind of 401 response. mock = Mock( MockRequestsResponse(401, {"Content-Type": "text/html"}, content="log in why don't you")) result = m(url, headers, payload, mock.do_post) assert isinstance(result, ProblemDetail) assert REMOTE_INTEGRATION_FAILED.uri == result.uri assert 'Remote service returned: "log in why don\'t you"' == result.detail
def test_helper_constructor(self): response = MockRequestsResponse(102, content="nonsense") exc = BadResponseException.from_response( "http://url/", "Terrible response, just terrible", response ) # Turn the exception into a problem detail document, and it's full # of useful information. doc, status_code, headers = exc.as_problem_detail_document(debug=True).response doc = json.loads(doc) assert "Bad response" == doc["title"] assert ( "The server made a request to http://url/, and got an unexpected or invalid response." == doc["detail"] ) assert ( "Bad response from http://url/: Terrible response, just terrible\n\nStatus code: 102\nContent: nonsense" == doc["debug_message"] ) # Unless debug is turned off, in which case none of that # information is present. doc, status_code, headers = exc.as_problem_detail_document(debug=False).response assert "debug_message" not in json.loads(doc)
def _send_registration_request(self, register_url, payload, do_post): self._send_registration_request_called_with = (register_url, payload, do_post) return MockRequestsResponse(200, content=json.dumps("you did it!"))
def test_import_feed_response(self): """Verify that import_feed_response instantiates the OPDS_IMPORTER_CLASS subclass and calls import_from_feed on it. """ class MockOPDSImporter(OPDSImporter): def import_from_feed(self, text): """Return information that's useful for verifying that the OPDSImporter was instantiated with the right values. """ return (text, self.collection, self.identifier_mapping, self.data_source_name) class MockProvider(MockOPDSImportCoverageProvider): OPDS_IMPORTER_CLASS = MockOPDSImporter provider = MockProvider(self._default_collection) provider.lookup_client = MockSimplifiedOPDSLookup(self._url) response = MockRequestsResponse( 200, {'content-type': OPDSFeed.ACQUISITION_FEED_TYPE}, "some data") id_mapping = object() (text, collection, mapping, data_source_name) = provider.import_feed_response( response, id_mapping) eq_("some data", text) eq_(provider.collection, collection) eq_(id_mapping, mapping) eq_(provider.data_source.name, data_source_name)
def _request(self, *args, **kwargs): self.called_with.append((args, kwargs)) if self.timeout: self.timeout = False raise RequestTimedOut("url", "timeout") else: return MockRequestsResponse(200, content="content")
def _request(self, *args, **kwargs): self.called_with.append((args, kwargs)) return MockRequestsResponse( 200, content="<html><title>oh no</title><body>%s</body>" % (EnkiAPI.ERROR_INDICATOR), )
def test_bad_status_code_helper(object): response = MockRequestsResponse(500, content="Internal Server Error!") exc = BadResponseException.bad_status_code("http://url/", response) doc, status_code, headers = exc.as_problem_detail_document(debug=True).response doc = json.loads(doc) assert doc["debug_message"].startswith( "Bad response from http://url/: Got status code 500 from external server, cannot continue." )
def test_badresponseexception_on_non_opds_feed(self): response = MockRequestsResponse(200, {"content-type": "text/plain"}, "Some data") provider = self._provider() assert_raises_regexp(BadResponseException, "Wrong media type: text/plain", provider.import_feed_response, response, None)
def test_401_during_refresh_raises_error(self): """If we fail to refresh the OAuth bearer token, an exception is raised. """ self.api.access_token_response = MockRequestsResponse(401, {}, "") with pytest.raises(BadResponseException) as excinfo: self.api.refresh_creds(None) assert "Got status code 401" in str(excinfo.value) assert "can only continue on: 200." in str(excinfo.value)
def test_process_feed(self): data = sample_data("metadata_reaper_response.opds", "opds") response = MockRequestsResponse( 200, {'content-type': OPDSFeed.ACQUISITION_FEED_TYPE}, data) # A successful response gives us OPDSMessage objects. values = list(self.reaper.process_feed_response(response, {})) for x in values: assert isinstance(x, OPDSMessage) eq_([ 'Successfully removed', 'Not in collection catalog', "I've never heard of this work." ], [x.message for x in values]) # We get an error if the 'server' sends data with the wrong media # type. response = MockRequestsResponse(200, {"content-type": "text/plain"}, data) assert_raises(BadResponseException, self.reaper.process_feed_response, response, {})
def fake_200_response(*args, **kwargs): # The HTTP method and URL are passed in the order # requests.request would expect. assert ("GET", "http://url/") == args # Keyword arguments to _request_with_timeout are passed in # as-is. assert "value" == kwargs["kwarg"] # A default timeout is added. assert 20 == kwargs["timeout"] return MockRequestsResponse(200, content="Success!")
def test_badresponseexception_on_non_opds_feed(self): """If the lookup protocol sends something that's not an OPDS feed, refuse to go any further. """ provider = self._provider() provider.lookup_client = MockSimplifiedOPDSLookup(self._url) response = MockRequestsResponse(200, {"content-type": "text/plain"}, "Some data") provider.lookup_client.queue_response(response) assert_raises_regexp(BadResponseException, "Wrong media type: text/plain", provider.import_feed_response, response, None)
def mock_ask_registry(self, service_area_object, db): places = {"US": ["Chicago"], "CA": ["Victoria, BC"]} service_area_info = json.loads( urllib.parse.unquote(service_area_object)) nation = list(service_area_info.keys())[0] city_or_county = list(service_area_info.values())[0] if city_or_county == "ERROR": test.responses.append(MockRequestsResponse(502)) elif city_or_county in places[nation]: self.called_with.append(service_area_info) test.responses.append( MockRequestsResponse(200, content=json.dumps( dict(unknown=None, ambiguous=None)))) else: self.called_with.append(service_area_info) test.responses.append( MockRequestsResponse( 200, content=json.dumps( dict(unknown=[city_or_county])))) return original_ask_registry(service_area_object, db, get)
def test_process_feed_response(self): data = sample_data("metadata_reaper_response.opds", "opds") response = MockRequestsResponse( 200, {'content-type': OPDSFeed.ACQUISITION_FEED_TYPE}, data) # A successful response gives us OPDSMessage objects. values = list(self.provider.process_feed_response(response, {})) for x in values: assert isinstance(x, OPDSMessage) eq_([ 'Successfully removed', 'Not in collection catalog', "I've never heard of this work." ], [x.message for x in values])
def updates(self, timestamp): self.last_timestamp = timestamp return MockRequestsResponse( 200, {"content-type": OPDSFeed.ACQUISITION_FEED_TYPE})
def do_get(*args, **kwargs): return MockRequestsResponse(200, content=auth_response)
def extract(document, type=Registration.OPDS_2_TYPE): response = MockRequestsResponse(200, {"Content-Type": type}, document) return Registration._extract_catalog_information(response)
def test_ask_registry(self, monkeypatch): validator = GeographicValidator() registry_1 = "https://registry_1_url" registry_2 = "https://registry_2_url" registry_3 = "https://registry_3_url" registries = self._registries([registry_1, registry_2, registry_3], monkeypatch) true_response = MockRequestsResponse(200, content="{}") unknown_response = MockRequestsResponse(200, content='{"unknown": "place"}') ambiguous_response = MockRequestsResponse( 200, content='{"ambiguous": "place"}') problem_response = MockRequestsResponse(404) # Registry 1 knows about the place self.responses.append(true_response) response_1 = validator.ask_registry(json.dumps({"CA": "Victoria, BC"}), self._db, self.do_request) assert response_1 == True assert len(self.requests) == 1 request_1 = self.requests.pop() assert ( request_1[0] == 'https://registry_1_url/coverage?coverage={"CA": "Victoria, BC"}') # Registry 1 says the place is unknown, but Registry 2 finds it. self.responses.append(true_response) self.responses.append(unknown_response) response_2 = validator.ask_registry(json.dumps({"CA": "Victoria, BC"}), self._db, self.do_request) assert response_2 == True assert len(self.requests) == 2 request_2 = self.requests.pop() assert ( request_2[0] == 'https://registry_2_url/coverage?coverage={"CA": "Victoria, BC"}') request_1 = self.requests.pop() assert ( request_1[0] == 'https://registry_1_url/coverage?coverage={"CA": "Victoria, BC"}') # Registry_1 says the place is ambiguous and Registry_2 says it's unknown, but Registry_3 finds it. self.responses.append(true_response) self.responses.append(unknown_response) self.responses.append(ambiguous_response) response_3 = validator.ask_registry(json.dumps({"CA": "Victoria, BC"}), self._db, self.do_request) assert response_3 == True assert len(self.requests) == 3 request_3 = self.requests.pop() assert ( request_3[0] == 'https://registry_3_url/coverage?coverage={"CA": "Victoria, BC"}') request_2 = self.requests.pop() assert ( request_2[0] == 'https://registry_2_url/coverage?coverage={"CA": "Victoria, BC"}') request_1 = self.requests.pop() assert ( request_1[0] == 'https://registry_1_url/coverage?coverage={"CA": "Victoria, BC"}') # Registry 1 returns a problem detail, but Registry 2 finds the place self.responses.append(true_response) self.responses.append(problem_response) response_4 = validator.ask_registry(json.dumps({"CA": "Victoria, BC"}), self._db, self.do_request) assert response_4 == True assert len(self.requests) == 2 request_2 = self.requests.pop() assert ( request_2[0] == 'https://registry_2_url/coverage?coverage={"CA": "Victoria, BC"}') request_1 = self.requests.pop() assert ( request_1[0] == 'https://registry_1_url/coverage?coverage={"CA": "Victoria, BC"}') # Registry 1 returns a problem detail and the other two registries can't find the place self.responses.append(unknown_response) self.responses.append(ambiguous_response) self.responses.append(problem_response) response_5 = validator.ask_registry(json.dumps({"CA": "Victoria, BC"}), self._db, self.do_request) assert response_5.status_code == 502 assert (response_5.detail == "Unable to contact the registry at https://registry_1_url.") assert len(self.requests) == 3 request_3 = self.requests.pop() assert ( request_3[0] == 'https://registry_3_url/coverage?coverage={"CA": "Victoria, BC"}') request_2 = self.requests.pop() assert ( request_2[0] == 'https://registry_2_url/coverage?coverage={"CA": "Victoria, BC"}') request_1 = self.requests.pop() assert ( request_1[0] == 'https://registry_1_url/coverage?coverage={"CA": "Victoria, BC"}')
def test_ask_registry(self): validator = GeographicValidator() registry_1 = self._registry("https://registry_1_url") registry_2 = self._registry("https://registry_2_url") registry_3 = self._registry("https://registry_3_url") true_response = MockRequestsResponse(200, content="{}") unknown_response = MockRequestsResponse(200, content='{"unknown": "place"}') ambiguous_response = MockRequestsResponse( 200, content='{"ambiguous": "place"}') problem_response = MockRequestsResponse(404) # Registry 1 knows about the place self.responses.append(true_response) response_1 = validator.ask_registry(json.dumps({"CA": "Victoria, BC"}), self._db, self.do_request) eq_(response_1, True) eq_(len(self.requests), 1) request_1 = self.requests.pop() eq_(request_1[0], 'https://registry_1_url/coverage?coverage={"CA": "Victoria, BC"}') # Registry 1 says the place is unknown, but Registry 2 finds it. self.responses.append(true_response) self.responses.append(unknown_response) response_2 = validator.ask_registry(json.dumps({"CA": "Victoria, BC"}), self._db, self.do_request) eq_(response_2, True) eq_(len(self.requests), 2) request_2 = self.requests.pop() eq_(request_2[0], 'https://registry_2_url/coverage?coverage={"CA": "Victoria, BC"}') request_1 = self.requests.pop() eq_(request_1[0], 'https://registry_1_url/coverage?coverage={"CA": "Victoria, BC"}') # Registry_1 says the place is ambiguous and Registry_2 says it's unknown, but Registry_3 finds it. self.responses.append(true_response) self.responses.append(unknown_response) self.responses.append(ambiguous_response) response_3 = validator.ask_registry(json.dumps({"CA": "Victoria, BC"}), self._db, self.do_request) eq_(response_3, True) eq_(len(self.requests), 3) request_3 = self.requests.pop() eq_(request_3[0], 'https://registry_3_url/coverage?coverage={"CA": "Victoria, BC"}') request_2 = self.requests.pop() eq_(request_2[0], 'https://registry_2_url/coverage?coverage={"CA": "Victoria, BC"}') request_1 = self.requests.pop() eq_(request_1[0], 'https://registry_1_url/coverage?coverage={"CA": "Victoria, BC"}') # Registry 1 returns a problem detail, but Registry 2 finds the place self.responses.append(true_response) self.responses.append(problem_response) response_4 = validator.ask_registry(json.dumps({"CA": "Victoria, BC"}), self._db, self.do_request) eq_(response_4, True) eq_(len(self.requests), 2) request_2 = self.requests.pop() eq_(request_2[0], 'https://registry_2_url/coverage?coverage={"CA": "Victoria, BC"}') request_1 = self.requests.pop() eq_(request_1[0], 'https://registry_1_url/coverage?coverage={"CA": "Victoria, BC"}') # Registry 1 returns a problem detail and the other two registries can't find the place self.responses.append(unknown_response) self.responses.append(ambiguous_response) self.responses.append(problem_response) response_5 = validator.ask_registry(json.dumps({"CA": "Victoria, BC"}), self._db, self.do_request) eq_(response_5.status_code, 502) eq_(response_5.detail, "Unable to contact the registry at https://registry_1_url.") eq_(len(self.requests), 3) request_3 = self.requests.pop() eq_(request_3[0], 'https://registry_3_url/coverage?coverage={"CA": "Victoria, BC"}') request_2 = self.requests.pop() eq_(request_2[0], 'https://registry_2_url/coverage?coverage={"CA": "Victoria, BC"}') request_1 = self.requests.pop() eq_(request_1[0], 'https://registry_1_url/coverage?coverage={"CA": "Victoria, BC"}')
def response(self, *args, **kwargs): self.requests.append((args, kwargs)) return MockRequestsResponse(200, content="Success!")
def mock_access_token_response(self, credential, expires_in=-1): token = dict(token=credential, expiresIn=expires_in) return MockRequestsResponse(200, {}, json.dumps(token))
def fake_200_response(*args, **kwargs): return MockRequestsResponse(200, content="Hurray")
def fake_401_response(*args, **kwargs): return MockRequestsResponse(401, content="Weird")
def fake_500_response(*args, **kwargs): return MockRequestsResponse(500, content="Failure!")
def _get(self, _url): self.urls.append(_url) return MockRequestsResponse( 200, {"content-type": OPDSFeed.ACQUISITION_FEED_TYPE})
def queue_response(self, status_code, headers={}, content=None): from core.testing import MockRequestsResponse self.responses.insert( 0, MockRequestsResponse(status_code, headers, content))
def queue_response(self, status_code, headers={}, content=None): self.responses.insert( 0, MockRequestsResponse(status_code, headers, content) )
def mock_request(*args, **kwargs): response = MockRequestsResponse(200, "Success!") return response