Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / test / images.py @ 07419e58

History | View | Annotate | Download (12.7 kB)

1 039e3e61 Christos Stavrakakis
# Copyright 2012 GRNET S.A. All rights reserved.
2 039e3e61 Christos Stavrakakis
#
3 039e3e61 Christos Stavrakakis
# Redistribution and use in source and binary forms, with or
4 039e3e61 Christos Stavrakakis
# without modification, are permitted provided that the following
5 039e3e61 Christos Stavrakakis
# conditions are met:
6 039e3e61 Christos Stavrakakis
#
7 039e3e61 Christos Stavrakakis
#   1. Redistributions of source code must retain the above
8 039e3e61 Christos Stavrakakis
#      copyright notice, this list of conditions and the following
9 039e3e61 Christos Stavrakakis
#      disclaimer.
10 039e3e61 Christos Stavrakakis
#
11 039e3e61 Christos Stavrakakis
#   2. Redistributions in binary form must reproduce the above
12 039e3e61 Christos Stavrakakis
#      copyright notice, this list of conditions and the following
13 039e3e61 Christos Stavrakakis
#      disclaimer in the documentation and/or other materials
14 039e3e61 Christos Stavrakakis
#      provided with the distribution.
15 039e3e61 Christos Stavrakakis
#
16 039e3e61 Christos Stavrakakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 039e3e61 Christos Stavrakakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 039e3e61 Christos Stavrakakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 039e3e61 Christos Stavrakakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 039e3e61 Christos Stavrakakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 039e3e61 Christos Stavrakakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 039e3e61 Christos Stavrakakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 039e3e61 Christos Stavrakakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 039e3e61 Christos Stavrakakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 039e3e61 Christos Stavrakakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 039e3e61 Christos Stavrakakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 039e3e61 Christos Stavrakakis
# POSSIBILITY OF SUCH DAMAGE.
28 039e3e61 Christos Stavrakakis
#
29 039e3e61 Christos Stavrakakis
# The views and conclusions contained in the software and
30 039e3e61 Christos Stavrakakis
# documentation are those of the authors and should not be
31 039e3e61 Christos Stavrakakis
# interpreted as representing official policies, either expressed
32 039e3e61 Christos Stavrakakis
# or implied, of GRNET S.A.
33 039e3e61 Christos Stavrakakis
34 039e3e61 Christos Stavrakakis
import json
35 039e3e61 Christos Stavrakakis
36 bd40abfa Christos Stavrakakis
from snf_django.lib.api import faults
37 d2b8ec7b Christos Stavrakakis
from snf_django.utils.testing import BaseAPITest
38 039e3e61 Christos Stavrakakis
39 039e3e61 Christos Stavrakakis
from mock import patch
40 039e3e61 Christos Stavrakakis
from functools import wraps
41 039e3e61 Christos Stavrakakis
42 039e3e61 Christos Stavrakakis
43 039e3e61 Christos Stavrakakis
def assert_backend_closed(func):
44 039e3e61 Christos Stavrakakis
    """Decorator for ensuring that ImageBackend is returned to pool."""
45 039e3e61 Christos Stavrakakis
    @wraps(func)
46 039e3e61 Christos Stavrakakis
    def wrapper(self, backend):
47 039e3e61 Christos Stavrakakis
        result = func(self, backend)
48 039e3e61 Christos Stavrakakis
        if backend.called is True:
49 cda71050 Christos Stavrakakis
            backend.return_value.close.assert_called_once_with()
50 039e3e61 Christos Stavrakakis
        return result
51 039e3e61 Christos Stavrakakis
    return wrapper
52 039e3e61 Christos Stavrakakis
53 039e3e61 Christos Stavrakakis
54 cda71050 Christos Stavrakakis
@patch('synnefo.plankton.backend.ImageBackend')
55 039e3e61 Christos Stavrakakis
class ImageAPITest(BaseAPITest):
56 039e3e61 Christos Stavrakakis
    @assert_backend_closed
57 039e3e61 Christos Stavrakakis
    def test_create_image(self, mimage):
58 039e3e61 Christos Stavrakakis
        """Test that create image is not implemented"""
