Merge lp:~rackspace-titan/nova/osapi-images-metadata into lp:~hudson-openstack/nova/trunk

Proposed by Brian Waldon
Status: Merged
Approved by: Devin Carlen
Approved revision: 1308
Merged at revision: 1320
Proposed branch: lp:~rackspace-titan/nova/osapi-images-metadata
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 604 lines (+254/-89)
6 files modified
nova/api/openstack/__init__.py (+8/-2)
nova/api/openstack/create_instance_helper.py (+7/-38)
nova/api/openstack/image_metadata.py (+50/-3)
nova/api/openstack/wsgi.py (+38/-4)
nova/tests/api/openstack/test_image_metadata.py (+144/-42)
nova/tests/api/openstack/test_wsgi.py (+7/-0)
To merge this branch: bzr merge lp:~rackspace-titan/nova/osapi-images-metadata
Reviewer Review Type Date Requested Status
Devin Carlen (community) Approve
Vish Ishaya (community) Approve
Review via email: mp+68909@code.launchpad.net

Description of the change

- Updates /images/<id>/meta and /images/<id>/meta/<key> to respect the latest specification
- Renames ../meta to ../metadata
- Adds PUT on ../metadata to set entire container (controller action is called update_all)

To post a comment you must log in.
Revision history for this message
Vish Ishaya (vishvananda) wrote :

rock on.

review: Approve
Revision history for this message
Devin Carlen (devcamcar) wrote :

