Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (13.3 kB)

1
# Copyright 2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
import json
35

    
36
from synnefo.api.tests import BaseAPITest
37
from synnefo.api.faults import ItemNotFound
38

    
39
from mock import patch
40
from functools import wraps
41

    
42

    
43
def assert_backend_closed(func):
44
    """Decorator for ensuring that ImageBackend is returned to pool."""
45
    @wraps(func)
46
    def wrapper(self, backend):
47
        result = func(self, backend)
48
        if backend.called is True:
49
            backend.return_value.close.assert_called_once_with()
50
        return result
51
    return wrapper
52

    
53

    
54
@patch('synnefo.api.images.ImageBackend')
55
class ImageAPITest(BaseAPITest):
56
    @assert_backend_closed
57
    def test_create_image(self, mimage):
58
        """Test that create image is not implemented"""
59
        response = self.post('/api/v1.1/images/', 'user', json.dumps(''),
60
                             'json')
61
        self.assertEqual(response.status_code, 503)
62

    
63
    @assert_backend_closed
64
    def test_list_images(self, mimage):
65
        """Test that expected list of images is returned"""
66
        images = [{'id': 1, 'name': 'image-1'},
67
                  {'id': 2, 'name': 'image-2'},
68
                  {'id': 3, 'name': 'image-3'}]
69
        mimage().list.return_value = images
70
        response = self.get('/api/v1.1/images/', 'user')
71
        self.assertSuccess(response)
72
        api_images = json.loads(response.content)['images']['values']
73
        self.assertEqual(images, api_images)
74

    
75
    @assert_backend_closed
76
    def test_list_images_detail(self, mimage):
77
        images = [{'id': 1,
78
                   'name': 'image-1',
79
                   'status':'available',
80
                   'created_at': '2012-11-26 11:52:54',
81
                   'updated_at': '2012-12-26 11:52:54',
82
                   'deleted_at': '',
83
                   'properties': {'foo':'bar'}},
84
                  {'id': 2,
85
                   'name': 'image-2',
86
                   'status': 'deleted',
87
                   'created_at': '2012-11-26 11:52:54',
88
                   'updated_at': '2012-12-26 11:52:54',
89
                   'deleted_at': '2012-12-27 11:52:54',
90
                   'properties': ''},
91
                  {'id': 3,
92
                   'name': 'image-3',
93
                   'status': 'available',
94
                   'created_at': '2012-11-26 11:52:54',
95
                   'deleted_at': '',
96
                   'updated_at': '2012-12-26 11:52:54',
97
                   'properties': ''}]
98
        result_images = [
99
                  {'id': 1,
100
                   'name': 'image-1',
101
                   'status':'ACTIVE',
102
                   'progress': 100,
103
                   'created': '2012-11-26T11:52:54+00:00',
104
                   'updated': '2012-12-26T11:52:54+00:00',
105
                   'metadata': {'values': {'foo':'bar'}}},
106
                  {'id': 2,
107
                   'name': 'image-2',
108
                   'status': 'DELETED',
109
                   'progress': 0,
110
                   'created': '2012-11-26T11:52:54+00:00',
111
                   'updated': '2012-12-26T11:52:54+00:00'},
112
                  {'id': 3,
113
                   'name': 'image-3',
114
                   'status': 'ACTIVE',
115
                   'progress': 100,
116
                   'created': '2012-11-26T11:52:54+00:00',
117
                   'updated': '2012-12-26T11:52:54+00:00'}]
118
        mimage().list.return_value = images
119
        response = self.get('/api/v1.1/images/detail', 'user')
120
        self.assertSuccess(response)
121
        api_images = json.loads(response.content)['images']['values']
122
        self.assertEqual(len(result_images), len(api_images))
123
        self.assertEqual(result_images, api_images)
124

    
125
    @assert_backend_closed
126
    def test_list_images_detail_since(self, mimage):
127
        from datetime import datetime, timedelta
128
        from time import sleep
129
        old_time = datetime.now()
130
        new_time = old_time + timedelta(seconds=0.1)
131
        sleep(0.1)