59 039e3e61 Christos Stavrakakis
        response = self.post('/api/v1.1/images/', 'user', json.dumps(''),
60 039e3e61 Christos Stavrakakis
                             'json')
61 bd40abfa Christos Stavrakakis
        self.assertEqual(response.status_code, 501)
62 039e3e61 Christos Stavrakakis
63 039e3e61 Christos Stavrakakis
    @assert_backend_closed
64 039e3e61 Christos Stavrakakis
    def test_list_images(self, mimage):
65 039e3e61 Christos Stavrakakis
        """Test that expected list of images is returned"""
66 039e3e61 Christos Stavrakakis
        images = [{'id': 1, 'name': 'image-1'},
67 039e3e61 Christos Stavrakakis
                  {'id': 2, 'name': 'image-2'},
68 039e3e61 Christos Stavrakakis
                  {'id': 3, 'name': 'image-3'}]
69 14c94c48 Christos Stavrakakis
        mimage().list_images.return_value = images
70 039e3e61 Christos Stavrakakis
        response = self.get('/api/v1.1/images/', 'user')
71 039e3e61 Christos Stavrakakis
        self.assertSuccess(response)
72 039e3e61 Christos Stavrakakis
        api_images = json.loads(response.content)['images']['values']
73 039e3e61 Christos Stavrakakis
        self.assertEqual(images, api_images)
74 039e3e61 Christos Stavrakakis
75 039e3e61 Christos Stavrakakis
    @assert_backend_closed
76 039e3e61 Christos Stavrakakis
    def test_list_images_detail(self, mimage):
77 039e3e61 Christos Stavrakakis
        images = [{'id': 1,
78 039e3e61 Christos Stavrakakis
                   'name': 'image-1',
79 039e3e61 Christos Stavrakakis
                   'status':'available',
80 039e3e61 Christos Stavrakakis
                   'created_at': '2012-11-26 11:52:54',
81 039e3e61 Christos Stavrakakis
                   'updated_at': '2012-12-26 11:52:54',
82 039e3e61 Christos Stavrakakis
                   'deleted_at': '',
83 039e3e61 Christos Stavrakakis
                   'properties': {'foo':'bar'}},
84 039e3e61 Christos Stavrakakis
                  {'id': 2,
85 039e3e61 Christos Stavrakakis
                   'name': 'image-2',
86 039e3e61 Christos Stavrakakis
                   'status': 'deleted',
87 039e3e61 Christos Stavrakakis
                   'created_at': '2012-11-26 11:52:54',
88 039e3e61 Christos Stavrakakis
                   'updated_at': '2012-12-26 11:52:54',
89 039e3e61 Christos Stavrakakis
                   'deleted_at': '2012-12-27 11:52:54',
90 039e3e61 Christos Stavrakakis
                   'properties': ''},
91 039e3e61 Christos Stavrakakis
                  {'id': 3,
92 039e3e61 Christos Stavrakakis
                   'name': 'image-3',
93 039e3e61 Christos Stavrakakis
                   'status': 'available',
94 039e3e61 Christos Stavrakakis
                   'created_at': '2012-11-26 11:52:54',
95 039e3e61 Christos Stavrakakis
                   'deleted_at': '',
96 039e3e61 Christos Stavrakakis
                   'updated_at': '2012-12-26 11:52:54',
97 039e3e61 Christos Stavrakakis
                   'properties': ''}]
