/atom/mock_http.py
http://radioappz.googlecode.com/ · Python · 132 lines · 77 code · 19 blank · 36 comment · 13 complexity · 6494631e5d70033d33ccb0205c8dcc13 MD5 · raw file
- #!/usr/bin/python
- #
- # Copyright (C) 2008 Google Inc.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- __author__ = 'api.jscudder (Jeff Scudder)'
- import atom.http_interface
- import atom.url
- class Error(Exception):
- pass
- class NoRecordingFound(Error):
- pass
- class MockRequest(object):
- """Holds parameters of an HTTP request for matching against future requests.
- """
- def __init__(self, operation, url, data=None, headers=None):
- self.operation = operation
- if isinstance(url, (str, unicode)):
- url = atom.url.parse_url(url)
- self.url = url
- self.data = data
- self.headers = headers
- class MockResponse(atom.http_interface.HttpResponse):
- """Simulates an httplib.HTTPResponse object."""
- def __init__(self, body=None, status=None, reason=None, headers=None):
- if body and hasattr(body, 'read'):
- self.body = body.read()
- else:
- self.body = body
- if status is not None:
- self.status = int(status)
- else:
- self.status = None
- self.reason = reason
- self._headers = headers or {}
- def read(self):
- return self.body
- class MockHttpClient(atom.http_interface.GenericHttpClient):
- def __init__(self, headers=None, recordings=None, real_client=None):
- """An HttpClient which responds to request with stored data.
- The request-response pairs are stored as tuples in a member list named
- recordings.
- The MockHttpClient can be switched from replay mode to record mode by
- setting the real_client member to an instance of an HttpClient which will
- make real HTTP requests and store the server's response in list of
- recordings.
-
- Args:
- headers: dict containing HTTP headers which should be included in all
- HTTP requests.
- recordings: The initial recordings to be used for responses. This list
- contains tuples in the form: (MockRequest, MockResponse)
- real_client: An HttpClient which will make a real HTTP request. The
- response will be converted into a MockResponse and stored in
- recordings.
- """
- self.recordings = recordings or []
- self.real_client = real_client
- self.headers = headers or {}
- def add_response(self, response, operation, url, data=None, headers=None):
- """Adds a request-response pair to the recordings list.
-
- After the recording is added, future matching requests will receive the
- response.
-
- Args:
- response: MockResponse
- operation: str
- url: str
- data: str, Currently the data is ignored when looking for matching
- requests.
- headers: dict of strings: Currently the headers are ignored when
- looking for matching requests.
- """
- request = MockRequest(operation, url, data=data, headers=headers)
- self.recordings.append((request, response))
- def request(self, operation, url, data=None, headers=None):
- """Returns a matching MockResponse from the recordings.
-
- If the real_client is set, the request will be passed along and the
- server's response will be added to the recordings and also returned.
- If there is no match, a NoRecordingFound error will be raised.
- """
- if self.real_client is None:
- if isinstance(url, (str, unicode)):
- url = atom.url.parse_url(url)
- for recording in self.recordings:
- if recording[0].operation == operation and recording[0].url == url:
- return recording[1]
- raise NoRecordingFound('No recodings found for %s %s' % (
- operation, url))
- else:
- # There is a real HTTP client, so make the request, and record the
- # response.
- response = self.real_client.request(operation, url, data=data,
- headers=headers)
- # TODO: copy the headers
- stored_response = MockResponse(body=response, status=response.status,
- reason=response.reason)
- self.add_response(stored_response, operation, url, data=data,
- headers=headers)
- return stored_response