Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / plankton / tests.py @ d9d1763e

History | View | Annotate | Download (11.6 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 django.test import TestCase
37

    
38
from contextlib import contextmanager
39
from mock import patch
40
from functools import wraps
41
from copy import deepcopy
42

    
43

    
44
FILTERS = ('name', 'container_format', 'disk_format', 'status', 'size_min',
45
           'size_max')
46
PARAMS = ('sort_key', 'sort_dir')
47
SORT_KEY_OPTIONS = ('id', 'name', 'status', 'size', 'disk_format',
48
                    'container_format', 'created_at', 'updated_at')
49
SORT_DIR_OPTIONS = ('asc', 'desc')
50
LIST_FIELDS = ('status', 'name', 'disk_format', 'container_format', 'size',
51
               'id')
52
DETAIL_FIELDS = ('name', 'disk_format', 'container_format', 'size', 'checksum',
53
                 'location', 'created_at', 'updated_at', 'deleted_at',
54
                 'status', 'is_public', 'owner', 'properties', 'id')
55
ADD_FIELDS = ('name', 'id', 'store', 'disk_format', 'container_format', 'size',
56
              'checksum', 'is_public', 'owner', 'properties', 'location')
57
UPDATE_FIELDS = ('name', 'disk_format', 'container_format', 'is_public',
58
                 'owner', 'properties', 'status')
59

    
60

    
61
@contextmanager
62
def astakos_user(user):
63
    """
64
    Context manager to mock astakos response.
65

66
    usage:
67
    with astakos_user("user@user.com"):
68
        .... make api calls ....
69

70
    """
71
    with patch("snf_django.lib.api.get_token") as get_token:
72
        get_token.return_value = "DummyToken"
73
        with patch('astakosclient.AstakosClient.get_user_info') as m:
74
            m.return_value = {"uuid": user}
75
            yield
76

    
77

    
78
class BaseAPITest(TestCase):
79
    def get(self, url, user='user', *args, **kwargs):
80
        with astakos_user(user):
81
            response = self.client.get(url, *args, **kwargs)
82
        return response
83

    
84
    def delete(self, url, user='user'):
85
        with astakos_user(user):
86
            response = self.client.delete(url)
87
        return response
88

    
89
    def post(self, url, user='user', params={}, ctype='json', *args, **kwargs):
90
        if ctype == 'json':
91
            content_type = 'application/json'
92
        with astakos_user(user):
93
            response = self.client.post(url, params, content_type=content_type,
94
                                        *args, **kwargs)
95
        return response
96

    
97
    def put(self, url, user='user', params={}, ctype='json', *args, **kwargs):
98
        if ctype == 'json':
99
            content_type = 'application/json'
100
        with astakos_user(user):
101
            response = self.client.put(url, params, content_type=content_type,
102
                                       *args, **kwargs)
103
        return response
104

    
105
    def assertSuccess(self, response):
106
        self.assertTrue(response.status_code in [200, 203, 204])
107

    
108
    def assertFault(self, response, status_code, name):
109
        self.assertEqual(response.status_code, status_code)
110
        fault = response.content
111
        self.assertEqual(fault, name)
112

    
113
    def assertBadRequest(self, response):
114
        self.assertFault(response, 400, '400 Bad Request')
115

    
116
    def assertItemNotFound(self, response):
117
        self.assertFault(response, 404, 'itemNotFound')
118

    
119

    
120
DummyImages = {
121
 '0786a349-9725-48ec-8b86-8598eefc4043':
122
 {'checksum': u'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
123
  u'container_format': u'bare',
124
  'created_at': '2012-12-04 09:50:20',
125
  'deleted_at': '',
126
  u'disk_format': u'diskdump',
127
  'id': u'0786a349-9725-48ec-8b86-8598eefc4043',
128
  'is_public': True,
129
  'location': u'pithos://foo@example.com/container/foo3',
130
  u'name': u'dummyname',
131
  'owner': u'foo@example.com',
132
  'properties': {},
133
  'size': 500L,
134
  u'status': u'available',
135
  'store': 'pithos',
136
  'updated_at': '2012-12-04 09:50:54'},
137

    
138
 'd8aa85b8-410b-4550-953d-6797572534e6':
139
 {'checksum': u'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
140
  u'container_format': u'bare',
141
  'created_at': '2012-11-26 11:56:42',
142
  'deleted_at': '',
143
  u'disk_format': u'diskdump',
144
  'id': u'd8aa85b8-410b-4550-953d-6797572534e6',
145
  'is_public': False,
146
  'location': u'pithos://foo@example.com/container/private',
147
  u'name': u'dummyname2',
148
  'owner': u'foo@example.com',
149
  'properties': {},
150
  'size': 10000L,
151
  u'status': u'available',
152
  'store': 'pithos',
153
  'updated_at': '2012-11-26 11:57:09'},
154

    
155
 '264fb9ac-2458-421c-b460-6a765a92825c':
156
 {'checksum': u'0c6d0586744781218672fff2d7ed94cc32efb02a6a8eb589a0628f0e22bd5a7f',
157
  u'container_format': u'bare',
158
  'created_at': '2012-11-26 11:52:54',
159
  'deleted_at': '',
160
  u'disk_format': u'diskdump',
161
  'id': u'264fb9ac-2458-421c-b460-6a765a92825c',
162
  'is_public': True,
163
  'location': u'pithos://foo@example.com/container/baz.diskdump',
164
  u'name': u'"dummyname3"',
165
  'owner': u'foo@example.com',
166
  'properties': {u'description': u'Debian Squeeze Base System',
167
                 u'gui': u'No GUI',
168
                 u'kernel': u'2.6.32',
169
                 u'os': u'debian',
170
                 u'osfamily': u'linux',
171
                 u'root_partition': u'1',
172
                 u'size': u'451',
173
                 u'sortorder': u'1',
174
                 u'users': u'root'},
175
  'size': 473772032L,
176
  u'status': u'available',
177
  'store': 'pithos',
178
  'updated_at': '2012-11-26 11:55:40'}}
179

    
180

    
181
def assert_backend_closed(func):
182
    @wraps(func)
183
    def wrapper(self, backend):
184
        result = func(self, backend)
185
        if backend.called is True:
186
            backend.return_value.close.assert_called_once_with()
187
        return result
188
    return wrapper
189

    
190

    
191
@patch("synnefo.plankton.utils.ImageBackend")
192
class PlanktonTest(BaseAPITest):
193
    @assert_backend_closed
194
    def test_list_images(self, backend):
195
        backend.return_value.list.return_value =\
196
                deepcopy(DummyImages).values()
197
        response = self.get("/plankton/images/")
198
        self.assertSuccess(response)
199
        images = json.loads(response.content)
200
        for api_image in images:
201
            id = api_image['id']
202
            pithos_image = dict([(key, val)\
203
                                for key, val in DummyImages[id].items()\
204
                                if key in LIST_FIELDS])
205
            self.assertEqual(api_image, pithos_image)
206
        backend.return_value\
207
                .list.assert_called_once_with({}, {'sort_key': 'created_at',
208
                                                   'sort_dir': 'desc'})
209

    
210
    @assert_backend_closed
211
    def test_list_images_detail(self, backend):
212
        backend.return_value.list.return_value =\
213
                deepcopy(DummyImages).values()
214
        response = self.get("/plankton/images/detail")
215
        self.assertSuccess(response)
216
        images = json.loads(response.content)
217
        for api_image in images:
218
            id = api_image['id']
219
            pithos_image = dict([(key, val)\
220
                                for key, val in DummyImages[id].items()\
221
                                if key in DETAIL_FIELDS])
222
            self.assertEqual(api_image, pithos_image)
223
        backend.return_value\
224
                .list.assert_called_once_with({}, {'sort_key': 'created_at',
225
                                                   'sort_dir': 'desc'})
226

    
227
    @assert_backend_closed
228
    def test_list_images_filters(self, backend):
229
        backend.return_value.list.return_value =\
230
                deepcopy(DummyImages).values()
231
        response = self.get("/plankton/images/?size_max=1000")
232
        self.assertSuccess(response)
233
        backend.return_value\
234
                .list.assert_called_once_with({'size_max': 1000},
235
                                              {'sort_key': 'created_at',
236
                                               'sort_dir': 'desc'})
237

    
238
    @assert_backend_closed
239
    def test_list_images_filters_error_1(self, backend):
240
        response = self.get("/plankton/images/?size_max=")
241
        self.assertBadRequest(response)
242

    
243
    @assert_backend_closed
244
    def test_list_images_filters_error_2(self, backend):
245
        response = self.get("/plankton/images/?size_min=foo")
246
        self.assertBadRequest(response)
247

    
248
    @assert_backend_closed
249
    def test_update_image(self, backend):
250
        db_image = DummyImages.values()[0]
251
        response = self.put("/plankton/images/%s" % db_image['id'],
252
                            json.dumps({}),
253
                            'json', HTTP_X_IMAGE_META_OWNER='user2')
254
        self.assertSuccess(response)
255
        backend.return_value.update.assert_called_once_with(db_image['id'],
256
                                                            {"owner": "user2"})
257

    
258
    @assert_backend_closed
259
    def test_add_image_member(self, backend):
260
        image_id = DummyImages.values()[0]['id']
261
        response = self.put("/plankton/images/%s/members/user3" % image_id,
262
                            json.dumps({}), 'json')
263
        self.assertSuccess(response)
264
        backend.return_value.add_user.assert_called_once_with(image_id,
265
                                                             'user3')
266

    
267
    @assert_backend_closed
268
    def test_remove_image_member(self, backend):
269
        image_id = DummyImages.values()[0]['id']
270
        response = self.delete("/plankton/images/%s/members/user3" % image_id)
271
        self.assertSuccess(response)
272
        backend.return_value.remove_user.assert_called_once_with(image_id,
273
                                                                'user3')
274

    
275
    @assert_backend_closed
276
    def test_add_image(self, backend):
277
        response = self.post("/plankton/images/",
278
                             json.dumps({}),
279
                             'json',
280
                             HTTP_X_IMAGE_META_NAME='dummy_name',
281
                             HTTP_X_IMAGE_META_OWNER='dummy_owner',
282
                             HTTP_X_IMAGE_META_LOCATION='dummy_location')
283
        self.assertSuccess(response)
284
        backend.return_value.register.assert_called_once_with('dummy_name',
285
                                                              'dummy_location',
286
                                                      {'owner': 'dummy_owner'})
287

    
288
    @assert_backend_closed
289
    def test_get_image(self, backend):
290
        response = self.get("/plankton/images/123")
291
        self.assertEqual(response.status_code, 501)
292

    
293
    @assert_backend_closed
294
    def test_delete_image(self, backend):
295
        response = self.delete("/plankton/images/123")
296
        self.assertEqual(response.status_code, 204)
297
        backend.return_value.unregister.assert_called_once_with('123')
298
        backend.return_value._delete.assert_not_called()