132
        images = [
133
                  {'id': 1,
134
                   'name': 'image-1',
135
                   'status':'available',
136
                   'progress': 100,
137
                   'created_at': old_time.isoformat(),
138
                   'deleted_at': '',
139
                   'updated_at': old_time.isoformat(),
140
                   'properties': ''},
141
                  {'id': 2,
142
                   'name': 'image-2',
143
                   'status': 'deleted',
144
                   'progress': 0,
145
                   'created_at': new_time.isoformat(),
146
                   'updated_at': new_time.isoformat(),
147
                   'deleted_at': new_time.isoformat(),
148
                   'properties': ''}]
149
        mimage().iter.return_value = images
150
        response =\
151
            self.get('/api/v1.1/images/detail?changes-since=%sUTC' % new_time)
152
        self.assertSuccess(response)
153
        api_images = json.loads(response.content)['images']['values']
154
        self.assertEqual(1, len(api_images))
155

    
156
    @assert_backend_closed
157
    def test_get_image_details(self, mimage):
158
        image = {'id': 42,
159
                 'name': 'image-1',
160
                 'status': 'available',
161
                 'created_at': '2012-11-26 11:52:54',
162
                 'updated_at': '2012-12-26 11:52:54',
163
                 'deleted_at': '',
164
                 'properties': {'foo': 'bar'}}
165
        result_image = \
166
                  {'id': 42,
167
                   'name': 'image-1',
168
                   'status': 'ACTIVE',
169
                   'progress': 100,
170
                   'created': '2012-11-26T11:52:54+00:00',
171
                   'updated': '2012-12-26T11:52:54+00:00',
172
                   'metadata': {'values': {'foo': 'bar'}}}
173
        with patch('synnefo.api.util.get_image') as m:
174
            m.return_value = image
175
            response = self.get('/api/v1.1/images/42', 'user')
176
        self.assertSuccess(response)
177
        api_image = json.loads(response.content)['image']
178
        self.assertEqual(api_image, result_image)
179

    
180
    @assert_backend_closed
181
    def test_invalid_image(self, mimage):
182
        with patch('synnefo.api.util.get_image') as m:
183
            m.side_effect = ItemNotFound('Image not found')
184
            response = self.get('/api/v1.1/images/42', 'user')
185
        self.assertItemNotFound(response)
186

    
187
    def test_delete_image(self, mimage):
188
        response = self.delete("/api/v1.1/images/42", "user")
189
        self.assertEqual(response.status_code, 204)
190
        mimage.return_value.delete.assert_called_once_with('42')
191

    
192

    
193
@patch('synnefo.api.util.ImageBackend')
194
class ImageMetadataAPITest(BaseAPITest):
195
    def setUp(self):
196
        self.image = {'id': 42,
197
                 'name': 'image-1',
198
                 'status': 'available',
199
                 'created_at': '2012-11-26 11:52:54',
200
                 'updated_at': '2012-12-26 11:52:54',
201
                 'deleted_at': '',
202
                 'properties': {'foo': 'bar', 'foo2': 'bar2'}}
203
        self.result_image = \
204
                  {'id': 42,
205
                   'name': 'image-1',
206
                   'status': 'ACTIVE',
207
                   'progress': 100,
208
                   'created': '2012-11-26T11:52:54+00:00',
209
                   'updated': '2012-12-26T11:52:54+00:00',
210
                   'metadata': {'values': {'foo': 'bar'}}}
211

    
212
    @assert_backend_closed
213
    def test_list_metadata(self, backend):
214
        backend.return_value.get_image.return_value = self.image
215
        response = self.get('/api/v1.1/images/42/meta', 'user')
216
        self.assertSuccess(response)
217
        meta = json.loads(response.content)['metadata']['values']
218
        self.assertEqual(meta, self.image['properties'])
219

    
220
    @assert_backend_closed
221
    def test_get_metadata(self, backend):
222
        backend.return_value.get_image.return_value = self.image
223
        response = self.get('/api/v1.1/images/42/meta/foo', 'user')
224
        self.assertSuccess(response)
225
        meta = json.loads(response.content)['meta']
226
        self.assertEqual(meta['foo'], 'bar')
