Statistics
| Branch: | Tag: | Revision:

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

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 b3fd98ae Christos Stavrakakis
            num = len(backend.mock_calls) / 2
50 b3fd98ae Christos Stavrakakis
            assert(len(backend.return_value.close.mock_calls), num)
51 039e3e61 Christos Stavrakakis
        return result
52 039e3e61 Christos Stavrakakis
    return wrapper
53 039e3e61 Christos Stavrakakis
54 039e3e61 Christos Stavrakakis
55 b3fd98ae Christos Stavrakakis
@patch('synnefo.plankton.utils.ImageBackend')
56 039e3e61 Christos Stavrakakis
class ImageAPITest(BaseAPITest):
57 039e3e61 Christos Stavrakakis
    @assert_backend_closed
58 039e3e61 Christos Stavrakakis
    def test_create_image(self, mimage):
59 039e3e61 Christos Stavrakakis
        """Test that create image is not implemented"""
60 039e3e61 Christos Stavrakakis
        response = self.post('/api/v1.1/images/', 'user', json.dumps(''),
61 039e3e61 Christos Stavrakakis
                             'json')
62 bd40abfa Christos Stavrakakis
        self.assertEqual(response.status_code, 501)
63 039e3e61 Christos Stavrakakis
64 039e3e61 Christos Stavrakakis
    @assert_backend_closed
65 039e3e61 Christos Stavrakakis
    def test_list_images(self, mimage):
66 039e3e61 Christos Stavrakakis
        """Test that expected list of images is returned"""
67 039e3e61 Christos Stavrakakis
        images = [{'id': 1, 'name': 'image-1'},
68 039e3e61 Christos Stavrakakis
                  {'id': 2, 'name': 'image-2'},
69 039e3e61 Christos Stavrakakis
                  {'id': 3, 'name': 'image-3'}]
70 039e3e61 Christos Stavrakakis
        mimage().list.return_value = images
71 039e3e61 Christos Stavrakakis
        response = self.get('/api/v1.1/images/', 'user')
72 039e3e61 Christos Stavrakakis
        self.assertSuccess(response)
73 039e3e61 Christos Stavrakakis
        api_images = json.loads(response.content)['images']['values']
74 039e3e61 Christos Stavrakakis
        self.assertEqual(images, api_images)
75 039e3e61 Christos Stavrakakis
76 039e3e61 Christos Stavrakakis
    @assert_backend_closed
77 039e3e61 Christos Stavrakakis
    def test_list_images_detail(self, mimage):
78 039e3e61 Christos Stavrakakis
        images = [{'id': 1,
79 039e3e61 Christos Stavrakakis
                   'name': 'image-1',
80 039e3e61 Christos Stavrakakis
                   'status':'available',
81 039e3e61 Christos Stavrakakis
                   'created_at': '2012-11-26 11:52:54',
82 039e3e61 Christos Stavrakakis
                   'updated_at': '2012-12-26 11:52:54',
83 039e3e61 Christos Stavrakakis
                   'deleted_at': '',
84 039e3e61 Christos Stavrakakis
                   'properties': {'foo':'bar'}},
85 039e3e61 Christos Stavrakakis
                  {'id': 2,
86 039e3e61 Christos Stavrakakis
                   'name': 'image-2',
87 039e3e61 Christos Stavrakakis
                   'status': 'deleted',
88 039e3e61 Christos Stavrakakis
                   'created_at': '2012-11-26 11:52:54',
89 039e3e61 Christos Stavrakakis
                   'updated_at': '2012-12-26 11:52:54',
90 039e3e61 Christos Stavrakakis
                   'deleted_at': '2012-12-27 11:52:54',
91 039e3e61 Christos Stavrakakis
                   'properties': ''},
92 039e3e61 Christos Stavrakakis
                  {'id': 3,
93 039e3e61 Christos Stavrakakis
                   'name': 'image-3',
94 039e3e61 Christos Stavrakakis
                   'status': 'available',
95 039e3e61 Christos Stavrakakis
                   'created_at': '2012-11-26 11:52:54',
96 039e3e61 Christos Stavrakakis
                   'deleted_at': '',
97 039e3e61 Christos Stavrakakis
                   'updated_at': '2012-12-26 11:52:54',
98 039e3e61 Christos Stavrakakis
                   'properties': ''}]
