Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / test / pithos.py @ cf687343

History | View | Annotate | Download (11.7 kB)

1
# Copyright 2013 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
from unittest import TestCase
35
from mock import patch, call, Mock
36

    
37
from kamaki.clients import ClientError
38
from kamaki.clients.pithos import PithosClient as PC
39
from kamaki.clients.astakos import AstakosClient
40
from kamaki.clients.connection.kamakicon import KamakiHTTPConnection as C
41

    
42
user_id = 'ac0un7-1d-5tr1ng'
43

    
44
account_info = {
45
    'content-language': 'en-us',
46
    'content-type': 'text/html; charset=utf-8',
47
    'date': 'Wed, 06 Mar 2013 13:25:51 GMT',
48
    'last-modified': 'Mon, 04 Mar 2013 18:22:31 GMT',
49
    'server': 'gunicorn/0.14.5',
50
    'vary': 'Accept-Language',
51
    'x-account-bytes-used': '751615526',
52
    'x-account-container-count': 7,
53
    'x-account-policy-quota': 53687091200,
54
    'x-account-policy-versioning': 'auto'}
55
container_info = {
56
    'content-language': 'en-us',
57
    'content-type': 'text/html; charset=utf-8',
58
    'date': 'Wed, 06 Mar 2013 15:11:05 GMT',
59
    'last-modified': 'Wed, 27 Feb 2013 15:56:13 GMT',
60
    'server': 'gunicorn/0.14.5',
61
    'vary': 'Accept-Language',
62
    'x-container-block-hash': 'sha256',
63
    'x-container-block-size': 4194304,
64
    'x-container-bytes-used': 309528938,
65
    'x-container-object-count': 14,
66
    'x-container-object-meta': '',
67
    'x-container-policy-quota': 53687091200,
68
    'x-container-policy-versioning': 'auto'}
69
container_list = [
70
    dict(
71
        count=2,
72
        last_modified="2013-02-27T11:56:09.893033+00:00",
73
        bytes=677076979,
74
        name="pithos",
75
        x_container_policy=dict(quota="21474836480", versioning="auto")),
76
    dict(
77
        count=0,
78
        last_modified="2012-10-23T12:25:17.229187+00:00",
79
        bytes=0,
80
        name="trash",
81
        x_container_policy=dict(quota="21474836480", versioning="auto"))]
82

    
83

    
84
class Pithos(TestCase):
85

    
86
    class FR(object):
87
        """FR stands for Fake Response"""
88
        json = dict()
89
        headers = dict()
90
        content = json
91
        status = None
92
        status_code = 200
93

    
94
        def release(self):
95
            pass
96

    
97
    files = []
98

    
99
    def assert_dicts_are_equal(self, d1, d2):
100
        for k, v in d1.items():
101
            self.assertTrue(k in d2)
102
            if isinstance(v, dict):
103
                self.assert_dicts_are_equal(v, d2[k])
104
            else:
105
                self.assertEqual(unicode(v), unicode(d2[k]))
106

    
107
    def setUp(self):
108
        self.url = 'https://www.example.com/pithos'
109
        self.token = 'p17h0570k3n'
110
        self.client = PC(self.url, self.token)
111
        self.client.account = user_id
112
        self.client.container = 'c0nt@1n3r_i'
113

    
114
    def tearDown(self):
115
        self.FR.headers = dict()
116
        self.FR.status_code = 200
117
        self.FR.json = dict()
118
        for f in self.files:
119
            f.close()
120

    
121
    def test_get_account_info(self):
122
        self.FR.headers = account_info
123
        self.FR.status_code = 204
124
        with patch.object(C, 'perform_request', return_value=self.FR()):
125
            r = self.client.get_account_info()
126
            self.assertEqual(self.client.http_client.url, self.url)
127
            self.assertEqual(self.client.http_client.path, '/%s' % user_id)
128
            self.assert_dicts_are_equal(r, account_info)
129
            PC.set_param = Mock()