98 039e3e61 Christos Stavrakakis
        result_images = [
99 039e3e61 Christos Stavrakakis
                  {'id': 1,
100 039e3e61 Christos Stavrakakis
                   'name': 'image-1',
101 039e3e61 Christos Stavrakakis
                   'status':'ACTIVE',
102 039e3e61 Christos Stavrakakis
                   'progress': 100,
103 039e3e61 Christos Stavrakakis
                   'created': '2012-11-26T11:52:54+00:00',
104 039e3e61 Christos Stavrakakis
                   'updated': '2012-12-26T11:52:54+00:00',
105 039e3e61 Christos Stavrakakis
                   'metadata': {'values': {'foo':'bar'}}},
106 039e3e61 Christos Stavrakakis
                  {'id': 2,
107 039e3e61 Christos Stavrakakis
                   'name': 'image-2',
108 039e3e61 Christos Stavrakakis
                   'status': 'DELETED',
109 039e3e61 Christos Stavrakakis
                   'progress': 0,
110 039e3e61 Christos Stavrakakis
                   'created': '2012-11-26T11:52:54+00:00',
111 039e3e61 Christos Stavrakakis
                   'updated': '2012-12-26T11:52:54+00:00'},
112 039e3e61 Christos Stavrakakis
                  {'id': 3,
113 039e3e61 Christos Stavrakakis
                   'name': 'image-3',
114 039e3e61 Christos Stavrakakis
                   'status': 'ACTIVE',
115 039e3e61 Christos Stavrakakis
                   'progress': 100,
116 039e3e61 Christos Stavrakakis
                   'created': '2012-11-26T11:52:54+00:00',
117 039e3e61 Christos Stavrakakis
                   'updated': '2012-12-26T11:52:54+00:00'}]
118 14c94c48 Christos Stavrakakis
        mimage().list_images.return_value = images
119 039e3e61 Christos Stavrakakis
        response = self.get('/api/v1.1/images/detail', 'user')
120 039e3e61 Christos Stavrakakis
        self.assertSuccess(response)
121 039e3e61 Christos Stavrakakis
        api_images = json.loads(response.content)['images']['values']
122 039e3e61 Christos Stavrakakis
        self.assertEqual(len(result_images), len(api_images))
123 039e3e61 Christos Stavrakakis
        self.assertEqual(result_images, api_images)
124 039e3e61 Christos Stavrakakis
125 039e3e61 Christos Stavrakakis
    @assert_backend_closed
126 039e3e61 Christos Stavrakakis
    def test_list_images_detail_since(self, mimage):
127 039e3e61 Christos Stavrakakis
        from datetime import datetime, timedelta
128 039e3e61 Christos Stavrakakis
        from time import sleep
129 039e3e61 Christos Stavrakakis
        old_time = datetime.now()
130 039e3e61 Christos Stavrakakis
        new_time = old_time + timedelta(seconds=0.1)
131 039e3e61 Christos Stavrakakis
        sleep(0.1)
132 039e3e61 Christos Stavrakakis
        images = [
133 039e3e61 Christos Stavrakakis
                  {'id': 1,
134 039e3e61 Christos Stavrakakis
                   'name': 'image-1',
135 039e3e61 Christos Stavrakakis
                   'status':'available',
136 039e3e61 Christos Stavrakakis
                   'progress': 100,
137 039e3e61 Christos Stavrakakis
                   'created_at': old_time.isoformat(),
138 039e3e61 Christos Stavrakakis
                   'deleted_at': '',
139 039e3e61 Christos Stavrakakis
                   'updated_at': old_time.isoformat(),
140 039e3e61 Christos Stavrakakis
                   'properties': ''},
141 039e3e61 Christos Stavrakakis
                  {'id': 2,
142 039e3e61 Christos Stavrakakis
                   'name': 'image-2',
143 039e3e61 Christos Stavrakakis
                   'status': 'deleted',
144 039e3e61 Christos Stavrakakis
                   'progress': 0,
145 039e3e61 Christos Stavrakakis
                   'created_at': new_time.isoformat(),
146 039e3e61 Christos Stavrakakis
                   'updated_at': new_time.isoformat(),
147 039e3e61 Christos Stavrakakis
                   'deleted_at': new_time.isoformat(),
148 039e3e61 Christos Stavrakakis
                   'properties': ''}]
149 14c94c48 Christos Stavrakakis
        mimage().list_images.return_value = images
150 039e3e61 Christos Stavrakakis
        response =\
151 039e3e61 Christos Stavrakakis
            self.get('/api/v1.1/images/detail?changes-since=%sUTC' % new_time)
152 039e3e61 Christos Stavrakakis
        self.assertSuccess(response)
153 039e3e61 Christos Stavrakakis
        api_images = json.loads(response.content)['images']['values']