99 039e3e61 Christos Stavrakakis
        result_images = [
100 039e3e61 Christos Stavrakakis
                  {'id': 1,
101 039e3e61 Christos Stavrakakis
                   'name': 'image-1',
102 039e3e61 Christos Stavrakakis
                   'status':'ACTIVE',
103 039e3e61 Christos Stavrakakis
                   'progress': 100,
104 039e3e61 Christos Stavrakakis
                   'created': '2012-11-26T11:52:54+00:00',
105 039e3e61 Christos Stavrakakis
                   'updated': '2012-12-26T11:52:54+00:00',
106 039e3e61 Christos Stavrakakis
                   'metadata': {'values': {'foo':'bar'}}},
107 039e3e61 Christos Stavrakakis
                  {'id': 2,
108 039e3e61 Christos Stavrakakis
                   'name': 'image-2',
109 039e3e61 Christos Stavrakakis
                   'status': 'DELETED',
110 039e3e61 Christos Stavrakakis
                   'progress': 0,
111 039e3e61 Christos Stavrakakis
                   'created': '2012-11-26T11:52:54+00:00',
112 039e3e61 Christos Stavrakakis
                   'updated': '2012-12-26T11:52:54+00:00'},
113 039e3e61 Christos Stavrakakis
                  {'id': 3,
114 039e3e61 Christos Stavrakakis
                   'name': 'image-3',
115 039e3e61 Christos Stavrakakis
                   'status': 'ACTIVE',
116 039e3e61 Christos Stavrakakis
                   'progress': 100,
117 039e3e61 Christos Stavrakakis
                   'created': '2012-11-26T11:52:54+00:00',
118 039e3e61 Christos Stavrakakis
                   'updated': '2012-12-26T11:52:54+00:00'}]
119 039e3e61 Christos Stavrakakis
        mimage().list.return_value = images
120 039e3e61 Christos Stavrakakis
        response = self.get('/api/v1.1/images/detail', 'user')
121 039e3e61 Christos Stavrakakis
        self.assertSuccess(response)
122 039e3e61 Christos Stavrakakis
        api_images = json.loads(response.content)['images']['values']
123 039e3e61 Christos Stavrakakis
        self.assertEqual(len(result_images), len(api_images))
124 039e3e61 Christos Stavrakakis
        self.assertEqual(result_images, api_images)
125 039e3e61 Christos Stavrakakis
126 039e3e61 Christos Stavrakakis
    @assert_backend_closed
127 039e3e61 Christos Stavrakakis
    def test_list_images_detail_since(self, mimage):
128 039e3e61 Christos Stavrakakis
        from datetime import datetime, timedelta
129 039e3e61 Christos Stavrakakis
        from time import sleep
130 039e3e61 Christos Stavrakakis
        old_time = datetime.now()
131 039e3e61 Christos Stavrakakis
        new_time = old_time + timedelta(seconds=0.1)
132 039e3e61 Christos Stavrakakis
        sleep(0.1)
133 039e3e61 Christos Stavrakakis
        images = [
134 039e3e61 Christos Stavrakakis
                  {'id': 1,
135 039e3e61 Christos Stavrakakis
                   'name': 'image-1',
136 039e3e61 Christos Stavrakakis
                   'status':'available',
137 039e3e61 Christos Stavrakakis
                   'progress': 100,
138 039e3e61 Christos Stavrakakis
                   'created_at': old_time.isoformat(),
139 039e3e61 Christos Stavrakakis
                   'deleted_at': '',
140 039e3e61 Christos Stavrakakis
                   'updated_at': old_time.isoformat(),
141 039e3e61 Christos Stavrakakis
                   'properties': ''},
142 039e3e61 Christos Stavrakakis
                  {'id': 2,
143 039e3e61 Christos Stavrakakis
                   'name': 'image-2',
144 039e3e61 Christos Stavrakakis
                   'status': 'deleted',
145 039e3e61 Christos Stavrakakis
                   'progress': 0,
146 039e3e61 Christos Stavrakakis
                   'created_at': new_time.isoformat(),
147 039e3e61 Christos Stavrakakis
                   'updated_at': new_time.isoformat(),
148 039e3e61 Christos Stavrakakis
                   'deleted_at': new_time.isoformat(),
149 039e3e61 Christos Stavrakakis
                   'properties': ''}]