lgtm

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'nova/api/openstack/__init__.py'
2--- nova/api/openstack/__init__.py 2011-07-13 15:10:40 +0000
3+++ nova/api/openstack/__init__.py 2011-07-25 19:58:26 +0000
4@@ -164,11 +164,17 @@
5
6 def _setup_routes(self, mapper):
7 super(APIRouterV11, self)._setup_routes(mapper, '1.1')
8- mapper.resource("image_meta", "meta",
9- controller=image_metadata.create_resource(),
10+ image_metadata_controller = image_metadata.create_resource()
11+ mapper.resource("image_meta", "metadata",
12+ controller=image_metadata_controller,
13 parent_resource=dict(member_name='image',
14 collection_name='images'))
15
16+ mapper.connect("metadata", "/images/{image_id}/metadata",
17+ controller=image_metadata_controller,
18+ action='update_all',
19+ conditions={"method": ['PUT']})
20+
21 mapper.resource("server_meta", "meta",
22 controller=server_metadata.create_resource(),
23 parent_resource=dict(member_name='server',
24
25=== modified file 'nova/api/openstack/create_instance_helper.py'
26--- nova/api/openstack/create_instance_helper.py 2011-07-21 19:14:16 +0000
27+++ nova/api/openstack/create_instance_helper.py 2011-07-25 19:58:26 +0000
28@@ -282,7 +282,7 @@
29 return password
30
31
32-class ServerXMLDeserializer(wsgi.XMLDeserializer):
33+class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer):
34 """
35 Deserializer to handle xml-formatted server create requests.
36
37@@ -299,11 +299,12 @@
38 def _extract_server(self, node):
39 """Marshal the server attribute of a parsed request"""
40 server = {}
41- server_node = self._find_first_child_named(node, 'server')
42+ server_node = self.find_first_child_named(node, 'server')
43 for attr in ["name", "imageId", "flavorId", "imageRef", "flavorRef"]:
44 if server_node.getAttribute(attr):
45 server[attr] = server_node.getAttribute(attr)
46- metadata = self._extract_metadata(server_node)
47+ metadata_node = self.find_first_child_named(server_node, "metadata")
48+ metadata = self.extract_metadata(metadata_node)
49 if metadata is not None:
50 server["metadata"] = metadata
51 personality = self._extract_personality(server_node)
52@@ -311,49 +312,17 @@
53 server["personality"] = personality
54 return server
55
56- def _extract_metadata(self, server_node):
57- """Marshal the metadata attribute of a parsed request"""
58- metadata_node = self._find_first_child_named(server_node, "metadata")
59- if metadata_node is None:
60- return None
61- metadata = {}
62- for meta_node in self._find_children_named(metadata_node, "meta"):
63- key = meta_node.getAttribute("key")
64- metadata[key] = self._extract_text(meta_node)
65- return metadata
66-
67 def _extract_personality(self, server_node):
68 """Marshal the personality attribute of a parsed request"""
69 personality_node = \
70- self._find_first_child_named(server_node, "personality")
71+ self.find_first_child_named(server_node, "personality")
72 if personality_node is None:
73 return None
74 personality = []
75- for file_node in self._find_children_named(personality_node, "file"):
76+ for file_node in self.find_children_named(personality_node, "file"):
77 item = {}
78 if file_node.hasAttribute("path"):
79 item["path"] = file_node.getAttribute("path")
80- item["contents"] = self._extract_text(file_node)
81+ item["contents"] = self.extract_text(file_node)
82 personality.append(item)
83 return personality
84-
85- def _find_first_child_named(self, parent, name):
86- """Search a nodes children for the first child with a given name"""
87- for node in parent.childNodes:
88- if node.nodeName == name:
89- return node
90- return None
91-
92- def _find_children_named(self, parent, name):
93- """Return all of a nodes children who have the given name"""
94- for node in parent.childNodes:
95- if node.nodeName == name:
96- yield node
97-
98- def _extract_text(self, node):
99- """Get the text field contained by the given node"""
100- if len(node.childNodes) == 1:
101- child = node.childNodes[0]
102- if child.nodeType == child.TEXT_NODE:
103- return child.nodeValue
104- return ""
105
106=== modified file 'nova/api/openstack/image_metadata.py'
107--- nova/api/openstack/image_metadata.py 2011-07-21 14:02:16 +0000
108+++ nova/api/openstack/image_metadata.py 2011-07-25 19:58:26 +0000
109@@ -96,8 +96,16 @@
110 self._check_quota_limit(context, metadata)
111 img['properties'] = metadata
112 self.image_service.update(context, image_id, img, None)
113+ return dict(meta=meta)
114
115- return req.body
116+ def update_all(self, req, image_id, body):
117+ context = req.environ['nova.context']
118+ img = self.image_service.show(context, image_id)
119+ metadata = body.get('metadata', {})
120+ self._check_quota_limit(context, metadata)
121+ img['properties'] = metadata
122+ self.image_service.update(context, image_id, img, None)
123+ return dict(metadata=metadata)
124
125 def delete(self, req, image_id, id):
126 context = req.environ['nova.context']
127@@ -110,6 +118,32 @@
128 self.image_service.update(context, image_id, img, None)
129
130
131+class ImageMetadataXMLDeserializer(wsgi.MetadataXMLDeserializer):
132+
133+ def _extract_metadata_container(self, datastring):
134+ dom = minidom.parseString(datastring)
135+ metadata_node = self.find_first_child_named(dom, "metadata")
136+ metadata = self.extract_metadata(metadata_node)
137+ return {'body': {'metadata': metadata}}
138+
139+ def create(self, datastring):
140+ return self._extract_metadata_container(datastring)
141+
142+ def update_all(self, datastring):
143+ return self._extract_metadata_container(datastring)
144+
145+ def update(self, datastring):
146+ dom = minidom.parseString(datastring)
147+ metadata_item = self.extract_metadata(dom)
148+ return {'body': {'meta': metadata_item}}
149+
150+
151+class HeadersSerializer(wsgi.ResponseHeadersSerializer):
152+
153+ def delete(self, response, data):
154+ response.status_int = 204
155+
156+
157 class ImageMetadataXMLSerializer(wsgi.XMLDictSerializer):
158 def __init__(self, xmlns=wsgi.XMLNS_V11):
159 super(ImageMetadataXMLSerializer, self).__init__(xmlns=xmlns)
160@@ -143,6 +177,9 @@
161 def create(self, metadata_dict):
162 return self._meta_list_to_xml_string(metadata_dict)
163
164+ def update_all(self, metadata_dict):
165+ return self._meta_list_to_xml_string(metadata_dict)
166+
167 def _meta_item_to_xml_string(self, meta_item_dict):
168 xml_doc = minidom.Document()
169 item_key, item_value = meta_item_dict.items()[0]
170@@ -157,11 +194,21 @@
171 def update(self, meta_item_dict):
172 return self._meta_item_to_xml_string(meta_item_dict['meta'])
173
174+ def default(self, *args, **kwargs):
175+ return ''
176+
177
178 def create_resource():
179+ headers_serializer = HeadersSerializer()
180+
181+ body_deserializers = {
182+ 'application/xml': ImageMetadataXMLDeserializer(),
183+ }
184+
185 body_serializers = {
186 'application/xml': ImageMetadataXMLSerializer(),
187 }
188- serializer = wsgi.ResponseSerializer(body_serializers)
189+ serializer = wsgi.ResponseSerializer(body_serializers, headers_serializer)
190+ deserializer = wsgi.RequestDeserializer(body_deserializers)
191
192- return wsgi.Resource(Controller(), serializer=serializer)
193+ return wsgi.Resource(Controller(), deserializer, serializer)
194
195=== modified file 'nova/api/openstack/wsgi.py'
196--- nova/api/openstack/wsgi.py 2011-07-22 03:44:45 +0000
197+++ nova/api/openstack/wsgi.py 2011-07-25 19:58:26 +0000
198@@ -136,10 +136,44 @@
199 listnames)
200 return result
201
202+ def find_first_child_named(self, parent, name):
203+ """Search a nodes children for the first child with a given name"""
204+ for node in parent.childNodes:
205+ if node.nodeName == name:
206+ return node
207+ return None
208+
209+ def find_children_named(self, parent, name):
210+ """Return all of a nodes children who have the given name"""
211+ for node in parent.childNodes:
212+ if node.nodeName == name:
213+ yield node
214+
215+ def extract_text(self, node):
216+ """Get the text field contained by the given node"""
217+ if len(node.childNodes) == 1:
218+ child = node.childNodes[0]
219+ if child.nodeType == child.TEXT_NODE:
220+ return child.nodeValue
221+ return ""
222+
223 def default(self, datastring):
224 return {'body': self._from_xml(datastring)}
225
226
227+class MetadataXMLDeserializer(XMLDeserializer):
228+
229+ def extract_metadata(self, metadata_node):
230+ """Marshal the metadata attribute of a parsed request"""
231+ if metadata_node is None:
232+ return None
233+ metadata = {}
234+ for meta_node in self.find_children_named(metadata_node, "meta"):
235+ key = meta_node.getAttribute("key")
236+ metadata[key] = self.extract_text(meta_node)
237+ return metadata
238+
239+
240 class RequestHeadersDeserializer(ActionDispatcher):
241 """Default request headers deserializer"""
242
243@@ -397,8 +431,9 @@
244
245 def serialize_body(self, response, data, content_type, action):
246 response.headers['Content-Type'] = content_type
247- serializer = self.get_body_serializer(content_type)
248- response.body = serializer.serialize(data, action)
249+ if data is not None:
250+ serializer = self.get_body_serializer(content_type)
251+ response.body = serializer.serialize(data, action)
252
253 def get_body_serializer(self, content_type):
254 try:
255@@ -444,7 +479,7 @@
256 action, args, accept = self.deserializer.deserialize(request)
257 except exception.InvalidContentType:
258 msg = _("Unsupported Content-Type")
259- return webob.exc.HTTPBadRequest(explanation=msg)
260+ return faults.Fault(webob.exc.HTTPBadRequest(explanation=msg))
261 except exception.MalformedRequestBody:
262 msg = _("Malformed request body")
263 return faults.Fault(webob.exc.HTTPBadRequest(explanation=msg))
264@@ -455,7 +490,6 @@
265 LOG.info(_("HTTP exception thrown: %s"), unicode(ex))
266 action_result = faults.Fault(ex)
267
268- #TODO(bcwaldon): find a more elegant way to pass through non-dict types
269 if type(action_result) is dict or action_result is None:
270 response = self.serializer.serialize(action_result,
271 accept,
272
273=== modified file 'nova/tests/api/openstack/test_image_metadata.py'
274--- nova/tests/api/openstack/test_image_metadata.py 2011-06-28 15:59:46 +0000
275+++ nova/tests/api/openstack/test_image_metadata.py 2011-07-25 19:58:26 +0000
276@@ -103,8 +103,7 @@
277 super(ImageMetaDataTest, self).tearDown()
278
279 def test_index(self):
280- req = webob.Request.blank('/v1.1/images/1/meta')
281- req.environ['api.version'] = '1.1'
282+ req = webob.Request.blank('/v1.1/images/1/metadata')
283 res = req.get_response(fakes.wsgi_app())
284 res_dict = json.loads(res.body)
285 self.assertEqual(200, res.status_int)
286@@ -114,8 +113,7 @@
287 self.assertEqual(value, res_dict['metadata'][key])
288
289 def test_show(self):
290- req = webob.Request.blank('/v1.1/images/1/meta/key1')
291- req.environ['api.version'] = '1.1'
292+ req = webob.Request.blank('/v1.1/images/1/metadata/key1')
293 res = req.get_response(fakes.wsgi_app())
294 res_dict = json.loads(res.body)
295 self.assertEqual(200, res.status_int)
296@@ -124,42 +122,66 @@
297 self.assertEqual('value1', res_dict['meta']['key1'])
298
299 def test_show_not_found(self):
300- req = webob.Request.blank('/v1.1/images/1/meta/key9')
301- req.environ['api.version'] = '1.1'
302+ req = webob.Request.blank('/v1.1/images/1/metadata/key9')
303 res = req.get_response(fakes.wsgi_app())
304 self.assertEqual(404, res.status_int)
305
306 def test_create(self):
307- req = webob.Request.blank('/v1.1/images/2/meta')
308- req.environ['api.version'] = '1.1'
309+ req = webob.Request.blank('/v1.1/images/2/metadata')
310 req.method = 'POST'
311 req.body = '{"metadata": {"key9": "value9"}}'
312 req.headers["content-type"] = "application/json"
313 res = req.get_response(fakes.wsgi_app())
314- res_dict = json.loads(res.body)
315- self.assertEqual(200, res.status_int)
316- self.assertEqual('value9', res_dict['metadata']['key9'])
317- # other items should not be modified
318- self.assertEqual('value1', res_dict['metadata']['key1'])
319- self.assertEqual('value2', res_dict['metadata']['key2'])
320- self.assertEqual(1, len(res_dict))
321+
322+ self.assertEqual(200, res.status_int)
323+ actual_output = json.loads(res.body)
324+
325+ expected_output = {
326+ 'metadata': {
327+ 'key1': 'value1',
328+ 'key2': 'value2',
329+ 'key9': 'value9',
330+ },
331+ }
332+
333+ self.assertEqual(expected_output, actual_output)
334+
335+ def test_update_all(self):
336+ req = webob.Request.blank('/v1.1/images/2/metadata')
337+ req.method = 'PUT'
338+ req.body = '{"metadata": {"key9": "value9"}}'
339+ req.headers["content-type"] = "application/json"
340+ res = req.get_response(fakes.wsgi_app())
341+
342+ self.assertEqual(200, res.status_int)
343+ actual_output = json.loads(res.body)
344+
345+ expected_output = {
346+ 'metadata': {
347+ 'key9': 'value9',
348+ },
349+ }
350+
351+ self.assertEqual(expected_output, actual_output)
352
353 def test_update_item(self):
354- req = webob.Request.blank('/v1.1/images/1/meta/key1')
355- req.environ['api.version'] = '1.1'
356+ req = webob.Request.blank('/v1.1/images/1/metadata/key1')
357 req.method = 'PUT'
358 req.body = '{"meta": {"key1": "zz"}}'
359 req.headers["content-type"] = "application/json"
360 res = req.get_response(fakes.wsgi_app())
361+
362 self.assertEqual(200, res.status_int)
363- res_dict = json.loads(res.body)
364- self.assertTrue('meta' in res_dict)
365- self.assertEqual(len(res_dict['meta']), 1)
366- self.assertEqual('zz', res_dict['meta']['key1'])
367+ actual_output = json.loads(res.body)
368+ expected_output = {
369+ 'meta': {
370+ 'key1': 'zz',
371+ },
372+ }
373+ self.assertEqual(actual_output, expected_output)
374
375 def test_update_item_bad_body(self):
376- req = webob.Request.blank('/v1.1/images/1/meta/key1')
377- req.environ['api.version'] = '1.1'
378+ req = webob.Request.blank('/v1.1/images/1/metadata/key1')
379 req.method = 'PUT'
380 req.body = '{"key1": "zz"}'
381 req.headers["content-type"] = "application/json"
382@@ -167,8 +189,7 @@
383 self.assertEqual(400, res.status_int)
384
385 def test_update_item_too_many_keys(self):
386- req = webob.Request.blank('/v1.1/images/1/meta/key1')
387- req.environ['api.version'] = '1.1'
388+ req = webob.Request.blank('/v1.1/images/1/metadata/key1')
389 req.method = 'PUT'
390 req.body = '{"meta": {"key1": "value1", "key2": "value2"}}'
391 req.headers["content-type"] = "application/json"
392@@ -176,24 +197,38 @@
393 self.assertEqual(400, res.status_int)
394
395 def test_update_item_body_uri_mismatch(self):
396- req = webob.Request.blank('/v1.1/images/1/meta/bad')
397- req.environ['api.version'] = '1.1'
398+ req = webob.Request.blank('/v1.1/images/1/metadata/bad')
399 req.method = 'PUT'
400 req.body = '{"meta": {"key1": "value1"}}'
401 req.headers["content-type"] = "application/json"
402 res = req.get_response(fakes.wsgi_app())
403 self.assertEqual(400, res.status_int)
404
405+ def test_update_item_xml(self):
406+ req = webob.Request.blank('/v1.1/images/1/metadata/key1')
407+ req.method = 'PUT'
408+ req.body = '<meta key="key1">five</meta>'
409+ req.headers["content-type"] = "application/xml"
410+ res = req.get_response(fakes.wsgi_app())
411+
412+ self.assertEqual(200, res.status_int)
413+ actual_output = json.loads(res.body)
414+ expected_output = {
415+ 'meta': {
416+ 'key1': 'five',
417+ },
418+ }
419+ self.assertEqual(actual_output, expected_output)
420+
421 def test_delete(self):
422- req = webob.Request.blank('/v1.1/images/2/meta/key1')
423- req.environ['api.version'] = '1.1'
424+ req = webob.Request.blank('/v1.1/images/2/metadata/key1')
425 req.method = 'DELETE'
426 res = req.get_response(fakes.wsgi_app())
427- self.assertEqual(200, res.status_int)
428+ self.assertEqual(204, res.status_int)
429+ self.assertEqual('', res.body)
430
431 def test_delete_not_found(self):
432- req = webob.Request.blank('/v1.1/images/2/meta/blah')
433- req.environ['api.version'] = '1.1'
434+ req = webob.Request.blank('/v1.1/images/2/metadata/blah')
435 req.method = 'DELETE'
436 res = req.get_response(fakes.wsgi_app())
437 self.assertEqual(404, res.status_int)
438@@ -203,8 +238,7 @@
439 for num in range(FLAGS.quota_metadata_items + 1):
440 data['metadata']['key%i' % num] = "blah"
441 json_string = str(data).replace("\'", "\"")
442- req = webob.Request.blank('/v1.1/images/2/meta')
443- req.environ['api.version'] = '1.1'
444+ req = webob.Request.blank('/v1.1/images/2/metadata')
445 req.method = 'POST'
446 req.body = json_string
447 req.headers["content-type"] = "application/json"
448@@ -212,8 +246,7 @@
449 self.assertEqual(400, res.status_int)
450
451 def test_too_many_metadata_items_on_put(self):
452- req = webob.Request.blank('/v1.1/images/3/meta/blah')
453- req.environ['api.version'] = '1.1'
454+ req = webob.Request.blank('/v1.1/images/3/metadata/blah')
455 req.method = 'PUT'
456 req.body = '{"meta": {"blah": "blah"}}'
457 req.headers["content-type"] = "application/json"
458@@ -221,9 +254,49 @@
459 self.assertEqual(400, res.status_int)
460
461
462+class ImageMetadataXMLDeserializationTest(test.TestCase):
463+
464+ deserializer = openstack.image_metadata.ImageMetadataXMLDeserializer()
465+
466+ def test_create(self):
467+ request_body = """
468+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
469+ <meta key='123'>asdf</meta>
470+ <meta key='567'>jkl;</meta>
471+ </metadata>"""
472+ output = self.deserializer.deserialize(request_body, 'create')
473+ expected = {"body": {"metadata": {"123": "asdf", "567": "jkl;"}}}
474+ self.assertEquals(output, expected)
475+
476+ def test_create_empty(self):
477+ request_body = """
478+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"/>"""
479+ output = self.deserializer.deserialize(request_body, 'create')
480+ expected = {"body": {"metadata": {}}}
481+ self.assertEquals(output, expected)
482+
483+ def test_update_all(self):
484+ request_body = """
485+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
486+ <meta key='123'>asdf</meta>
487+ <meta key='567'>jkl;</meta>
488+ </metadata>"""
489+ output = self.deserializer.deserialize(request_body, 'update_all')
490+ expected = {"body": {"metadata": {"123": "asdf", "567": "jkl;"}}}
491+ self.assertEquals(output, expected)
492+
493+ def test_update(self):
494+ request_body = """
495+ <meta xmlns="http://docs.openstack.org/compute/api/v1.1"
496+ key='123'>asdf</meta>"""
497+ output = self.deserializer.deserialize(request_body, 'update')
498+ expected = {"body": {"meta": {"123": "asdf"}}}
499+ self.assertEquals(output, expected)
500+
501+
502 class ImageMetadataXMLSerializationTest(test.TestCase):
503
504- def test_index_xml(self):
505+ def test_index(self):
506 serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
507 fixture = {
508 'metadata': {
509@@ -247,7 +320,7 @@
510
511 self.assertEqual(expected.toxml(), actual.toxml())
512
513- def test_index_xml_null(self):
514+ def test_index_null(self):
515 serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
516 fixture = {
517 'metadata': {
518@@ -267,7 +340,7 @@
519
520 self.assertEqual(expected.toxml(), actual.toxml())
521
522- def test_index_xml_unicode(self):
523+ def test_index_unicode(self):
524 serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
525 fixture = {
526 'metadata': {
527@@ -287,7 +360,7 @@
528
529 self.assertEqual(expected.toxml(), actual.toxml())
530
531- def test_show_xml(self):
532+ def test_show(self):
533 serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
534 fixture = {
535 'meta': {
536@@ -305,7 +378,31 @@
537
538 self.assertEqual(expected.toxml(), actual.toxml())
539
540- def test_update_item_xml(self):
541+ def test_update_all(self):
542+ serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
543+ fixture = {
544+ 'metadata': {
545+ 'key6': 'value6',
546+ 'key4': 'value4',
547+ },
548+ }
549+ output = serializer.serialize(fixture, 'update_all')
550+ actual = minidom.parseString(output.replace(" ", ""))
551+
552+ expected = minidom.parseString("""
553+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
554+ <meta key="key6">
555+ value6
556+ </meta>
557+ <meta key="key4">
558+ value4
559+ </meta>
560+ </metadata>
561+ """.replace(" ", ""))
562+
563+ self.assertEqual(expected.toxml(), actual.toxml())
564+
565+ def test_update_item(self):
566 serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
567 fixture = {
568 'meta': {
569@@ -323,7 +420,7 @@
570
571 self.assertEqual(expected.toxml(), actual.toxml())
572
573- def test_create_xml(self):
574+ def test_create(self):
575 serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
576 fixture = {
577 'metadata': {
578@@ -350,3 +447,8 @@
579 """.replace(" ", ""))
580
581 self.assertEqual(expected.toxml(), actual.toxml())
582+
583+ def test_delete(self):
584+ serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
585+ output = serializer.serialize(None, 'delete')
586+ self.assertEqual(output, '')
587
588=== modified file 'nova/tests/api/openstack/test_wsgi.py'
589--- nova/tests/api/openstack/test_wsgi.py 2011-07-11 16:33:53 +0000
590+++ nova/tests/api/openstack/test_wsgi.py 2011-07-25 19:58:26 +0000
591@@ -256,6 +256,13 @@
592 self.assertEqual(response.body, 'pew_json')
593 self.assertEqual(response.status_int, 404)
594
595+ def test_serialize_response_None(self):
596+ response = self.serializer.serialize(None, 'application/json')
597+ print response
598+ self.assertEqual(response.headers['Content-Type'], 'application/json')
599+ self.assertEqual(response.body, '')
600+ self.assertEqual(response.status_int, 404)
601+
602 def test_serialize_response_dict_to_unknown_content_type(self):
603 self.assertRaises(exception.InvalidContentType,
604 self.serializer.serialize,