154 039e3e61 Christos Stavrakakis
        self.assertEqual(1, len(api_images))
155 039e3e61 Christos Stavrakakis
156 039e3e61 Christos Stavrakakis
    @assert_backend_closed
157 039e3e61 Christos Stavrakakis
    def test_get_image_details(self, mimage):
158 039e3e61 Christos Stavrakakis
        image = {'id': 42,
159 039e3e61 Christos Stavrakakis
                 'name': 'image-1',
160 039e3e61 Christos Stavrakakis
                 'status': 'available',
161 039e3e61 Christos Stavrakakis
                 'created_at': '2012-11-26 11:52:54',
162 039e3e61 Christos Stavrakakis
                 'updated_at': '2012-12-26 11:52:54',
163 039e3e61 Christos Stavrakakis
                 'deleted_at': '',
164 039e3e61 Christos Stavrakakis
                 'properties': {'foo': 'bar'}}
165 039e3e61 Christos Stavrakakis
        result_image = \
166 039e3e61 Christos Stavrakakis
                  {'id': 42,
167 039e3e61 Christos Stavrakakis
                   'name': 'image-1',
168 039e3e61 Christos Stavrakakis
                   'status': 'ACTIVE',
169 039e3e61 Christos Stavrakakis
                   'progress': 100,
170 039e3e61 Christos Stavrakakis
                   'created': '2012-11-26T11:52:54+00:00',
171 039e3e61 Christos Stavrakakis
                   'updated': '2012-12-26T11:52:54+00:00',
172 039e3e61 Christos Stavrakakis
                   'metadata': {'values': {'foo': 'bar'}}}
173 cda71050 Christos Stavrakakis
        mimage.return_value.get_image.return_value = image
174 cda71050 Christos Stavrakakis
        response = self.get('/api/v1.1/images/42', 'user')
175 039e3e61 Christos Stavrakakis
        self.assertSuccess(response)
176 039e3e61 Christos Stavrakakis
        api_image = json.loads(response.content)['image']
177 039e3e61 Christos Stavrakakis
        self.assertEqual(api_image, result_image)
178 039e3e61 Christos Stavrakakis
179 039e3e61 Christos Stavrakakis
    @assert_backend_closed
180 039e3e61 Christos Stavrakakis
    def test_invalid_image(self, mimage):
181 cda71050 Christos Stavrakakis
        mimage.return_value.get_image.side_effect = faults.ItemNotFound('Image not found')
182 cda71050 Christos Stavrakakis
        response = self.get('/api/v1.1/images/42', 'user')
183 039e3e61 Christos Stavrakakis
        self.assertItemNotFound(response)
184 039e3e61 Christos Stavrakakis
185 cda71050 Christos Stavrakakis
    @assert_backend_closed
186 039e3e61 Christos Stavrakakis
    def test_delete_image(self, mimage):
187 039e3e61 Christos Stavrakakis
        response = self.delete("/api/v1.1/images/42", "user")
188 039e3e61 Christos Stavrakakis
        self.assertEqual(response.status_code, 204)
189 f13aab5d Christos Stavrakakis
        mimage.return_value.unregister.assert_called_once_with('42')
190 f13aab5d Christos Stavrakakis
        mimage.return_value._delete.assert_not_called('42')
191 039e3e61 Christos Stavrakakis
192 039e3e61 Christos Stavrakakis
193 cda71050 Christos Stavrakakis
@patch('synnefo.plankton.backend.ImageBackend')
194 039e3e61 Christos Stavrakakis
class ImageMetadataAPITest(BaseAPITest):
195 039e3e61 Christos Stavrakakis
    def setUp(self):
196 039e3e61 Christos Stavrakakis
        self.image = {'id': 42,
197 039e3e61 Christos Stavrakakis
                 'name': 'image-1',
198 039e3e61 Christos Stavrakakis
                 'status': 'available',
199 039e3e61 Christos Stavrakakis
                 'created_at': '2012-11-26 11:52:54',
200 039e3e61 Christos Stavrakakis
                 'updated_at': '2012-12-26 11:52:54',
201 039e3e61 Christos Stavrakakis
                 'deleted_at': '',
202 039e3e61 Christos Stavrakakis
                 'properties': {'foo': 'bar', 'foo2': 'bar2'}}