130
            untils = ['date 1', 'date 2', 'date 3']
131
            for unt in untils:
132
                r = self.client.get_account_info(until=unt)
133
                self.assert_dicts_are_equal(r, account_info)
134
            for i in range(len(untils)):
135
                self.assertEqual(
136
                    PC.set_param.mock_calls[i],
137
                    call('until', untils[i], iff=untils[i]))
138
            self.FR.status_code = 401
139
            self.assertRaises(ClientError, self.client.get_account_info)
140

    
141
    def test_replace_account_meta(self):
142
        self.FR.status_code = 202
143
        metas = dict(k1='v1', k2='v2', k3='v3')
144
        PC.set_header = Mock()
145
        with patch.object(C, 'perform_request', return_value=self.FR()):
146
            self.client.replace_account_meta(metas)
147
            self.assertEqual(self.client.http_client.url, self.url)
148
            self.assertEqual(self.client.http_client.path, '/%s' % user_id)
149
            prfx = 'X-Account-Meta-'
150
            expected = [call('%s%s' % (prfx, k), v) for k, v in metas.items()]
151
            self.assertEqual(PC.set_header.mock_calls, expected)
152

    
153
    def test_del_account_meta(self):
154
        keys = ['k1', 'k2', 'k3']
155
        with patch.object(PC, 'account_post', return_value=self.FR()) as ap:
156
            expected = []
157
            for key in keys:
158
                self.client.del_account_meta(key)
159
                expected.append(call(update=True, metadata={key: ''}))
160
            self.assertEqual(ap.mock_calls, expected)
161

    
162
    def test_create_container(self):
163
        self.FR.status_code = 201
164
        with patch.object(PC, 'put', return_value=self.FR()) as put:
165
            cont = 's0m3c0n731n3r'
166
            self.client.create_container(cont)
167
            expected = [call('/%s/%s' % (user_id, cont), success=(201, 202))]
168
            self.assertEqual(put.mock_calls, expected)
169
            self.FR.status_code = 202
170
            self.assertRaises(ClientError, self.client.create_container, cont)
171

    
172
    def test_get_container_info(self):
173
        self.FR.headers = container_info
174
        with patch.object(PC, 'container_head', return_value=self.FR()) as ch:
175
            r = self.client.get_container_info()
176
            self.assert_dicts_are_equal(r, container_info)
177
            u = 'some date'
178
            r = self.client.get_container_info(until=u)
179
            self.assertEqual(ch.mock_calls, [call(until=None), call(until=u)])
180

    
181
    def test_delete_container(self):
182
        self.FR.status_code = 204
183
        with patch.object(PC, 'delete', return_value=self.FR()) as delete:
184
            cont = 's0m3c0n731n3r'
185
            self.client.delete_container(cont)
186
            self.FR.status_code = 404
187
            self.assertRaises(ClientError, self.client.delete_container, cont)
188
            self.FR.status_code = 409
189
            self.assertRaises(ClientError, self.client.delete_container, cont)
190
            acall = call('/%s/%s' % (user_id, cont), success=(204, 404, 409))
191
            self.assertEqual(delete.mock_calls, [acall] * 3)
192

    
193
    def test_list_containers(self):
194
        self.FR.json = container_list
195
        with patch.object(PC, 'account_get', return_value=self.FR()):
196
            r = self.client.list_containers()
197
            for i in range(len(r)):
198
                self.assert_dicts_are_equal(r[i], container_list[i])
199

    
200
    def test_upload_object(self):
201
        PC.get_container_info = Mock(return_value=container_info)
202
        PC.container_post = Mock(return_value=self.FR())
203
        PC.object_put = Mock(return_value=self.FR())
204
        from tempfile import NamedTemporaryFile
205
        from os import urandom
206
        self.files.append(NamedTemporaryFile())
207
        tmpFile = self.files[-1]
208
        num_of_blocks = 8
209
        file_size = num_of_blocks * 4 * 1024 * 1024