227

    
228
    @assert_backend_closed
229
    def test_get_invalid_metadata(self, backend):
230
        backend.return_value.get_image.return_value = self.image
231
        response = self.get('/api/v1.1/images/42/meta/not_found', 'user')
232
        self.assertItemNotFound(response)
233

    
234
    @assert_backend_closed
235
    def test_delete_metadata_item(self, backend):
236
        backend.return_value.get_image.return_value = self.image
237
        with patch("synnefo.api.images.ImageBackend") as m:
238
            response = self.delete('/api/v1.1/images/42/meta/foo', 'user')
239
            self.assertEqual(response.status_code, 204)
240
            m.return_value.update.assert_called_once_with('42',
241
                                        {'properties': {'foo2': 'bar2'}})
242

    
243
    @assert_backend_closed
244
    def test_create_metadata_item(self, backend):
245
        backend.return_value.get_image.return_value = self.image
246
        with patch("synnefo.api.images.ImageBackend") as m:
247
                request = {'meta': {'foo3': 'bar3'}}
248
                response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
249
                                    json.dumps(request), 'json')
250
                self.assertEqual(response.status_code, 201)
251
                m.return_value.update.assert_called_once_with('42',
252
                        {'properties':
253
                            {'foo': 'bar', 'foo2': 'bar2', 'foo3': 'bar3'}})
254

    
255
    @assert_backend_closed
256
    def test_create_metadata_malformed_1(self, backend):
257
        backend.return_value.get_image.return_value = self.image
258
        with patch("synnefo.api.images.ImageBackend"):
259
                request = {'met': {'foo3': 'bar3'}}
260
                response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
261
                                    json.dumps(request), 'json')
262
                self.assertBadRequest(response)
263

    
264
    @assert_backend_closed
265
    def test_create_metadata_malformed_2(self, backend):
266
        backend.return_value.get_image.return_value = self.image
267
        with patch("synnefo.api.images.ImageBackend"):
268
                request = {'meta': [('foo3', 'bar3')]}
269
                response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
270
                                    json.dumps(request), 'json')
271
                self.assertBadRequest(response)
272

    
273
    @assert_backend_closed
274
    def test_create_metadata_malformed_3(self, backend):
275
        backend.return_value.get_image.return_value = self.image
276
        with patch("synnefo.api.images.ImageBackend"):
277
            request = {'met': {'foo3': 'bar3', 'foo4': 'bar4'}}
278
            response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
279
                                    json.dumps(request), 'json')
280
            self.assertBadRequest(response)
281

    
282
    @assert_backend_closed
283
    def test_create_metadata_malformed_4(self, backend):
284
        backend.return_value.get_image.return_value = self.image
285
        with patch("synnefo.api.images.ImageBackend"):
286
            request = {'met': {'foo3': 'bar3'}}
287
            response = self.put('/api/v1.1/images/42/meta/foo4', 'user',
288
                                    json.dumps(request), 'json')
289
            self.assertBadRequest(response)
290

    
291
    @assert_backend_closed
292
    def test_update_metadata_item(self, backend):
293
        backend.return_value.get_image.return_value = self.image
294
        with patch("synnefo.api.images.ImageBackend") as m:
295
                request = {'metadata': {'foo': 'bar_new', 'foo4': 'bar4'}}
296
                response = self.post('/api/v1.1/images/42/meta', 'user',
297
                                    json.dumps(request), 'json')
298
                self.assertEqual(response.status_code, 201)
299
                m.return_value.update.assert_called_once_with('42',
300
                        {'properties':
301
                            {'foo': 'bar_new', 'foo2': 'bar2', 'foo4': 'bar4'}
302
                        })
303

    
304
    @assert_backend_closed
305
    def test_update_metadata_malformed(self, backend):
306
        backend.return_value.get_image.return_value = self.image
307
        with patch("synnefo.api.images.ImageBackend"):
308
                request = {'meta': {'foo': 'bar_new', 'foo4': 'bar4'}}
309
                response = self.post('/api/v1.1/images/42/meta', 'user',
310
                                    json.dumps(request), 'json')
311
                self.assertBadRequest(response)