summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDolph Mathews <dolph.mathews@rackspace.com>2011-07-14 13:23:56 -0500
committerDolph Mathews <dolph.mathews@rackspace.com>2011-07-14 13:23:56 -0500
commitb9e24f93a6d9d3814a7cced6ddac47a0b49d0ccb (patch)
tree58fcdb1ee44226bbb947df9976844c019c1fd1d3
parent6e184df0c8782eaf90f958cc03cb44ad5b4188f6 (diff)
Abstracted underlying HTTP behavior away from RestfulTestCase
Added 'automatic' JSON body encoding (TODO: automatic XML encoding) Improved user-feedback on automatic response status assertion Known bug: Content-Type header does not appear to be set properly
-rw-r--r--keystone/test/system/common.py114
1 files changed, 82 insertions, 32 deletions
diff --git a/keystone/test/system/common.py b/keystone/test/system/common.py
index 28460a75..ec149741 100644
--- a/keystone/test/system/common.py
+++ b/keystone/test/system/common.py
@@ -1,19 +1,12 @@
import unittest
import httplib
-class RestfulTestCase(unittest.TestCase):
+class HttpTestCase(unittest.TestCase):
"""Performs generic HTTP request testing"""
def request(self, host='127.0.0.1', port=80, method='GET', path='/',
headers={}, body=None, expect_exception=False,):
- """Perform request and fetch httplib.HTTPResponse from the server
-
- Dynamically includes 'json' and 'xml' attributes based on the detected
- response type, and fails the current test case if unsuccessful.
-
- response.json: standard python dictionary
- response.xml: xml.etree.ElementTree
- """
+ """Perform request and fetch httplib.HTTPResponse from the server"""
# Initialize a connection
connection = httplib.HTTPConnection(host, port, timeout=3)
@@ -30,51 +23,108 @@ class RestfulTestCase(unittest.TestCase):
# Automatically assert HTTP status code
if not expect_exception:
- self.assertSuccessfulResponse(response.status)
+ self.assertSuccessfulResponse(response)
else:
- self.assertExceptionalResponse(response.status)
+ self.assertExceptionalResponse(response)
- # Attempt to parse JSON and XML automatically, if detected
- response = self._parseResponseBody(response)
-
- # Contains the response headers, body, parsed json/xml, etc
+ # Contains the response headers, body, etc
return response
- def assertSuccessfulResponse(self, status_code):
+ def assertSuccessfulResponse(self, response):
"""Asserts that a status code lies inside the 2xx range"""
- self.assertTrue(status_code >= 200 and status_code <= 299)
+ self.assertTrue(response.status >= 200 and response.status <= 299,
+ 'Status code %d is outside of the expected range (2xx) \n\n%s' %
+ (response.status, response.body))
- def assertExceptionalResponse(self, status_code):
+ def assertExceptionalResponse(self, response):
"""Asserts that a status code lies outside the 2xx range"""
- self.assertFalse(status_code >= 200 and status_code <= 299)
+ self.assertFalse(response.status >= 200 and response.status <= 299,
+ 'Status code %d is outside of the expected range (not 2xx)\n\n%s' %
+ (response.status, response.body))
- def _parseResponseBody(self, response):
- """Detects response body type, and attempts to decode it"""
- if 'application/json' in response.getheader('Content-Type'):
- response.json = self._parseJson(response.body)
- elif 'application/xml' in response.getheader('Content-Type'):
- response.xml = self._parseXml(response.body)
+class RestfulTestCase(HttpTestCase):
+ """Performs restful HTTP request testing"""
+
+ def restful_request(self, headers={}, json=None, xml=None, **kwargs):
+ """Encodes and decodes (JSON & XML) HTTP requests and responses.
+
+ Dynamically encodes json or xml request body if one is provided.
+
+ WARNING: Existing Content-Type header will be overwritten.
+ WARNING: If both json and xml are provided, the xml is ignored.
+ WARNING: If either json or xml AND a body is provided, the body is
+ ignored.
+
+ Dynamically returns 'json' or 'xml' attribute based on the detected
+ response type, and fails the current test case if unsuccessful.
+
+ response.json: standard python dictionary
+ response.xml: xml.etree.ElementTree
+ """
+
+ # Attempt to encode JSON and XML automatically, if requested
+ if json:
+ body = self._encodeJson(json)
+ headers['Content-Type'] = 'application/json'
+ elif xml:
+ body = self._encodeXml(xml)
+ headers['Content-Type'] = 'application/xml'
+ else:
+ body = kwargs.get('body')
+
+ # Perform the HTTP request/response
+ response = self.request(headers=headers, body=body, **kwargs)
+
+ # Attempt to parse JSON and XML automatically, if detected
+ response = self._decodeResponseBody(response)
+
+ # Contains the decoded response json/xml, etc
return response
- def _parseXml(self, xml_str):
- """Returns an ElementTree of the given XML string"""
+ def _encodeJson(self, data):
+ """Returns a JSON-encoded string of the given python dictionary"""
try:
- import xml.etree.ElementTree
- return xml.etree.ElementTree.fromstring(xml_str)
+ import json
+ return json.dumps(data)
except Exception as e:
self.fail(e)
- def _parseJson(self, json_str):
+ def _encodeXml(self, data):
+ """Returns an XML-encoded string of the given python dictionary"""
+ self.fail('XML encoding is not yet supported by this framework')
+
+ def _decodeResponseBody(self, response):
+ """Detects response body type, and attempts to decode it"""
+ if 'application/json' in response.getheader('Content-Type'):
+ response.json = self._decodeJson(response.body)
+ elif 'application/xml' in response.getheader('Content-Type'):
+ response.xml = self._decodeXml(response.body)
+ return response
+
+ def _decodeJson(self, json_str):
"""Returns a dict of the given JSON string"""
try:
import json
return json.loads(json_str)
except Exception as e:
self.fail(e)
+
+ def _decodeXml(self, xml_str):
+ """Returns an ElementTree of the given XML string"""
+ try:
+ import xml.etree.ElementTree
+ return xml.etree.ElementTree.fromstring(xml_str)
+ except Exception as e:
+ self.fail(e)
class KeystoneTestCase(RestfulTestCase):
"""Perform generic HTTP request against Keystone APIs"""
service_token = None
+
+ admin_credentials = {'passwordCredentials':{
+ 'username':'admin',
+ 'password':'secrete',
+ }}
admin_token = None
def service_request(self, port=5000, headers={}, **kwargs):
@@ -83,7 +133,7 @@ class KeystoneTestCase(RestfulTestCase):
if self.service_token:
headers['X-Auth-Token'] = self.service_token
- return self.request(port=port, headers=headers, **kwargs)
+ return self.restful_request(port=port, headers=headers, **kwargs)
def admin_request(self, port=5001, headers={}, **kwargs):
"""Returns a request to the admin API"""
@@ -91,4 +141,4 @@ class KeystoneTestCase(RestfulTestCase):
if self.admin_token:
headers['X-Auth-Token'] = self.admin_token
- return self.request(port=port, headers=headers, **kwargs)
+ return self.restful_request(port=port, headers=headers, **kwargs)