203 039e3e61 Christos Stavrakakis
        self.result_image = \
204 039e3e61 Christos Stavrakakis
                  {'id': 42,
205 039e3e61 Christos Stavrakakis
                   'name': 'image-1',
206 039e3e61 Christos Stavrakakis
                   'status': 'ACTIVE',
207 039e3e61 Christos Stavrakakis
                   'progress': 100,
208 039e3e61 Christos Stavrakakis
                   'created': '2012-11-26T11:52:54+00:00',
209 039e3e61 Christos Stavrakakis
                   'updated': '2012-12-26T11:52:54+00:00',
210 039e3e61 Christos Stavrakakis
                   'metadata': {'values': {'foo': 'bar'}}}
211 039e3e61 Christos Stavrakakis
212 039e3e61 Christos Stavrakakis
    @assert_backend_closed
213 039e3e61 Christos Stavrakakis
    def test_list_metadata(self, backend):
214 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
215 039e3e61 Christos Stavrakakis
        response = self.get('/api/v1.1/images/42/meta', 'user')
216 039e3e61 Christos Stavrakakis
        self.assertSuccess(response)
217 039e3e61 Christos Stavrakakis
        meta = json.loads(response.content)['metadata']['values']
218 039e3e61 Christos Stavrakakis
        self.assertEqual(meta, self.image['properties'])
219 039e3e61 Christos Stavrakakis
220 039e3e61 Christos Stavrakakis
    @assert_backend_closed
221 039e3e61 Christos Stavrakakis
    def test_get_metadata(self, backend):
222 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
223 039e3e61 Christos Stavrakakis
        response = self.get('/api/v1.1/images/42/meta/foo', 'user')
224 039e3e61 Christos Stavrakakis
        self.assertSuccess(response)
225 039e3e61 Christos Stavrakakis
        meta = json.loads(response.content)['meta']
226 039e3e61 Christos Stavrakakis
        self.assertEqual(meta['foo'], 'bar')
227 039e3e61 Christos Stavrakakis
228 039e3e61 Christos Stavrakakis
    @assert_backend_closed
229 039e3e61 Christos Stavrakakis
    def test_get_invalid_metadata(self, backend):
230 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
231 039e3e61 Christos Stavrakakis
        response = self.get('/api/v1.1/images/42/meta/not_found', 'user')
232 039e3e61 Christos Stavrakakis
        self.assertItemNotFound(response)
233 039e3e61 Christos Stavrakakis
234 039e3e61 Christos Stavrakakis
    def test_delete_metadata_item(self, backend):
235 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
236 b3fd98ae Christos Stavrakakis
        response = self.delete('/api/v1.1/images/42/meta/foo', 'user')
237 b3fd98ae Christos Stavrakakis
        self.assertEqual(response.status_code, 204)
238 14c94c48 Christos Stavrakakis
        backend.return_value.update_metadata.assert_called_once_with('42', {'properties': {'foo2':
239 b3fd98ae Christos Stavrakakis
                                                    'bar2'}})
240 039e3e61 Christos Stavrakakis
241 039e3e61 Christos Stavrakakis
    @assert_backend_closed
242 039e3e61 Christos Stavrakakis
    def test_create_metadata_item(self, backend):
243 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
244 b3fd98ae Christos Stavrakakis
        request = {'meta': {'foo3': 'bar3'}}
245 b3fd98ae Christos Stavrakakis
        response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
246 b3fd98ae Christos Stavrakakis
                            json.dumps(request), 'json')
247 b3fd98ae Christos Stavrakakis
        self.assertEqual(response.status_code, 201)
248 14c94c48 Christos Stavrakakis
        backend.return_value.update_metadata.assert_called_once_with('42',
249 b3fd98ae Christos Stavrakakis
                {'properties':
250 b3fd98ae Christos Stavrakakis
                    {'foo': 'bar', 'foo2': 'bar2', 'foo3': 'bar3'}})