150 039e3e61 Christos Stavrakakis
        mimage().iter.return_value = images
151 039e3e61 Christos Stavrakakis
        response =\
152 039e3e61 Christos Stavrakakis
            self.get('/api/v1.1/images/detail?changes-since=%sUTC' % new_time)
153 039e3e61 Christos Stavrakakis
        self.assertSuccess(response)
154 039e3e61 Christos Stavrakakis
        api_images = json.loads(response.content)['images']['values']
155 039e3e61 Christos Stavrakakis
        self.assertEqual(1, len(api_images))
156 039e3e61 Christos Stavrakakis
157 039e3e61 Christos Stavrakakis
    @assert_backend_closed
158 039e3e61 Christos Stavrakakis
    def test_get_image_details(self, mimage):
159 039e3e61 Christos Stavrakakis
        image = {'id': 42,
160 039e3e61 Christos Stavrakakis
                 'name': 'image-1',
161 039e3e61 Christos Stavrakakis
                 'status': 'available',
162 039e3e61 Christos Stavrakakis
                 'created_at': '2012-11-26 11:52:54',
163 039e3e61 Christos Stavrakakis
                 'updated_at': '2012-12-26 11:52:54',
164 039e3e61 Christos Stavrakakis
                 'deleted_at': '',
165 039e3e61 Christos Stavrakakis
                 'properties': {'foo': 'bar'}}
166 039e3e61 Christos Stavrakakis
        result_image = \
167 039e3e61 Christos Stavrakakis
                  {'id': 42,
168 039e3e61 Christos Stavrakakis
                   'name': 'image-1',
169 039e3e61 Christos Stavrakakis
                   'status': 'ACTIVE',
170 039e3e61 Christos Stavrakakis
                   'progress': 100,
171 039e3e61 Christos Stavrakakis
                   'created': '2012-11-26T11:52:54+00:00',
172 039e3e61 Christos Stavrakakis
                   'updated': '2012-12-26T11:52:54+00:00',
173 039e3e61 Christos Stavrakakis
                   'metadata': {'values': {'foo': 'bar'}}}
174 039e3e61 Christos Stavrakakis
        with patch('synnefo.api.util.get_image') as m:
175 039e3e61 Christos Stavrakakis
            m.return_value = image
176 039e3e61 Christos Stavrakakis
            response = self.get('/api/v1.1/images/42', 'user')
177 039e3e61 Christos Stavrakakis
        self.assertSuccess(response)
178 039e3e61 Christos Stavrakakis
        api_image = json.loads(response.content)['image']
179 039e3e61 Christos Stavrakakis
        self.assertEqual(api_image, result_image)
180 039e3e61 Christos Stavrakakis
181 039e3e61 Christos Stavrakakis
    @assert_backend_closed
182 039e3e61 Christos Stavrakakis
    def test_invalid_image(self, mimage):
183 039e3e61 Christos Stavrakakis
        with patch('synnefo.api.util.get_image') as m:
184 bd40abfa Christos Stavrakakis
            m.side_effect = faults.ItemNotFound('Image not found')
185 039e3e61 Christos Stavrakakis
            response = self.get('/api/v1.1/images/42', 'user')
186 039e3e61 Christos Stavrakakis
        self.assertItemNotFound(response)
187 039e3e61 Christos Stavrakakis
188 039e3e61 Christos Stavrakakis
    def test_delete_image(self, mimage):
189 039e3e61 Christos Stavrakakis
        response = self.delete("/api/v1.1/images/42", "user")
190 039e3e61 Christos Stavrakakis
        self.assertEqual(response.status_code, 204)
191 f13aab5d Christos Stavrakakis
        mimage.return_value.unregister.assert_called_once_with('42')
192 f13aab5d Christos Stavrakakis
        mimage.return_value._delete.assert_not_called('42')
193 039e3e61 Christos Stavrakakis
194 039e3e61 Christos Stavrakakis
195 b3fd98ae Christos Stavrakakis
@patch('synnefo.plankton.utils.ImageBackend')
196 039e3e61 Christos Stavrakakis
class ImageMetadataAPITest(BaseAPITest):
197 039e3e61 Christos Stavrakakis
    def setUp(self):
