Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (13.4 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.unregister.assert_called_once_with('42')
191
        mimage.return_value._delete.assert_not_called('42')
192

    
193

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

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

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

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

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

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

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

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

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

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

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

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