210
        print('\n\tCreate tmp file')
211
        tmpFile.write(urandom(file_size))
212
        tmpFile.flush()
213
        tmpFile.seek(0)
214
        print('\t\tDone')
215
        obj = 'objectName'
216

    
217
        # No special args
218
        self.client.upload_object(obj, tmpFile)
219
        self.assertEqual(PC.get_container_info.mock_calls, [call()])
220
        [call1, call2] = PC.object_put.mock_calls
221

    
222
        (args1, kwargs1) = call1[1:3]
223
        (args2, kwargs2) = call2[1:3]
224
        self.assertEqual(args1, (obj,))
225
        expected1 = dict(
226
            hashmap=True,
227
            success=(201, 409),
228
            format='json',
229
            json=dict(
230
                hashes=['s0m3h@5h'] * num_of_blocks,
231
                bytes=file_size),
232
            etag=None,
233
            content_encoding=None,
234
            content_type='application/octet-stream',
235
            content_disposition=None,
236
            public=None,
237
            permissions=None)
238
        for k, v in expected1.items():
239
            if k == 'json':
240
                self.assertEqual(len(v['hashes']), len(kwargs1[k]['hashes']))
241
                self.assertEqual(v['bytes'], kwargs1[k]['bytes'])
242
            else:
243
                self.assertEqual(v, kwargs1[k])
244

    
245
        (args2, kwargs2) = call2[1:3]
246
        self.assertEqual(args2, (obj,))
247
        expected2 = dict(
248
            json=dict(
249
                hashes=['s0m3h@5h'] * num_of_blocks,
250
                bytes=file_size),
251
            content_type='application/octet-stream',
252
            hashmap=True,
253
            success=201,
254
            format='json')
255
        for k, v in expected2.items():
256
            if k == 'json':
257
                self.assertEqual(len(v['hashes']), len(kwargs2[k]['hashes']))
258
                self.assertEqual(v['bytes'], kwargs2[k]['bytes'])
259
            else:
260
                self.assertEqual(v, kwargs2[k])
261

    
262
        OP = PC.object_put
263
        mock_offset = 2
264

    
265
        #  With progress bars
266
        try:
267
            from progress.bar import ShadyBar
268
            blck_bar = ShadyBar('Mock blck calc.')
269
            upld_bar = ShadyBar('Mock uplds')
270
        except ImportError:
271
            blck_bar = None
272
            upld_bar = None
273

    
274
        if blck_bar and upld_bar:
275

    
276
            def blck_gen(n):
277
                for i in blck_bar.iter(range(n)):
278
                    yield
279
                yield
280

    
281
            def upld_gen(n):
282
                for i in upld_bar.iter(range(n)):
283
                    yield
284
                yield
285

    
286
            tmpFile.seek(0)
287
            self.client.upload_object(
288
                obj, tmpFile,
289
                hash_cb=blck_gen, upload_cb=upld_gen)
290

    
291
            for i, c in enumerate(OP.mock_calls[-mock_offset:]):
292
                self.assertEqual(OP.mock_calls[i], c)
293

    
294
        #  With content-type
295
        tmpFile.seek(0)
296
        ctype = 'video/mpeg'
297
        sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
298
        self.client.upload_object(obj, tmpFile,
299
            content_type=ctype, sharing=sharing)
300
        self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
301
        self.assert_dicts_are_equal(
302
            OP.mock_calls[-2][2]['permissions'],
303
            sharing)
304

    
305
        # With other args
306
        tmpFile.seek(0)
307
        kwargs = dict(
308
            etag='s0m3E74g',
309
            content_type=ctype,
310
            content_disposition=ctype + 'd15p051710n',
311
            public=True,
312
            content_encoding='802.11')
313
        self.client.upload_object(obj, tmpFile, **kwargs)
314
        for arg, val in kwargs.items():
315
            self.assertEqual(OP.mock_calls[-2][2][arg], val)