198 039e3e61 Christos Stavrakakis
        self.image = {'id': 42,
199 039e3e61 Christos Stavrakakis
                 'name': 'image-1',
200 039e3e61 Christos Stavrakakis
                 'status': 'available',
201 039e3e61 Christos Stavrakakis
                 'created_at': '2012-11-26 11:52:54',
202 039e3e61 Christos Stavrakakis
                 'updated_at': '2012-12-26 11:52:54',
203 039e3e61 Christos Stavrakakis
                 'deleted_at': '',
204 039e3e61 Christos Stavrakakis
                 'properties': {'foo': 'bar', 'foo2': 'bar2'}}
205 039e3e61 Christos Stavrakakis
        self.result_image = \
206 039e3e61 Christos Stavrakakis
                  {'id': 42,
207 039e3e61 Christos Stavrakakis
                   'name': 'image-1',
208 039e3e61 Christos Stavrakakis
                   'status': 'ACTIVE',
209 039e3e61 Christos Stavrakakis
                   'progress': 100,
210 039e3e61 Christos Stavrakakis
                   'created': '2012-11-26T11:52:54+00:00',
211 039e3e61 Christos Stavrakakis
                   'updated': '2012-12-26T11:52:54+00:00',
212 039e3e61 Christos Stavrakakis
                   'metadata': {'values': {'foo': 'bar'}}}
213 039e3e61 Christos Stavrakakis
214 039e3e61 Christos Stavrakakis
    @assert_backend_closed
215 039e3e61 Christos Stavrakakis
    def test_list_metadata(self, backend):
216 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
217 039e3e61 Christos Stavrakakis
        response = self.get('/api/v1.1/images/42/meta', 'user')
218 039e3e61 Christos Stavrakakis
        self.assertSuccess(response)
219 039e3e61 Christos Stavrakakis
        meta = json.loads(response.content)['metadata']['values']
220 039e3e61 Christos Stavrakakis
        self.assertEqual(meta, self.image['properties'])
221 039e3e61 Christos Stavrakakis
222 039e3e61 Christos Stavrakakis
    @assert_backend_closed
223 039e3e61 Christos Stavrakakis
    def test_get_metadata(self, backend):
224 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
225 039e3e61 Christos Stavrakakis
        response = self.get('/api/v1.1/images/42/meta/foo', 'user')
226 039e3e61 Christos Stavrakakis
        self.assertSuccess(response)
227 039e3e61 Christos Stavrakakis
        meta = json.loads(response.content)['meta']
228 039e3e61 Christos Stavrakakis
        self.assertEqual(meta['foo'], 'bar')
229 039e3e61 Christos Stavrakakis
230 039e3e61 Christos Stavrakakis
    @assert_backend_closed
231 039e3e61 Christos Stavrakakis
    def test_get_invalid_metadata(self, backend):
232 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
233 039e3e61 Christos Stavrakakis
        response = self.get('/api/v1.1/images/42/meta/not_found', 'user')
234 039e3e61 Christos Stavrakakis
        self.assertItemNotFound(response)
235 039e3e61 Christos Stavrakakis
236 039e3e61 Christos Stavrakakis
    def test_delete_metadata_item(self, backend):
237 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
238 b3fd98ae Christos Stavrakakis
        response = self.delete('/api/v1.1/images/42/meta/foo', 'user')
239 b3fd98ae Christos Stavrakakis
        self.assertEqual(response.status_code, 204)
240 b3fd98ae Christos Stavrakakis
        backend.return_value.update.assert_called_once_with('42', {'properties': {'foo2':
241 b3fd98ae Christos Stavrakakis
                                                    'bar2'}})
242 039e3e61 Christos Stavrakakis
243 039e3e61 Christos Stavrakakis
    @assert_backend_closed
244 039e3e61 Christos Stavrakakis
    def test_create_metadata_item(self, backend):
245 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
246 b3fd98ae Christos Stavrakakis
        request = {'meta': {'foo3': 'bar3'}}
247 b3fd98ae Christos Stavrakakis
        response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
248 b3fd98ae Christos Stavrakakis
                            json.dumps(request), 'json')
249 b3fd98ae Christos Stavrakakis
        self.assertEqual(response.status_code, 201)
250 b3fd98ae Christos Stavrakakis
        backend.return_value.update.assert_called_once_with('42',
251 b3fd98ae Christos Stavrakakis
                {'properties':
252 b3fd98ae Christos Stavrakakis
                    {'foo': 'bar', 'foo2': 'bar2', 'foo3': 'bar3'}})