251 039e3e61 Christos Stavrakakis
252 039e3e61 Christos Stavrakakis
    @assert_backend_closed
253 039e3e61 Christos Stavrakakis
    def test_create_metadata_malformed_1(self, backend):
254 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
255 b3fd98ae Christos Stavrakakis
        request = {'met': {'foo3': 'bar3'}}
256 b3fd98ae Christos Stavrakakis
        response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
257 b3fd98ae Christos Stavrakakis
                            json.dumps(request), 'json')
258 b3fd98ae Christos Stavrakakis
        self.assertBadRequest(response)
259 039e3e61 Christos Stavrakakis
260 039e3e61 Christos Stavrakakis
    @assert_backend_closed
261 039e3e61 Christos Stavrakakis
    def test_create_metadata_malformed_2(self, backend):
262 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
263 b3fd98ae Christos Stavrakakis
        request = {'meta': [('foo3', 'bar3')]}
264 b3fd98ae Christos Stavrakakis
        response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
265 b3fd98ae Christos Stavrakakis
                            json.dumps(request), 'json')
266 b3fd98ae Christos Stavrakakis
        self.assertBadRequest(response)
267 039e3e61 Christos Stavrakakis
268 039e3e61 Christos Stavrakakis
    @assert_backend_closed
269 039e3e61 Christos Stavrakakis
    def test_create_metadata_malformed_3(self, backend):
270 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
271 b3fd98ae Christos Stavrakakis
        request = {'met': {'foo3': 'bar3', 'foo4': 'bar4'}}
272 b3fd98ae Christos Stavrakakis
        response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
273 b3fd98ae Christos Stavrakakis
                                json.dumps(request), 'json')
274 b3fd98ae Christos Stavrakakis
        self.assertBadRequest(response)
275 039e3e61 Christos Stavrakakis
276 039e3e61 Christos Stavrakakis
    @assert_backend_closed
277 039e3e61 Christos Stavrakakis
    def test_create_metadata_malformed_4(self, backend):
278 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
279 b3fd98ae Christos Stavrakakis
        request = {'met': {'foo3': 'bar3'}}
280 b3fd98ae Christos Stavrakakis
        response = self.put('/api/v1.1/images/42/meta/foo4', 'user',
281 b3fd98ae Christos Stavrakakis
                                json.dumps(request), 'json')
282 b3fd98ae Christos Stavrakakis
        self.assertBadRequest(response)
283 039e3e61 Christos Stavrakakis
284 039e3e61 Christos Stavrakakis
    @assert_backend_closed
285 039e3e61 Christos Stavrakakis
    def test_update_metadata_item(self, backend):
286 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
287 b3fd98ae Christos Stavrakakis
        request = {'metadata': {'foo': 'bar_new', 'foo4': 'bar4'}}
288 b3fd98ae Christos Stavrakakis
        response = self.post('/api/v1.1/images/42/meta', 'user',
289 b3fd98ae Christos Stavrakakis
                             json.dumps(request), 'json')
290 b3fd98ae Christos Stavrakakis
        self.assertEqual(response.status_code, 201)
291 14c94c48 Christos Stavrakakis
        backend.return_value.update_metadata.assert_called_once_with('42',
292 b3fd98ae Christos Stavrakakis
                {'properties':
293 b3fd98ae Christos Stavrakakis
                    {'foo': 'bar_new', 'foo2': 'bar2', 'foo4': 'bar4'}
294 b3fd98ae Christos Stavrakakis
                })
295 039e3e61 Christos Stavrakakis
296 039e3e61 Christos Stavrakakis
    @assert_backend_closed
297 039e3e61 Christos Stavrakakis
    def test_update_metadata_malformed(self, backend):
298 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
299 b3fd98ae Christos Stavrakakis
        request = {'meta': {'foo': 'bar_new', 'foo4': 'bar4'}}
300 b3fd98ae Christos Stavrakakis
        response = self.post('/api/v1.1/images/42/meta', 'user',
301 b3fd98ae Christos Stavrakakis
                            json.dumps(request), 'json')
302 b3fd98ae Christos Stavrakakis
        self.assertBadRequest(response)