253 039e3e61 Christos Stavrakakis
254 039e3e61 Christos Stavrakakis
    @assert_backend_closed
255 039e3e61 Christos Stavrakakis
    def test_create_metadata_malformed_1(self, backend):
256 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
257 b3fd98ae Christos Stavrakakis
        request = {'met': {'foo3': 'bar3'}}
258 b3fd98ae Christos Stavrakakis
        response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
259 b3fd98ae Christos Stavrakakis
                            json.dumps(request), 'json')
260 b3fd98ae Christos Stavrakakis
        self.assertBadRequest(response)
261 039e3e61 Christos Stavrakakis
262 039e3e61 Christos Stavrakakis
    @assert_backend_closed
263 039e3e61 Christos Stavrakakis
    def test_create_metadata_malformed_2(self, backend):
264 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
265 b3fd98ae Christos Stavrakakis
        request = {'meta': [('foo3', 'bar3')]}
266 b3fd98ae Christos Stavrakakis
        response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
267 b3fd98ae Christos Stavrakakis
                            json.dumps(request), 'json')
268 b3fd98ae Christos Stavrakakis
        self.assertBadRequest(response)
269 039e3e61 Christos Stavrakakis
270 039e3e61 Christos Stavrakakis
    @assert_backend_closed
271 039e3e61 Christos Stavrakakis
    def test_create_metadata_malformed_3(self, backend):
272 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
273 b3fd98ae Christos Stavrakakis
        request = {'met': {'foo3': 'bar3', 'foo4': 'bar4'}}
274 b3fd98ae Christos Stavrakakis
        response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
275 b3fd98ae Christos Stavrakakis
                                json.dumps(request), 'json')
276 b3fd98ae Christos Stavrakakis
        self.assertBadRequest(response)
277 039e3e61 Christos Stavrakakis
278 039e3e61 Christos Stavrakakis
    @assert_backend_closed
279 039e3e61 Christos Stavrakakis
    def test_create_metadata_malformed_4(self, backend):
280 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
281 b3fd98ae Christos Stavrakakis
        request = {'met': {'foo3': 'bar3'}}
282 b3fd98ae Christos Stavrakakis
        response = self.put('/api/v1.1/images/42/meta/foo4', 'user',
283 b3fd98ae Christos Stavrakakis
                                json.dumps(request), 'json')
284 b3fd98ae Christos Stavrakakis
        self.assertBadRequest(response)
285 039e3e61 Christos Stavrakakis
286 039e3e61 Christos Stavrakakis
    @assert_backend_closed
287 039e3e61 Christos Stavrakakis
    def test_update_metadata_item(self, backend):
288 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
289 b3fd98ae Christos Stavrakakis
        request = {'metadata': {'foo': 'bar_new', 'foo4': 'bar4'}}
290 b3fd98ae Christos Stavrakakis
        response = self.post('/api/v1.1/images/42/meta', 'user',
291 b3fd98ae Christos Stavrakakis
                             json.dumps(request), 'json')
292 b3fd98ae Christos Stavrakakis
        self.assertEqual(response.status_code, 201)
293 b3fd98ae Christos Stavrakakis
        backend.return_value.update.assert_called_once_with('42',
294 b3fd98ae Christos Stavrakakis
                {'properties':
295 b3fd98ae Christos Stavrakakis
                    {'foo': 'bar_new', 'foo2': 'bar2', 'foo4': 'bar4'}
296 b3fd98ae Christos Stavrakakis
                })
297 039e3e61 Christos Stavrakakis
298 039e3e61 Christos Stavrakakis
    @assert_backend_closed
299 039e3e61 Christos Stavrakakis
    def test_update_metadata_malformed(self, backend):
300 039e3e61 Christos Stavrakakis
        backend.return_value.get_image.return_value = self.image
301 b3fd98ae Christos Stavrakakis
        request = {'meta': {'foo': 'bar_new', 'foo4': 'bar4'}}
302 b3fd98ae Christos Stavrakakis
        response = self.post('/api/v1.1/images/42/meta', 'user',
303 b3fd98ae Christos Stavrakakis
                            json.dumps(request), 'json')
304 b3fd98ae Christos Stavrakakis
        self.assertBadRequest(response)