Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-tools / pithos / tools / test.py @ 2535deff

History | View | Annotate | Download (94 kB)

1
#!/usr/bin/env python
2
#coding=utf8
3

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

    
37
from pithos.tools.lib.client import Pithos_Client, Fault
38
from pithos.tools.lib.util import get_user, get_auth, get_url
39

    
40
from xml.dom import minidom
41
from StringIO import StringIO
42
from hashlib import new as newhasher
43
from binascii import hexlify
44
from httplib import HTTPConnection, HTTPSConnection
45
from urlparse import urlparse
46

    
47
import json
48
import unittest
49
import time as _time
50
import types
51
import hashlib
52
import mimetypes
53
import random
54
import datetime
55
import string
56
import re
57

    
58
DATE_FORMATS = ["%a %b %d %H:%M:%S %Y",
59
                "%A, %d-%b-%y %H:%M:%S GMT",
60
                "%a, %d %b %Y %H:%M:%S GMT"]
61

    
62
OTHER_ACCOUNTS = {
63
    '0001': 'verigak',
64
    '0002': 'chazapis',
65
    '0003': 'gtsouk',
66
    '0004': 'papagian',
67
    '0005': 'louridas',
68
    '0006': 'chstath',
69
    '0007': 'pkanavos',
70
    '0008': 'mvasilak',
71
    '0009': 'διογένης'}
72

    
73
class BaseTestCase(unittest.TestCase):
74
    #TODO unauthorized request
75
    def setUp(self):
76
        self.client = Pithos_Client(get_url(), get_auth(), get_user())
77
        self._clean_account()
78
        self.invalid_client = Pithos_Client(get_url(), get_auth(), 'invalid')
79

    
80
        #keep track of initial account groups
81
        self.initial_groups = self.client.retrieve_account_groups()
82

    
83
        #keep track of initial account meta
84
        self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
85

    
86
        self.extended = {
87
            'container':(
88
                'name',
89
                'count',
90
                'bytes',
91
                'last_modified',
92
                'x_container_policy'),
93
            'object':(
94
                'name',
95
                'hash',
96
                'bytes',
97
                'content_type',
98
                'content_encoding',
99
                'last_modified',)}
100
        self.return_codes = (400, 401, 403, 404, 503,)
101

    
102
    def tearDown(self):
103
        #delete additionally created meta
104
        l = []
105
        for m in self.client.retrieve_account_metadata(restricted=True):
106
            if m not in self.initial_meta:
107
                l.append(m)
108
        self.client.delete_account_metadata(l)
109

    
110
        #delete additionally created groups
111
        l = []
112
        for g in self.client.retrieve_account_groups():
113
            if g not in self.initial_groups:
114
                l.append(g)
115
        self.client.unset_account_groups(l)
116
        self._clean_account()
117

    
118
    def _clean_account(self):
119
        for c in self.client.list_containers():
120
            while True:
121
                #list objects returns at most 10000 objects
122
                #so repeat until there are no more objects
123
                objects = self.client.list_objects(c)
124
                if not objects:
125
                    break
126
                for o in objects:
127
                    self.client.delete_object(c, o)
128
            self.client.delete_container(c)
129

    
130
    def assert_status(self, status, codes):
131
        l = [elem for elem in self.return_codes]
132
        if type(codes) == types.ListType:
133
            l.extend(codes)
134
        else:
135
            l.append(codes)
136
        self.assertTrue(status in l)
137

    
138
    def assert_extended(self, data, format, type, size=10000):
139
        if format == 'xml':
140
            self._assert_xml(data, type, size)
141
        elif format == 'json':
142
            self._assert_json(data, type, size)
143

    
144
    def _assert_json(self, data, type, size):
145
        convert = lambda s: s.lower()
146
        info = [convert(elem) for elem in self.extended[type]]
147
        self.assertTrue(len(data) <= size)
148
        for item in info:
149
            for i in data:
150
                if 'subdir' in i.keys():
151
                    continue
152
                self.assertTrue(item in i.keys())
153

    
154
    def _assert_xml(self, data, type, size):
155
        convert = lambda s: s.lower()
156
        info = [convert(elem) for elem in self.extended[type]]
157
        try:
158
            info.remove('content_encoding')
159
        except ValueError:
160
            pass
161
        xml = data
162
        entities = xml.getElementsByTagName(type)
163
        self.assertTrue(len(entities) <= size)
164
        for e in entities:
165
            for item in info:
166
                self.assertTrue(e.getElementsByTagName(item))
167

    
168
    def assert_raises_fault(self, status, callableObj, *args, **kwargs):
169
        """
170
        asserts that a Fault with a specific status is raised
171
        when callableObj is called with the specific arguments
172
        """
173
        try:
174
            r = callableObj(*args, **kwargs)
175
            self.fail('Should never reach here')
176
        except Fault, f:
177
            if type(status) == types.ListType:
178
                self.failUnless(f.status in status)
179
            else:
180
                self.failUnless(f.status == status)
181

    
182
    def assert_not_raises_fault(self, status, callableObj, *args, **kwargs):
183
        """
184
        asserts that a Fault with a specific status is not raised
185
        when callableObj is called with the specific arguments
186
        """
187
        try:
188
            r = callableObj(*args, **kwargs)
189
        except Fault, f:
190
            self.failIfEqual(f.status, status)
191

    
192
    def assert_container_exists(self, container):
193
        """
194
        asserts the existence of a container
195
        """
196
        try:
197
            self.client.retrieve_container_metadata(container)
198
        except Fault, f:
199
            self.failIf(f.status == 404)
200

    
201
    def assert_container_not_exists(self, container):
202
        """
203
        asserts there is no such a container
204
        """
205
        self.assert_raises_fault(404, self.client.retrieve_container_metadata,
206
                                 container)
207

    
208
    def assert_object_exists(self, container, object):
209
        """
210
        asserts the existence of an object
211
        """
212
        try:
213
            self.client.retrieve_object_metadata(container, object)
214
        except Fault, f:
215
            self.failIf(f.status == 404)
216

    
217
    def assert_object_not_exists(self, container, object):
218
        """
219
        asserts there is no such an object
220
        """
221
        self.assert_raises_fault(404, self.client.retrieve_object_metadata,
222
                                 container, object)
223

    
224
    def assert_versionlist_structure(self, versionlist):
225
        self.assertTrue(type(versionlist) == types.ListType)
226
        for elem in versionlist:
227
            self.assertTrue(type(elem) == types.ListType)
228
            self.assertEqual(len(elem), 2)
229

    
230
    def upload_random_data(self, container, name, length=1024, type=None,
231
                           enc=None, **meta):
232
        data = get_random_data(length)
233
        return self.upload_data(container, name, data, type, enc, **meta)
234

    
235
    def upload_data(self, container, name, data, type=None, enc=None, etag=None,
236
                    **meta):
237
        obj = {}
238
        obj['name'] = name
239
        try:
240
            obj['data'] = data
241
            obj['hash'] = compute_md5_hash(obj['data'])
242

    
243
            args = {}
244
            args['etag'] = etag if etag else obj['hash']
245

    
246
            try:
247
                guess = mimetypes.guess_type(name)
248
                type = type if type else guess[0]
249
                enc = enc if enc else guess[1]
250
            except:
251
                pass
252
            args['content_type'] = type if type else 'plain/text'
253
            args['content_encoding'] = enc if enc else None
254

    
255
            obj['meta'] = args
256

    
257
            path = '/%s/%s' % (container, name)
258
            self.client.create_object(container, name, f=StringIO(obj['data']),
259
                                      meta=meta, **args)
260

    
261
            return obj
262
        except IOError:
263
            return
264

    
265
class AccountHead(BaseTestCase):
266
    def setUp(self):
267
        BaseTestCase.setUp(self)
268
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
269
        for item in self.containers:
270
            self.client.create_container(item)
271

    
272
        meta = {'foo':'bar'}
273
        self.client.update_account_metadata(**meta)
274
        #self.updated_meta = self.initial_meta.update(meta)
275

    
276
    def test_get_account_meta(self):
277
        meta = self.client.retrieve_account_metadata()
278

    
279
        containers = self.client.list_containers()
280
        l = str(len(containers))
281
        self.assertEqual(meta['x-account-container-count'], l)
282
        size = 0
283
        for c in containers:
284
            m = self.client.retrieve_container_metadata(c)
285
            size = size + int(m['x-container-bytes-used'])
286
        self.assertEqual(meta['x-account-bytes-used'], str(size))
287

    
288
    def test_get_account_403(self):
289
        self.assert_raises_fault(403,
290
                                 self.invalid_client.retrieve_account_metadata)
291

    
292
    def test_get_account_meta_until(self):
293
        t = datetime.datetime.utcnow()
294
        past = t - datetime.timedelta(minutes=-15)
295
        past = int(_time.mktime(past.timetuple()))
296

    
297
        meta = {'premium':True}
298
        self.client.update_account_metadata(**meta)
299
        meta = self.client.retrieve_account_metadata(restricted=True,
300
                                                     until=past)
301
        self.assertTrue('premium' not in meta)
302

    
303
        meta = self.client.retrieve_account_metadata(restricted=True)
304
        self.assertTrue('premium' in meta)
305

    
306
    def test_get_account_meta_until_invalid_date(self):
307
        meta = {'premium':True}
308
        self.client.update_account_metadata(**meta)
309
        meta = self.client.retrieve_account_metadata(restricted=True,
310
                                                     until='kshfksfh')
311
        self.assertTrue('premium' in meta)
312

    
313
class AccountGet(BaseTestCase):
314
    def setUp(self):
315
        BaseTestCase.setUp(self)
316
        #create some containers
317
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
318
        for item in self.containers:
319
            self.client.create_container(item)
320

    
321
    def test_list(self):
322
        #list containers
323
        containers = self.client.list_containers()
324
        self.assertEquals(self.containers, containers)
325

    
326
    def test_list_403(self):
327
        self.assert_raises_fault(403, self.invalid_client.list_containers)
328

    
329
    def test_list_with_limit(self):
330
        limit = 2
331
        containers = self.client.list_containers(limit=limit)
332
        self.assertEquals(len(containers), limit)
333
        self.assertEquals(self.containers[:2], containers)
334

    
335
    def test_list_with_marker(self):
336
        l = 2
337
        m = 'bananas'
338
        containers = self.client.list_containers(limit=l, marker=m)
339
        i = self.containers.index(m) + 1
340
        self.assertEquals(self.containers[i:(i+l)], containers)
341

    
342
        m = 'oranges'
343
        containers = self.client.list_containers(limit=l, marker=m)
344
        i = self.containers.index(m) + 1
345
        self.assertEquals(self.containers[i:(i+l)], containers)
346

    
347
    def test_list_json_with_marker(self):
348
        l = 2
349
        m = 'bananas'
350
        containers = self.client.list_containers(limit=l, marker=m, format='json')
351
        self.assert_extended(containers, 'json', 'container', l)
352
        self.assertEqual(containers[0]['name'], 'kiwis')
353
        self.assertEqual(containers[1]['name'], 'oranges')
354

    
355
    def test_list_xml_with_marker(self):
356
        l = 2
357
        m = 'oranges'
358
        xml = self.client.list_containers(limit=l, marker=m, format='xml')
359
        self.assert_extended(xml, 'xml', 'container', l)
360
        nodes = xml.getElementsByTagName('name')
361
        self.assertEqual(len(nodes), 1)
362
        self.assertEqual(nodes[0].childNodes[0].data, 'pears')
363

    
364
    def test_if_modified_since(self):
365
        t = datetime.datetime.utcnow()
366
        t2 = t - datetime.timedelta(minutes=10)
367

    
368
        #add a new container
369
        self.client.create_container('dummy')
370

    
371
        for f in DATE_FORMATS:
372
            past = t2.strftime(f)
373
            try:
374
                c = self.client.list_containers(if_modified_since=past)
375
                self.assertEqual(len(c), len(self.containers) + 1)
376
            except Fault, f:
377
                self.failIf(f.status == 304) #fail if not modified
378
        
379

    
380
    def test_if_modified_since_invalid_date(self):
381
        c = self.client.list_containers(if_modified_since='')
382
        self.assertEqual(len(c), len(self.containers))
383

    
384
    def test_if_not_modified_since(self):
385
        now = datetime.datetime.utcnow()
386
        since = now + datetime.timedelta(1)
387

    
388
        for f in DATE_FORMATS:
389
            args = {'if_modified_since':'%s' %since.strftime(f)}
390

    
391
            #assert not modified
392
            self.assert_raises_fault(304, self.client.list_containers, **args)
393

    
394
    def test_if_unmodified_since(self):
395
        now = datetime.datetime.utcnow()
396
        since = now + datetime.timedelta(1)
397

    
398
        for f in DATE_FORMATS:
399
            c = self.client.list_containers(if_unmodified_since=since.strftime(f))
400

    
401
            #assert success
402
            self.assertEqual(self.containers, c)
403

    
404
    def test_if_unmodified_since_precondition_failed(self):
405
        t = datetime.datetime.utcnow()
406
        t2 = t - datetime.timedelta(minutes=10)
407

    
408
        #add a new container
409
        self.client.create_container('dummy')
410

    
411
        for f in DATE_FORMATS:
412
            past = t2.strftime(f)
413

    
414
            args = {'if_unmodified_since':'%s' %past}
415

    
416
            #assert precondition failed
417
            self.assert_raises_fault(412, self.client.list_containers, **args)
418

    
419
class AccountPost(BaseTestCase):
420
    def setUp(self):
421
        BaseTestCase.setUp(self)
422
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
423
        for item in self.containers:
424
            self.client.create_container(item)
425

    
426
        meta = {'foo':'bar'}
427
        self.client.update_account_metadata(**meta)
428
        self.updated_meta = self.initial_meta.update(meta)
429

    
430
    def test_update_meta(self):
431
        with AssertMappingInvariant(self.client.retrieve_account_groups):
432
            meta = {'test':'test', 'tost':'tost'}
433
            self.client.update_account_metadata(**meta)
434

    
435
            meta.update(self.initial_meta)
436
            self.assertEqual(meta,
437
                             self.client.retrieve_account_metadata(
438
                                restricted=True))
439

    
440
    def test_invalid_account_update_meta(self):
441
        meta = {'test':'test', 'tost':'tost'}
442
        self.assert_raises_fault(403,
443
                                 self.invalid_client.update_account_metadata,
444
                                 **meta)
445

    
446
    def test_reset_meta(self):
447
        with AssertMappingInvariant(self.client.retrieve_account_groups):
448
            meta = {'test':'test', 'tost':'tost'}
449
            self.client.update_account_metadata(**meta)
450

    
451
            meta = {'test':'test33'}
452
            self.client.reset_account_metadata(**meta)
453

    
454
            self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
455

    
456
    def test_delete_meta(self):
457
        with AssertMappingInvariant(self.client.retrieve_account_groups):
458
            meta = {'test':'test', 'tost':'tost'}
459
            self.client.update_account_metadata(**meta)
460

    
461
            self.client.delete_account_metadata(meta.keys())
462

    
463
            account_meta = self.client.retrieve_account_metadata(restricted=True)
464
            for m in meta:
465
                self.assertTrue(m not in account_meta.keys())
466

    
467
    def test_set_account_groups(self):
468
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
469
            groups = {'pithosdev':'verigak,gtsouk,chazapis'}
470
            self.client.set_account_groups(**groups)
471

    
472
            self.assertEqual(set(groups['pithosdev']),
473
                             set(self.client.retrieve_account_groups()['pithosdev']))
474

    
475
            more_groups = {'clientsdev':'pkanavos,mvasilak'}
476
            self.client.set_account_groups(**more_groups)
477

    
478
            groups.update(more_groups)
479
            self.assertEqual(set(groups['clientsdev']),
480
                             set(self.client.retrieve_account_groups()['clientsdev']))
481

    
482
    def test_reset_account_groups(self):
483
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
484
            groups = {'pithosdev':'verigak,gtsouk,chazapis',
485
                      'clientsdev':'pkanavos,mvasilak'}
486
            self.client.set_account_groups(**groups)
487

    
488
            self.assertEqual(set(groups['pithosdev'].split(',')),
489
                             set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
490
            self.assertEqual(set(groups['clientsdev'].split(',')),
491
                             set(self.client.retrieve_account_groups()['clientsdev'].split(',')))
492

    
493
            groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
494
            self.client.reset_account_groups(**groups)
495

    
496
            self.assertEqual(set(groups['pithosdev'].split(',')),
497
                             set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
498

    
499
    def test_delete_account_groups(self):
500
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
501
            groups = {'pithosdev':'verigak,gtsouk,chazapis',
502
                      'clientsdev':'pkanavos,mvasilak'}
503
            self.client.set_account_groups(**groups)
504

    
505
            self.client.unset_account_groups(groups.keys())
506

    
507
            self.assertEqual({}, self.client.retrieve_account_groups())
508

    
509
class ContainerHead(BaseTestCase):
510
    def setUp(self):
511
        BaseTestCase.setUp(self)
512
        self.container = 'apples'
513
        self.client.create_container(self.container)
514

    
515
    def test_get_meta(self):
516
        meta = {'trash':'true'}
517
        t1 = datetime.datetime.utcnow()
518
        o = self.upload_random_data(self.container, o_names[0], **meta)
519
        if o:
520
            headers = self.client.retrieve_container_metadata(self.container)
521
            self.assertEqual(headers['x-container-object-count'], '1')
522
            self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
523
            t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
524
            delta = (t2 - t1)
525
            threashold = datetime.timedelta(seconds=1)
526
            self.assertTrue(delta < threashold)
527
            self.assertTrue(headers['x-container-object-meta'])
528
            self.assertTrue('Trash' in headers['x-container-object-meta'])
529

    
530
class ContainerGet(BaseTestCase):
531
    def setUp(self):
532
        BaseTestCase.setUp(self)
533
        self.container = ['pears', 'apples']
534
        for c in self.container:
535
            self.client.create_container(c)
536
        self.obj = []
537
        for o in o_names[:8]:
538
            self.obj.append(self.upload_random_data(self.container[0], o))
539
        for o in o_names[8:]:
540
            self.obj.append(self.upload_random_data(self.container[1], o))
541

    
542
    def test_list_objects(self):
543
        objects = self.client.list_objects(self.container[0])
544
        l = [elem['name'] for elem in self.obj[:8]]
545
        l.sort()
546
        self.assertEqual(objects, l)
547

    
548
    def test_list_objects_containing_slash(self):
549
        self.client.create_container('test')
550
        self.upload_random_data('test', '/objectname')
551

    
552
        objects = self.client.list_objects('test')
553
        self.assertEqual(objects, ['/objectname'])
554

    
555
        objects = self.client.list_objects('test', format='json')
556
        self.assertEqual(objects[0]['name'], '/objectname')
557

    
558
        objects = self.client.list_objects('test', format='xml')
559
        self.assert_extended(objects, 'xml', 'object')
560
        node_name = objects.getElementsByTagName('name')[0]
561
        self.assertEqual(node_name.firstChild.data, '/objectname')
562

    
563
    def test_list_objects_with_limit_marker(self):
564
        objects = self.client.list_objects(self.container[0], limit=2)
565
        l = [elem['name'] for elem in self.obj[:8]]
566
        l.sort()
567
        self.assertEqual(objects, l[:2])
568

    
569
        markers = ['How To Win Friends And Influence People.pdf',
570
                   'moms_birthday.jpg']
571
        limit = 4
572
        for m in markers:
573
            objects = self.client.list_objects(self.container[0], limit=limit,
574
                                               marker=m)
575
            l = [elem['name'] for elem in self.obj[:8]]
576
            l.sort()
577
            start = l.index(m) + 1
578
            end = start + limit
579
            end = end if len(l) >= end else len(l)
580
            self.assertEqual(objects, l[start:end])
581

    
582
    #takes too long
583
    def _test_list_limit_exceeds(self):
584
        self.client.create_container('pithos')
585

    
586
        for i in range(10001):
587
            self.client.create_zero_length_object('pithos', i)
588

    
589
        self.assertEqual(10000, len(self.client.list_objects('pithos')))
590

    
591
    def test_list_empty_params(self):
592
        objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
593
        if objects:
594
            objects = objects.strip().split('\n')
595
        self.assertEqual(objects,
596
                         self.client.list_objects(self.container[0]))
597

    
598
    def test_list_pseudo_hierarchical_folders(self):
599
        objects = self.client.list_objects(self.container[1], prefix='photos',
600
                                           delimiter='/')
601
        self.assertEquals(['photos/animals/', 'photos/me.jpg',
602
                           'photos/plants/'], objects)
603

    
604
        objects = self.client.list_objects(self.container[1],
605
                                           prefix='photos/animals',
606
                                           delimiter='/')
607
        l = ['photos/animals/cats/', 'photos/animals/dogs/']
608
        self.assertEquals(l, objects)
609

    
610
        objects = self.client.list_objects(self.container[1], path='photos')
611
        self.assertEquals(['photos/me.jpg'], objects)
612

    
613
    def test_extended_list_json(self):
614
        objects = self.client.list_objects(self.container[1], format='json',
615
                                           limit=2, prefix='photos/animals',
616
                                           delimiter='/')
617
        self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
618
        self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
619

    
620
    def test_extended_list_xml(self):
621
        xml = self.client.list_objects(self.container[1], format='xml', limit=4,
622
                                       prefix='photos', delimiter='/')
623
        self.assert_extended(xml, 'xml', 'object', size=4)
624
        dirs = xml.getElementsByTagName('subdir')
625
        self.assertEqual(len(dirs), 2)
626
        self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
627
        self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
628

    
629
        objects = xml.getElementsByTagName('name')
630
        self.assertEqual(len(objects), 1)
631
        self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
632

    
633
    def test_list_meta_double_matching(self):
634
        meta = {'quality':'aaa', 'stock':'true'}
635
        self.client.update_object_metadata(self.container[0],
636
                                           self.obj[0]['name'], **meta)
637
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
638
        self.assertEqual(len(obj), 1)
639
        self.assertTrue(obj, self.obj[0]['name'])
640

    
641
    def test_list_using_meta(self):
642
        meta = {'quality':'aaa'}
643
        for o in self.obj[:2]:
644
            self.client.update_object_metadata(self.container[0], o['name'],
645
                                               **meta)
646
        meta = {'stock':'true'}
647
        for o in self.obj[3:5]:
648
            self.client.update_object_metadata(self.container[0], o['name'],
649
                                               **meta)
650

    
651
        obj = self.client.list_objects(self.container[0], meta='Quality')
652
        self.assertEqual(len(obj), 2)
653
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
654

    
655
        # test case insensitive
656
        obj = self.client.list_objects(self.container[0], meta='quality')
657
        self.assertEqual(len(obj), 2)
658
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
659

    
660
        # test multiple matches
661
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
662
        self.assertEqual(len(obj), 4)
663
        self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
664

    
665
        # test non 1-1 multiple match
666
        obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
667
        self.assertEqual(len(obj), 2)
668
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
669

    
670
    def test_if_modified_since(self):
671
        t = datetime.datetime.utcnow()
672
        t2 = t - datetime.timedelta(minutes=10)
673

    
674
        #add a new object
675
        self.upload_random_data(self.container[0], o_names[0])
676

    
677
        for f in DATE_FORMATS:
678
            past = t2.strftime(f)
679
            try:
680
                o = self.client.list_objects(self.container[0],
681
                                            if_modified_since=past)
682
                self.assertEqual(o,
683
                                 self.client.list_objects(self.container[0]))
684
            except Fault, f:
685
                self.failIf(f.status == 304) #fail if not modified
686

    
687
    def test_if_modified_since_invalid_date(self):
688
        headers = {'if-modified-since':''}
689
        o = self.client.list_objects(self.container[0], if_modified_since='')
690
        self.assertEqual(o, self.client.list_objects(self.container[0]))
691

    
692
    def test_if_not_modified_since(self):
693
        now = datetime.datetime.utcnow()
694
        since = now + datetime.timedelta(1)
695

    
696
        for f in DATE_FORMATS:
697
            args = {'if_modified_since':'%s' %since.strftime(f)}
698

    
699
            #assert not modified
700
            self.assert_raises_fault(304, self.client.list_objects,
701
                                     self.container[0], **args)
702

    
703
    def test_if_unmodified_since(self):
704
        now = datetime.datetime.utcnow()
705
        since = now + datetime.timedelta(1)
706

    
707
        for f in DATE_FORMATS:
708
            obj = self.client.list_objects(self.container[0],
709
                                           if_unmodified_since=since.strftime(f))
710

    
711
            #assert unmodified
712
            self.assertEqual(obj, self.client.list_objects(self.container[0]))
713

    
714
    def test_if_unmodified_since_precondition_failed(self):
715
        t = datetime.datetime.utcnow()
716
        t2 = t - datetime.timedelta(minutes=10)
717

    
718
        #add a new container
719
        self.client.create_container('dummy')
720

    
721
        for f in DATE_FORMATS:
722
            past = t2.strftime(f)
723

    
724
            args = {'if_unmodified_since':'%s' %past}
725

    
726
            #assert precondition failed
727
            self.assert_raises_fault(412, self.client.list_objects,
728
                                     self.container[0], **args)
729

    
730
class ContainerPut(BaseTestCase):
731
    def setUp(self):
732
        BaseTestCase.setUp(self)
733
        self.containers = ['c1', 'c2']
734

    
735
    def test_create(self):
736
        self.client.create_container(self.containers[0])
737
        containers = self.client.list_containers()
738
        self.assertTrue(self.containers[0] in containers)
739
        self.assert_container_exists(self.containers[0])
740

    
741
    def test_create_twice(self):
742
        self.client.create_container(self.containers[0])
743
        self.assertTrue(not self.client.create_container(self.containers[0]))
744

    
745
    def test_quota(self):
746
        self.client.create_container(self.containers[0])
747

    
748
        policy = {'quota':100}
749
        self.client.set_container_policies('c1', **policy)
750

    
751
        meta = self.client.retrieve_container_metadata('c1')
752
        self.assertTrue('x-container-policy-quota' in meta)
753
        self.assertEqual(meta['x-container-policy-quota'], '100')
754

    
755
        args = ['c1', 'o1']
756
        kwargs = {'length':101}
757
        self.assert_raises_fault(413, self.upload_random_data, *args, **kwargs)
758

    
759
        #reset quota
760
        policy = {'quota':0}
761
        self.client.set_container_policies('c1', **policy)
762

    
763
class ContainerPost(BaseTestCase):
764
    def setUp(self):
765
        BaseTestCase.setUp(self)
766
        self.container = 'apples'
767
        self.client.create_container(self.container)
768

    
769
    def test_update_meta(self):
770
        meta = {'test':'test33',
771
                'tost':'tost22'}
772
        self.client.update_container_metadata(self.container, **meta)
773
        headers = self.client.retrieve_container_metadata(self.container)
774
        for k,v in meta.items():
775
            k = 'x-container-meta-%s' % k
776
            self.assertTrue(headers[k])
777
            self.assertEqual(headers[k], v)
778

    
779
class ContainerDelete(BaseTestCase):
780
    def setUp(self):
781
        BaseTestCase.setUp(self)
782
        self.containers = ['c1', 'c2']
783
        for c in self.containers:
784
            self.client.create_container(c)
785

    
786
    def test_delete(self):
787
        status = self.client.delete_container(self.containers[0])[0]
788
        self.assertEqual(status, 204)
789

    
790
    def test_delete_non_empty(self):
791
        self.upload_random_data(self.containers[1], o_names[0])
792
        self.assert_raises_fault(409, self.client.delete_container,
793
                                 self.containers[1])
794

    
795
    def test_delete_invalid(self):
796
        self.assert_raises_fault(404, self.client.delete_container, 'c3')
797

    
798
class ObjectGet(BaseTestCase):
799
    def setUp(self):
800
        BaseTestCase.setUp(self)
801
        self.containers = ['c1', 'c2']
802
        #create some containers
803
        for c in self.containers:
804
            self.client.create_container(c)
805

    
806
        #upload a file
807
        names = ('obj1', 'obj2')
808
        self.objects = []
809
        for n in names:
810
            self.objects.append(self.upload_random_data(self.containers[1], n))
811

    
812
    def test_versions(self):
813
        c = self.containers[1]
814
        o = self.objects[0]
815
        b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
816
        self.assert_versionlist_structure(b)
817

    
818
        #update meta
819
        meta = {'quality':'AAA', 'stock':True}
820
        self.client.update_object_metadata(c, o['name'], **meta)
821

    
822
        a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
823
        self.assert_versionlist_structure(a)
824
        self.assertEqual(len(b)+1, len(a))
825
        self.assertEqual(b, a[:-1])
826

    
827
        #get exact previous version metadata
828
        v = a[-2][0]
829
        v_meta = self.client.retrieve_object_metadata(c, o['name'],
830
                                                      restricted=True,
831
                                                      version=v)
832
        for k in meta.keys():
833
            self.assertTrue(k not in v_meta)
834

    
835
        #update obejct
836
        data = get_random_data()
837
        self.client.update_object(c, o['name'], StringIO(data))
838

    
839
        aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
840
        self.assert_versionlist_structure(aa)
841
        self.assertEqual(len(a)+1, len(aa))
842
        self.assertEqual(a, aa[:-1])
843

    
844
        #get exact previous version
845
        v = aa[-3][0]
846
        v_data = self.client.retrieve_object_version(c, o['name'], version=v)
847
        self.assertEqual(o['data'], v_data)
848
        self.assertEqual(self.client.retrieve_object(c, o['name']),
849
                         '%s%s' %(v_data, data))
850

    
851
    def test_get(self):
852
        #perform get
853
        o = self.client.retrieve_object(self.containers[1],
854
                                        self.objects[0]['name'],
855
                                        self.objects[0]['meta'])
856
        self.assertEqual(o, self.objects[0]['data'])
857

    
858
    def test_objects_with_trailing_spaces(self):
859
        self.client.create_container('test')
860
        #create 'a' object
861
        self.upload_random_data('test', 'a')
862
        #look for 'a ' object
863
        self.assert_raises_fault(404, self.client.retrieve_object,
864
                                 'test', 'a ')
865

    
866
        #delete 'a' object
867
        self.client.delete_object('test', 'a')
868
        self.assert_raises_fault(404, self.client.retrieve_object,
869
                                 'test', 'a')
870

    
871
        #create 'a ' object
872
        self.upload_random_data('test', 'a ')
873
        #look for 'a' object
874
        self.assert_raises_fault(404, self.client.retrieve_object,
875
                                 'test', 'a')
876

    
877
    def test_get_invalid(self):
878
        self.assert_raises_fault(404, self.client.retrieve_object,
879
                                 self.containers[0], self.objects[0]['name'])
880

    
881
    def test_get_partial(self):
882
        #perform get with range
883
        status, headers, data = self.client.request_object(self.containers[1],
884
                                                            self.objects[0]['name'],
885
                                                            range='bytes=0-499')
886

    
887
        #assert successful partial content
888
        self.assertEqual(status, 206)
889

    
890
        #assert content-type
891
        self.assertEqual(headers['content-type'],
892
                         self.objects[0]['meta']['content_type'])
893

    
894
        #assert content length
895
        self.assertEqual(int(headers['content-length']), 500)
896

    
897
        #assert content
898
        self.assertEqual(self.objects[0]['data'][:500], data)
899

    
900
    def test_get_final_500(self):
901
        #perform get with range
902
        headers = {'range':'bytes=-500'}
903
        status, headers, data = self.client.request_object(self.containers[1],
904
                                                            self.objects[0]['name'],
905
                                                            range='bytes=-500')
906

    
907
        #assert successful partial content
908
        self.assertEqual(status, 206)
909

    
910
        #assert content-type
911
        self.assertEqual(headers['content-type'],
912
                         self.objects[0]['meta']['content_type'])
913

    
914
        #assert content length
915
        self.assertEqual(int(headers['content-length']), 500)
916

    
917
        #assert content
918
        self.assertTrue(self.objects[0]['data'][-500:], data)
919

    
920
    def test_get_rest(self):
921
        #perform get with range
922
        offset = len(self.objects[0]['data']) - 500
923
        status, headers, data = self.client.request_object(self.containers[1],
924
                                                self.objects[0]['name'],
925
                                                range='bytes=%s-' %offset)
926

    
927
        #assert successful partial content
928
        self.assertEqual(status, 206)
929

    
930
        #assert content-type
931
        self.assertEqual(headers['content-type'],
932
                         self.objects[0]['meta']['content_type'])
933

    
934
        #assert content length
935
        self.assertEqual(int(headers['content-length']), 500)
936

    
937
        #assert content
938
        self.assertTrue(self.objects[0]['data'][-500:], data)
939

    
940
    def test_get_range_not_satisfiable(self):
941
        #perform get with range
942
        offset = len(self.objects[0]['data']) + 1
943

    
944
        #assert range not satisfiable
945
        self.assert_raises_fault(416, self.client.retrieve_object,
946
                                 self.containers[1], self.objects[0]['name'],
947
                                 range='bytes=0-%s' %offset)
948

    
949
    def test_multiple_range(self):
950
        #perform get with multiple range
951
        ranges = ['0-499', '-500', '1000-']
952
        bytes = 'bytes=%s' % ','.join(ranges)
953
        status, headers, data = self.client.request_object(self.containers[1],
954
                                                           self.objects[0]['name'],
955
                                                           range=bytes)
956

    
957
        # assert partial content
958
        self.assertEqual(status, 206)
959

    
960
        # assert Content-Type of the reply will be multipart/byteranges
961
        self.assertTrue(headers['content-type'])
962
        content_type_parts = headers['content-type'].split()
963
        self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
964

    
965
        boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
966
        cparts = data.split(boundary)[1:-1]
967

    
968
        # assert content parts are exactly 2
969
        self.assertEqual(len(cparts), len(ranges))
970

    
971
        # for each content part assert headers
972
        i = 0
973
        for cpart in cparts:
974
            content = cpart.split('\r\n')
975
            headers = content[1:3]
976
            content_range = headers[0].split(': ')
977
            self.assertEqual(content_range[0], 'Content-Range')
978

    
979
            r = ranges[i].split('-')
980
            if not r[0] and not r[1]:
981
                pass
982
            elif not r[0]:
983
                start = len(self.objects[0]['data']) - int(r[1])
984
                end = len(self.objects[0]['data'])
985
            elif not r[1]:
986
                start = int(r[0])
987
                end = len(self.objects[0]['data'])
988
            else:
989
                start = int(r[0])
990
                end = int(r[1]) + 1
991
            fdata = self.objects[0]['data'][start:end]
992
            sdata = '\r\n'.join(content[4:-1])
993
            self.assertEqual(len(fdata), len(sdata))
994
            self.assertEquals(fdata, sdata)
995
            i+=1
996

    
997
    def test_multiple_range_not_satisfiable(self):
998
        #perform get with multiple range
999
        out_of_range = len(self.objects[0]['data']) + 1
1000
        ranges = ['0-499', '-500', '%d-' %out_of_range]
1001
        bytes = 'bytes=%s' % ','.join(ranges)
1002

    
1003
        # assert partial content
1004
        self.assert_raises_fault(416, self.client.retrieve_object,
1005
                                 self.containers[1],
1006
                                 self.objects[0]['name'], range=bytes)
1007

    
1008
    def test_get_with_if_match(self):
1009
        #perform get with If-Match
1010
        etag = self.objects[0]['hash']
1011
        status, headers, data = self.client.request_object(self.containers[1],
1012
                                                           self.objects[0]['name'],
1013
                                                           if_match=etag)
1014
        #assert get success
1015
        self.assertEqual(status, 200)
1016

    
1017
        #assert content-type
1018
        self.assertEqual(headers['content-type'],
1019
                         self.objects[0]['meta']['content_type'])
1020

    
1021
        #assert response content
1022
        self.assertEqual(self.objects[0]['data'], data)
1023

    
1024
    def test_get_with_if_match_star(self):
1025
        #perform get with If-Match *
1026
        headers = {'if-match':'*'}
1027
        status, headers, data = self.client.request_object(self.containers[1],
1028
                                                self.objects[0]['name'],
1029
                                                **headers)
1030
        #assert get success
1031
        self.assertEqual(status, 200)
1032

    
1033
        #assert content-type
1034
        self.assertEqual(headers['content-type'],
1035
                         self.objects[0]['meta']['content_type'])
1036

    
1037
        #assert response content
1038
        self.assertEqual(self.objects[0]['data'], data)
1039

    
1040
    def test_get_with_multiple_if_match(self):
1041
        #perform get with If-Match
1042
        etags = [i['hash'] for i in self.objects if i]
1043
        etags = ','.join('"%s"' % etag for etag in etags)
1044
        status, headers, data = self.client.request_object(self.containers[1],
1045
                                                           self.objects[0]['name'],
1046
                                                           if_match=etags)
1047
        #assert get success
1048
        self.assertEqual(status, 200)
1049

    
1050
        #assert content-type
1051
        self.assertEqual(headers['content-type'],
1052
                         self.objects[0]['meta']['content_type'])
1053

    
1054
        #assert content-type
1055
        self.assertEqual(headers['content-type'],
1056
                         self.objects[0]['meta']['content_type'])
1057

    
1058
        #assert response content
1059
        self.assertEqual(self.objects[0]['data'], data)
1060

    
1061
    def test_if_match_precondition_failed(self):
1062
        #assert precondition failed
1063
        self.assert_raises_fault(412, self.client.retrieve_object,
1064
                                 self.containers[1],
1065
                                 self.objects[0]['name'], if_match='123')
1066

    
1067
    def test_if_none_match(self):
1068
        #perform get with If-None-Match
1069
        status, headers, data = self.client.request_object(self.containers[1],
1070
                                                           self.objects[0]['name'],
1071
                                                           if_none_match='123')
1072

    
1073
        #assert get success
1074
        self.assertEqual(status, 200)
1075

    
1076
        #assert content-type
1077
        self.assertEqual(headers['content_type'],
1078
                         self.objects[0]['meta']['content_type'])
1079

    
1080
    def test_if_none_match(self):
1081
        #perform get with If-None-Match * and assert not modified
1082
        self.assert_raises_fault(304, self.client.retrieve_object,
1083
                                 self.containers[1],
1084
                                 self.objects[0]['name'],
1085
                                 if_none_match='*')
1086

    
1087
    def test_if_none_match_not_modified(self):
1088
        #perform get with If-None-Match and assert not modified
1089
        self.assert_raises_fault(304, self.client.retrieve_object,
1090
                                 self.containers[1],
1091
                                 self.objects[0]['name'],
1092
                                 if_none_match=self.objects[0]['hash'])
1093

    
1094
        meta = self.client.retrieve_object_metadata(self.containers[1],
1095
                                                    self.objects[0]['name'])
1096
        self.assertEqual(meta['etag'], self.objects[0]['hash'])
1097

    
1098
    def test_if_modified_since(self):
1099
        t = datetime.datetime.utcnow()
1100
        t2 = t - datetime.timedelta(minutes=10)
1101

    
1102
        #modify the object
1103
        self.upload_data(self.containers[1],
1104
                           self.objects[0]['name'],
1105
                           self.objects[0]['data'][:200])
1106

    
1107
        for f in DATE_FORMATS:
1108
            past = t2.strftime(f)
1109

    
1110
            headers = {'if-modified-since':'%s' %past}
1111
            try:
1112
                o = self.client.retrieve_object(self.containers[1],
1113
                                                self.objects[0]['name'],
1114
                                                if_modified_since=past)
1115
                self.assertEqual(o,
1116
                                 self.client.retrieve_object(self.containers[1],
1117
                                                             self.objects[0]['name']))
1118
            except Fault, f:
1119
                self.failIf(f.status == 304)
1120

    
1121
    def test_if_modified_since_invalid_date(self):
1122
        o = self.client.retrieve_object(self.containers[1],
1123
                                        self.objects[0]['name'],
1124
                                        if_modified_since='')
1125
        self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1126
                                                        self.objects[0]['name']))
1127

    
1128
    def test_if_not_modified_since(self):
1129
        now = datetime.datetime.utcnow()
1130
        since = now + datetime.timedelta(1)
1131

    
1132
        for f in DATE_FORMATS:
1133
            #assert not modified
1134
            self.assert_raises_fault(304, self.client.retrieve_object,
1135
                                     self.containers[1], self.objects[0]['name'],
1136
                                     if_modified_since=since.strftime(f))
1137

    
1138
    def test_if_unmodified_since(self):
1139
        now = datetime.datetime.utcnow()
1140
        since = now + datetime.timedelta(1)
1141

    
1142
        for f in DATE_FORMATS:
1143
            t = since.strftime(f)
1144
            status, headers, data = self.client.request_object(self.containers[1],
1145
                                                               self.objects[0]['name'],
1146
                                                               if_unmodified_since=t)
1147
            #assert success
1148
            self.assertEqual(status, 200)
1149
            self.assertEqual(self.objects[0]['data'], data)
1150

    
1151
            #assert content-type
1152
            self.assertEqual(headers['content-type'],
1153
                             self.objects[0]['meta']['content_type'])
1154

    
1155
    def test_if_unmodified_since_precondition_failed(self):
1156
        t = datetime.datetime.utcnow()
1157
        t2 = t - datetime.timedelta(minutes=10)
1158

    
1159
        #modify the object
1160
        self.upload_data(self.containers[1],
1161
                           self.objects[0]['name'],
1162
                           self.objects[0]['data'][:200])
1163

    
1164
        for f in DATE_FORMATS:
1165
            past = t2.strftime(f)
1166
            #assert precondition failed
1167
            self.assert_raises_fault(412, self.client.retrieve_object,
1168
                                     self.containers[1], self.objects[0]['name'],
1169
                                     if_unmodified_since=past)
1170

    
1171
    def test_hashes(self):
1172
        l = 8388609
1173
        fname = 'largefile'
1174
        o = self.upload_random_data(self.containers[1], fname, l)
1175
        if o:
1176
            body = self.client.retrieve_object(self.containers[1], fname,
1177
                                               format='json')
1178
            hashes = body['hashes']
1179
            block_size = body['block_size']
1180
            block_hash = body['block_hash']
1181
            block_num = l/block_size if l/block_size == 0 else l/block_size + 1
1182
            self.assertTrue(len(hashes), block_num)
1183
            i = 0
1184
            for h in hashes:
1185
                start = i * block_size
1186
                end = (i + 1) * block_size
1187
                hash = compute_block_hash(o['data'][start:end], block_hash)
1188
                self.assertEqual(h, hash)
1189
                i += 1
1190

    
1191
class ObjectPut(BaseTestCase):
1192
    def setUp(self):
1193
        BaseTestCase.setUp(self)
1194
        self.container = 'c1'
1195
        self.client.create_container(self.container)
1196

    
1197
    def test_upload(self):
1198
        name = o_names[0]
1199
        meta = {'test':'test1'}
1200
        o = self.upload_random_data(self.container, name, **meta)
1201

    
1202
        headers = self.client.retrieve_object_metadata(self.container,
1203
                                                       name,
1204
                                                       restricted=True)
1205
        self.assertTrue('test' in headers.keys())
1206
        self.assertEqual(headers['test'], meta['test'])
1207

    
1208
        #assert uploaded content
1209
        status, h, data = self.client.request_object(self.container, name)
1210
        self.assertEqual(len(o['data']), int(h['content-length']))
1211
        self.assertEqual(o['data'], data)
1212

    
1213
        #assert content-type
1214
        self.assertEqual(h['content-type'], o['meta']['content_type'])
1215

    
1216
    def _test_maximum_upload_size_exceeds(self):
1217
        name = o_names[0]
1218
        meta = {'test':'test1'}
1219
        #upload 100MB
1220
        length=1024*1024*100
1221
        self.assert_raises_fault(400, self.upload_random_data, self.container,
1222
                                 name, length, **meta)
1223

    
1224
    def test_upload_with_name_containing_slash(self):
1225
        name = '/%s' % o_names[0]
1226
        meta = {'test':'test1'}
1227
        o = self.upload_random_data(self.container, name, **meta)
1228

    
1229
        self.assertEqual(o['data'],
1230
                         self.client.retrieve_object(self.container, name))
1231

    
1232
        self.assertTrue(name in self.client.list_objects(self.container))
1233

    
1234
    def test_create_directory_marker(self):
1235
        self.client.create_directory_marker(self.container, 'foo')
1236
        meta = self.client.retrieve_object_metadata(self.container, 'foo')
1237
        self.assertEqual(meta['content-length'], '0')
1238
        self.assertEqual(meta['content-type'], 'application/directory')
1239

    
1240
    def test_upload_unprocessable_entity(self):
1241
        meta={'etag':'123', 'test':'test1'}
1242

    
1243
        #assert unprocessable entity
1244
        self.assert_raises_fault(422, self.upload_random_data, self.container,
1245
                                 o_names[0], **meta)
1246

    
1247
    def test_chunked_transfer(self):
1248
        data = get_random_data()
1249
        objname = 'object'
1250
        self.client.create_object_using_chunks(self.container, objname,
1251
                                               StringIO(data))
1252

    
1253
        uploaded_data = self.client.retrieve_object(self.container, objname)
1254
        self.assertEqual(data, uploaded_data)
1255

    
1256
    def test_manifestation(self):
1257
        prefix = 'myobject/'
1258
        data = ''
1259
        for i in range(5):
1260
            part = '%s%d' %(prefix, i)
1261
            o = self.upload_random_data(self.container, part)
1262
            data += o['data']
1263

    
1264
        manifest = '%s/%s' %(self.container, prefix)
1265
        self.client.create_manifestation(self.container, 'large-object', manifest)
1266

    
1267
        self.assert_object_exists(self.container, 'large-object')
1268
        self.assertEqual(data, self.client.retrieve_object(self.container,
1269
                                                           'large-object'))
1270
        
1271
        r = self.client.retrieve_object_hashmap(self.container,'large-object')
1272
        hashes = r['hashes']
1273
        block_size = int(r['block_size'])
1274
        block_hash = r['block_hash']
1275
        l = len(data)
1276
        block_num = l/block_size if l/block_size != 0 else l/block_size + 1
1277
        self.assertEqual(block_num, len(hashes))
1278
        
1279
        #wrong manifestation
1280
        self.client.create_manifestation(self.container, 'large-object',
1281
                                         '%s/invalid' % self.container)
1282
        self.assertEqual('', self.client.retrieve_object(self.container,
1283
                                                         'large-object'))
1284

    
1285
    def test_create_zero_length_object(self):
1286
        c = self.container
1287
        o = 'object'
1288
        zero = self.client.create_zero_length_object(c, o)
1289
        zero_meta = self.client.retrieve_object_metadata(c, o)
1290
        zero_hash = self.client.retrieve_object_hashmap(c, o)["hashes"]
1291
        zero_data = self.client.retrieve_object(c, o)
1292

    
1293
        self.assertEqual(int(zero_meta['content-length']), 0)
1294
        hasher = newhasher('sha256')
1295
        hasher.update("")
1296
        emptyhash = hasher.digest()
1297
        self.assertEqual(zero_hash, [hexlify(emptyhash)])
1298
        self.assertEqual(zero_data, '')
1299

    
1300
    def test_create_object_by_hashmap(self):
1301
        c = self.container
1302
        o = 'object'
1303
        self.upload_random_data(c, o)
1304
        hashmap = self.client.retrieve_object(c, o, format='json')
1305
        o2 = 'object-copy'
1306
        self.client.create_object_by_hashmap(c, o2, hashmap)
1307
        self.assertEqual(self.client.retrieve_object(c, o),
1308
                         self.client.retrieve_object(c, o))
1309

    
1310
class ObjectCopy(BaseTestCase):
1311
    def setUp(self):
1312
        BaseTestCase.setUp(self)
1313
        self.containers = ['c1', 'c2']
1314
        for c in self.containers:
1315
            self.client.create_container(c)
1316
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1317

    
1318
    def test_copy(self):
1319
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1320
                             self.containers[0], self.obj['name']):
1321
            #perform copy
1322
            meta = {'test':'testcopy'}
1323
            status = self.client.copy_object(self.containers[0],
1324
                                              self.obj['name'],
1325
                                              self.containers[0],
1326
                                              'testcopy',
1327
                                              meta)[0]
1328

    
1329
            #assert copy success
1330
            self.assertEqual(status, 201)
1331

    
1332
            #assert access the new object
1333
            headers = self.client.retrieve_object_metadata(self.containers[0],
1334
                                                           'testcopy')
1335
            self.assertTrue('x-object-meta-test' in headers.keys())
1336
            self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1337

    
1338
            #assert etag is the same
1339
            self.assertEqual(headers['etag'], self.obj['hash'])
1340

    
1341
            #assert src object still exists
1342
            self.assert_object_exists(self.containers[0], self.obj['name'])
1343

    
1344
    def test_copy_from_different_container(self):
1345
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1346
                             self.containers[0], self.obj['name']):
1347
            meta = {'test':'testcopy'}
1348
            status = self.client.copy_object(self.containers[0],
1349
                                             self.obj['name'],
1350
                                             self.containers[1],
1351
                                             'testcopy',
1352
                                             meta)[0]
1353
            self.assertEqual(status, 201)
1354

    
1355
            # assert updated metadata
1356
            meta = self.client.retrieve_object_metadata(self.containers[1],
1357
                                                           'testcopy',
1358
                                                           restricted=True)
1359
            self.assertTrue('test' in meta.keys())
1360
            self.assertTrue(meta['test'], 'testcopy')
1361

    
1362
            #assert src object still exists
1363
            self.assert_object_exists(self.containers[0], self.obj['name'])
1364

    
1365
    def test_copy_invalid(self):
1366
        #copy from invalid object
1367
        meta = {'test':'testcopy'}
1368
        self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1369
                                 'test.py', self.containers[1], 'testcopy', meta)
1370

    
1371
        #copy from invalid container
1372
        meta = {'test':'testcopy'}
1373
        self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1374
                                 self.obj['name'], self.containers[1],
1375
                                 'testcopy', meta)
1376

    
1377
class ObjectMove(BaseTestCase):
1378
    def setUp(self):
1379
        BaseTestCase.setUp(self)
1380
        self.containers = ['c1', 'c2']
1381
        for c in self.containers:
1382
            self.client.create_container(c)
1383
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1384

    
1385
    def test_move(self):
1386
        meta = self.client.retrieve_object_metadata(self.containers[0],
1387
                                                    self.obj['name'])
1388
        self.assertTrue('x-object-uuid' in meta)
1389
        uuid = meta['x-object-uuid']
1390

    
1391
        #perform move
1392
        meta = {'test':'testcopy'}
1393
        src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1394
        status = self.client.move_object(self.containers[0], self.obj['name'],
1395
                                         self.containers[0], 'testcopy',
1396
                                         meta)[0]
1397

    
1398
        #assert successful move
1399
        self.assertEqual(status, 201)
1400

    
1401
        #assert updated metadata
1402
        meta = self.client.retrieve_object_metadata(self.containers[0],
1403
                                                    'testcopy')
1404
        self.assertTrue('x-object-meta-test' in meta.keys())
1405
        self.assertTrue(meta['x-object-meta-test'], 'testcopy')
1406

    
1407
        #assert same uuid
1408
        self.assertTrue(meta['x-object-uuid'], uuid)
1409

    
1410
        #assert src object no more exists
1411
        self.assert_object_not_exists(self.containers[0], self.obj['name'])
1412

    
1413
class ObjectPost(BaseTestCase):
1414
    def setUp(self):
1415
        BaseTestCase.setUp(self)
1416
        self.containers = ['c1', 'c2']
1417
        for c in self.containers:
1418
            self.client.create_container(c)
1419
        self.obj = []
1420
        for i in range(2):
1421
            self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1422

    
1423
    def test_update_meta(self):
1424
        with AssertUUidInvariant(self.client.retrieve_object_metadata,
1425
                                 self.containers[0],
1426
                                 self.obj[0]['name']):
1427
            #perform update metadata
1428
            more = {'foo': 'foo', 'bar': 'bar', 'f' * 114: 'b' * 256}
1429
            status = self.client.update_object_metadata(self.containers[0],
1430
                                                        self.obj[0]['name'],
1431
                                                        **more)[0]
1432
            #assert request accepted
1433
            self.assertEqual(status, 202)
1434

    
1435
            #assert old metadata are still there
1436
            headers = self.client.retrieve_object_metadata(self.containers[0],
1437
                                                           self.obj[0]['name'],
1438
                                                           restricted=True)
1439
            #assert new metadata have been updated
1440
            for k,v in more.items():
1441
                self.assertTrue(k in headers.keys())
1442
                self.assertTrue(headers[k], v)
1443

    
1444
            #out of limits
1445
            more = {'f' * 114: 'b' * 257}
1446
            self.assert_raises_fault(400, self.client.update_object_metadata,
1447
                                                        self.containers[0],
1448
                                                        self.obj[0]['name'],
1449
                                                        **more)
1450
            
1451
            #perform update metadata
1452
            more = {'α': 'β' * 256}
1453
            status = self.client.update_object_metadata(self.containers[0],
1454
                                                        self.obj[0]['name'],
1455
                                                        **more)[0]
1456
            #assert request accepted
1457
            self.assertEqual(status, 202)
1458
            
1459
            #assert old metadata are still there
1460
            headers = self.client.retrieve_object_metadata(self.containers[0],
1461
                                                           self.obj[0]['name'],
1462
                                                           restricted=True)
1463
            #assert new metadata have been updated
1464
            for k,v in more.items():
1465
                self.assertTrue(k in headers.keys())
1466
                self.assertTrue(headers[k], v)
1467
            
1468
            #out of limits
1469
            more = {'α': 'β' * 257}
1470
            self.assert_raises_fault(400, self.client.update_object_metadata,
1471
                                                        self.containers[0],
1472
                                                        self.obj[0]['name'],
1473
                                                        **more)
1474
    
1475
    def test_update_object(self,
1476
                           first_byte_pos=0,
1477
                           last_byte_pos=499,
1478
                           instance_length = True,
1479
                           content_length = 500):
1480
        with AssertUUidInvariant(self.client.retrieve_object_metadata,
1481
                                 self.containers[0],
1482
                                 self.obj[0]['name']):
1483
            l = len(self.obj[0]['data'])
1484
            range = 'bytes %d-%d/%s' %(first_byte_pos,
1485
                                           last_byte_pos,
1486
                                            l if instance_length else '*')
1487
            partial = last_byte_pos - first_byte_pos + 1
1488
            length = first_byte_pos + partial
1489
            data = get_random_data(partial)
1490
            args = {'content_type':'application/octet-stream',
1491
                    'content_range':'%s' %range}
1492
            if content_length:
1493
                args['content_length'] = content_length
1494

    
1495
            r = self.client.update_object(self.containers[0], self.obj[0]['name'],
1496
                                      StringIO(data), **args)
1497
            status = r[0]
1498
            etag = r[1]['etag']
1499
            if partial < 0 or (instance_length and l <= last_byte_pos):
1500
                self.assertEqual(status, 202)
1501
            else:
1502
                self.assertEqual(status, 204)
1503
                #check modified object
1504
                content = self.client.retrieve_object(self.containers[0],
1505
                                                  self.obj[0]['name'])
1506
                self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1507
                self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1508
                self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1509
                self.assertEqual(etag, compute_md5_hash(content))
1510

    
1511
    def test_update_object_lt_blocksize(self):
1512
        self.test_update_object(10, 20, content_length=None)
1513

    
1514
    def test_update_object_gt_blocksize(self):
1515
        o = self.upload_random_data(self.containers[0], o_names[1],
1516
                                length=4*1024*1024+5)
1517
        c = self.containers[0]
1518
        o_name = o['name']
1519
        o_data = o['data']
1520
        first_byte_pos = 4*1024*1024+1
1521
        last_byte_pos = 4*1024*1024+4
1522
        l = last_byte_pos - first_byte_pos + 1
1523
        data = get_random_data(l)
1524
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1525
        self.client.update_object(c, o_name, StringIO(data), content_range=range)
1526
        content = self.client.retrieve_object(c, o_name)
1527
        self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1528
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1529
        self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1530

    
1531
    def test_update_object_divided_by_blocksize(self):
1532
        o = self.upload_random_data(self.containers[0], o_names[1],
1533
                                length=4*1024*1024+5)
1534
        c = self.containers[0]
1535
        o_name = o['name']
1536
        o_data = o['data']
1537
        first_byte_pos = 4*1024*1024
1538
        last_byte_pos = 5*1024*1024
1539
        l = last_byte_pos - first_byte_pos + 1
1540
        data = get_random_data(l)
1541
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1542
        self.client.update_object(c, o_name, StringIO(data), content_range=range)
1543
        content = self.client.retrieve_object(c, o_name)
1544
        self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1545
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1546
        self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1547

    
1548
    def test_update_object_no_content_length(self):
1549
        self.test_update_object(content_length = None)
1550

    
1551
    def test_update_object_invalid_content_length(self):
1552
        with AssertContentInvariant(self.client.retrieve_object,
1553
                                    self.containers[0], self.obj[0]['name']):
1554
            self.assert_raises_fault(400, self.test_update_object,
1555
                                     content_length = 1000)
1556

    
1557
    def _test_update_object_invalid_range(self):
1558
        with AssertContentInvariant(self.client.retrieve_object,
1559
                                    self.containers[0], self.obj[0]['name']):
1560
            self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1561

    
1562
    def _test_update_object_invalid_range_and_length(self):
1563
        with AssertContentInvariant(self.client.retrieve_object,
1564
                                    self.containers[0], self.obj[0]['name']):
1565
            self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1566
                                     -1)
1567

    
1568
    def test_update_object_invalid_range_with_no_content_length(self):
1569
        with AssertContentInvariant(self.client.retrieve_object,
1570
                                    self.containers[0], self.obj[0]['name']):
1571
            self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1572
                                     content_length = None)
1573

    
1574
    def test_update_object_out_of_limits(self):
1575
        with AssertContentInvariant(self.client.retrieve_object,
1576
                                    self.containers[0], self.obj[0]['name']):
1577
            l = len(self.obj[0]['data'])
1578
            self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1579

    
1580
    def test_append(self):
1581
        data = get_random_data(500)
1582
        headers = {}
1583
        self.client.update_object(self.containers[0], self.obj[0]['name'],
1584
                                  StringIO(data), content_length=500,
1585
                                  content_type='application/octet-stream')
1586

    
1587
        content = self.client.retrieve_object(self.containers[0],
1588
                                              self.obj[0]['name'])
1589
        self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1590
        self.assertEqual(content[:-500], self.obj[0]['data'])
1591

    
1592
    def test_update_with_chunked_transfer(self):
1593
        data = get_random_data(500)
1594
        dl = len(data)
1595
        fl = len(self.obj[0]['data'])
1596

    
1597
        self.client.update_object_using_chunks(self.containers[0],
1598
                                               self.obj[0]['name'],
1599
                                               StringIO(data),
1600
                                               offset=0,
1601
                                               content_type='application/octet-stream')
1602

    
1603
        #check modified object
1604
        content = self.client.retrieve_object(self.containers[0],
1605
                                              self.obj[0]['name'])
1606
        self.assertEqual(content[0:dl], data)
1607
        self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1608

    
1609
    def test_update_from_other_object(self):
1610
        c = self.containers[0]
1611
        src = o_names[0]
1612
        dest = 'object'
1613

    
1614
        source_data = self.client.retrieve_object(c, src)
1615
        source_meta = self.client.retrieve_object_metadata(c, src)
1616
        source_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1617

    
1618
        #update zero length object
1619
        self.client.create_zero_length_object(c, dest)
1620
        source_object = '/%s/%s' % (c, src)
1621
        self.client.update_from_other_source(c, dest, source_object)
1622
        dest_data = self.client.retrieve_object(c, src)
1623
        dest_meta = self.client.retrieve_object_metadata(c, dest)
1624
        dest_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1625
        self.assertEqual(source_data, dest_data)
1626
        self.assertEqual(source_hash, dest_hash)
1627

    
1628
        #test append
1629
        self.client.update_from_other_source(c, dest, source_object)
1630
        content = self.client.retrieve_object(c, dest)
1631
        self.assertEqual(source_data * 2, content)
1632

    
1633
    def test_update_range_from_other_object(self):
1634
        c = self.containers[0]
1635
        dest = 'object'
1636

    
1637
        #test update range
1638
        src = self.obj[1]['name']
1639
        src_data = self.client.retrieve_object(c, src)
1640

    
1641
        #update zero length object
1642
        prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1643
        source_object = '/%s/%s' % (c, src)
1644
        first_byte_pos = 4*1024*1024+1
1645
        last_byte_pos = 4*1024*1024+4
1646
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1647
        self.client.update_from_other_source(c, dest, source_object,
1648
                                             content_range=range)
1649
        content = self.client.retrieve_object(c, dest)
1650
        self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1651
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1652
        self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1653

    
1654
    def test_update_hashes_from_other_object(self):
1655
        c = self.containers[0]
1656
        dest = 'object'
1657

    
1658
        #test update range
1659
        src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1660

    
1661
        #update zero length object
1662
        prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1663
        source_object = '/%s/%s' % (c, o_names[0])
1664
        first_byte_pos = 4*1024*1024
1665
        last_byte_pos = 5*1024*1024
1666
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1667
        self.client.update_from_other_source(c, dest, source_object,
1668
                                             content_range=range)
1669
        content = self.client.retrieve_object(c, dest)
1670
        self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1671
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1672
        self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1673

    
1674

    
1675
    def test_update_zero_length_object(self):
1676
        c = self.containers[0]
1677
        o = 'object'
1678
        other = 'other'
1679
        zero = self.client.create_zero_length_object(c, o)
1680

    
1681
        data = get_random_data()
1682
        self.client.update_object(c, o, StringIO(data))
1683
        self.client.create_object(c, other, StringIO(data))
1684

    
1685
        self.assertEqual(self.client.retrieve_object(c, o),
1686
                         self.client.retrieve_object(c, other))
1687

    
1688
        self.assertEqual(self.client.retrieve_object_hashmap(c, o)["hashes"],
1689
                         self.client.retrieve_object_hashmap(c, other)["hashes"])
1690

    
1691
class ObjectDelete(BaseTestCase):
1692
    def setUp(self):
1693
        BaseTestCase.setUp(self)
1694
        self.containers = ['c1', 'c2']
1695
        for c in self.containers:
1696
            self.client.create_container(c)
1697
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1698

    
1699
    def test_delete(self):
1700
        #perform delete object
1701
        self.client.delete_object(self.containers[0], self.obj['name'])[0]
1702

    
1703
    def test_delete_invalid(self):
1704
        #assert item not found
1705
        self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1706
                                 self.obj['name'])
1707

    
1708
class ListSharing(BaseTestCase):
1709
    def setUp(self):
1710
        BaseTestCase.setUp(self)
1711
        for i in range(2):
1712
            self.client.create_container('c%s' %i)
1713
        self.client.create_container('c')
1714
        for i in range(2):
1715
            self.upload_random_data('c1', 'o%s' %i)
1716
        accounts = OTHER_ACCOUNTS.copy()
1717
        self.o1_sharing_with = accounts.popitem()
1718
        self.o1_sharing = [self.o1_sharing_with[1]]
1719
        self.client.share_object('c1', 'o1', self.o1_sharing, read=True)
1720

    
1721
        l = []
1722
        for i in range(2):
1723
            l.append(accounts.popitem())
1724

    
1725
    def test_list_other_shared(self):
1726
        self.other = Pithos_Client(get_url(),
1727
                              self.o1_sharing_with[0],
1728
                              self.o1_sharing_with[1])
1729
        self.assertTrue(get_user() in self.other.list_shared_by_others())
1730

    
1731
    def test_list_my_shared(self):
1732
        my_shared_containers = self.client.list_containers(shared=True)
1733
        self.assertTrue('c1' in my_shared_containers)
1734
        self.assertTrue('c2' not in my_shared_containers)
1735

    
1736
        my_shared_objects = self.client.list_objects('c1', shared=True)
1737
        self.assertTrue('o1' in my_shared_objects)
1738
        self.assertTrue('o2' not in my_shared_objects)
1739

    
1740
class List(BaseTestCase):
1741
    def setUp(self):
1742
        BaseTestCase.setUp(self)
1743
        for i in range(1, 5):
1744
            c = 'c%s' % i
1745
            self.client.create_container(c)
1746
            for j in range(1, 3):
1747
                o = 'o%s' % j
1748
                self.upload_random_data(c, o)
1749
            if i < 3:
1750
                self.client.share_object(c, 'o1', ['papagian'], read=True)
1751
            if i%2 != 0:
1752
                self.client.publish_object(c, 'o2')
1753
    
1754
    def test_shared_public(self):
1755
        self.assertEqual(self.client.list_containers(shared=True),
1756
                         ['c1', 'c2'])
1757
        self.assertEqual(self.client.list_containers(public=True), ['c1', 'c3'])
1758
        self.assertEqual(self.client.list_containers(shared=True, public=True), ['c1', 'c2', 'c3'])
1759
        
1760
        self.assertEqual(self.client.list_objects('c1', shared=True), ['o1'])
1761
        self.assertEqual(self.client.list_objects('c1', public=True), ['o2'])
1762
        self.assertEqual(self.client.list_objects('c1', shared=True, public=True), ['o1', 'o2'])
1763
        
1764
        self.assertEqual(self.client.list_objects('c2', shared=True), ['o1'])
1765
        self.assertEqual(self.client.list_objects('c2', public=True), '')
1766
        self.assertEqual(self.client.list_objects('c2', shared=True, public=True), ['o1'])
1767
        
1768
        self.assertEqual(self.client.list_objects('c3', shared=True), '')
1769
        self.assertEqual(self.client.list_objects('c3', public=True), ['o2'])
1770
        self.assertEqual(self.client.list_objects('c3', shared=True, public=True), ['o2'])
1771
        
1772
        self.assertEqual(self.client.list_objects('c4', shared=True), '')
1773
        self.assertEqual(self.client.list_objects('c4', public=True), '')
1774
        self.assertEqual(self.client.list_objects('c4', shared=True, public=True), '')
1775

    
1776
class TestGreek(BaseTestCase):
1777
    def test_create_container(self):
1778
        self.client.create_container('φάκελος')
1779
        self.assert_container_exists('φάκελος')
1780

    
1781
        self.assertTrue('φάκελος' in self.client.list_containers())
1782

    
1783
    def test_create_object(self):
1784
        self.client.create_container('φάκελος')
1785
        self.upload_random_data('φάκελος', 'αντικείμενο')
1786

    
1787
        self.assert_object_exists('φάκελος', 'αντικείμενο')
1788
        self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1789

    
1790
    def test_copy_object(self):
1791
        src_container = 'φάκελος'
1792
        src_object = 'αντικείμενο'
1793
        dest_container = 'αντίγραφα'
1794
        dest_object = 'ασφαλές-αντίγραφο'
1795

    
1796
        self.client.create_container(src_container)
1797
        self.upload_random_data(src_container, src_object)
1798

    
1799
        self.client.create_container(dest_container)
1800
        self.client.copy_object(src_container, src_object, dest_container,
1801
                                dest_object)
1802

    
1803
        self.assert_object_exists(src_container, src_object)
1804
        self.assert_object_exists(dest_container, dest_object)
1805
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
1806

    
1807
    def test_move_object(self):
1808
        src_container = 'φάκελος'
1809
        src_object = 'αντικείμενο'
1810
        dest_container = 'αντίγραφα'
1811
        dest_object = 'ασφαλές-αντίγραφο'
1812

    
1813
        self.client.create_container(src_container)
1814
        self.upload_random_data(src_container, src_object)
1815

    
1816
        self.client.create_container(dest_container)
1817
        self.client.move_object(src_container, src_object, dest_container,
1818
                                dest_object)
1819

    
1820
        self.assert_object_not_exists(src_container, src_object)
1821
        self.assert_object_exists(dest_container, dest_object)
1822
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
1823

    
1824
    def test_delete_object(self):
1825
        self.client.create_container('φάκελος')
1826
        self.upload_random_data('φάκελος', 'αντικείμενο')
1827
        self.assert_object_exists('φάκελος', 'αντικείμενο')
1828

    
1829
        self.client.delete_object('φάκελος', 'αντικείμενο')
1830
        self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1831
        self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1832

    
1833
    def test_delete_container(self):
1834
        self.client.create_container('φάκελος')
1835
        self.assert_container_exists('φάκελος')
1836

    
1837
        self.client.delete_container('φάκελος')
1838
        self.assert_container_not_exists('φάκελος')
1839
        self.assertTrue('φάκελος' not in self.client.list_containers())
1840

    
1841
    def test_account_meta(self):
1842
        meta = {'ποιότητα':'ΑΑΑ'}
1843
        self.client.update_account_metadata(**meta)
1844
        meta = self.client.retrieve_account_metadata(restricted=True)
1845
        self.assertTrue('ποιότητα' in meta.keys())
1846
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1847

    
1848
    def test_container_meta(self):
1849
        meta = {'ποιότητα':'ΑΑΑ'}
1850
        self.client.create_container('φάκελος', meta=meta)
1851

    
1852
        meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1853
        self.assertTrue('ποιότητα' in meta.keys())
1854
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1855

    
1856
    def test_object_meta(self):
1857
        self.client.create_container('φάκελος')
1858
        meta = {'ποιότητα':'ΑΑΑ'}
1859
        self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1860

    
1861
        meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1862
                                                    restricted=True)
1863
        self.assertTrue('ποιότητα' in meta.keys())
1864
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1865

    
1866
    def test_list_meta_filtering(self):
1867
        self.client.create_container('φάκελος')
1868
        meta = {'ποιότητα':'ΑΑΑ'}
1869
        self.upload_random_data('φάκελος', 'ο1', **meta)
1870
        self.upload_random_data('φάκελος', 'ο2')
1871
        self.upload_random_data('φάκελος', 'ο3')
1872

    
1873
        meta = {'ποσότητα':'μεγάλη'}
1874
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1875
        objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1876
        self.assertEquals(objects, ['ο1', 'ο2'])
1877

    
1878
        objects = self.client.list_objects('φάκελος', meta='!ποιότητα')
1879
        self.assertEquals(objects, ['ο2', 'ο3'])
1880

    
1881
        objects = self.client.list_objects('φάκελος', meta='!ποιότητα, !ποσότητα')
1882
        self.assertEquals(objects, ['ο3'])
1883

    
1884
        meta = {'ποιότητα':'ΑΒ'}
1885
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1886
        objects = self.client.list_objects('φάκελος', meta='ποιότητα=ΑΑΑ')
1887
        self.assertEquals(objects, ['ο1'])
1888
        objects = self.client.list_objects('φάκελος', meta='ποιότητα!=ΑΑΑ')
1889
        self.assertEquals(objects, ['ο2'])
1890

    
1891
        meta = {'έτος':'2011'}
1892
        self.client.update_object_metadata('φάκελος', 'ο3', **meta)
1893
        meta = {'έτος':'2012'}
1894
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1895
        objects = self.client.list_objects('φάκελος', meta='έτος<2012')
1896
        self.assertEquals(objects, ['ο3'])
1897
        objects = self.client.list_objects('φάκελος', meta='έτος<=2012')
1898
        self.assertEquals(objects, ['ο2', 'ο3'])
1899
        objects = self.client.list_objects('φάκελος', meta='έτος<2012,έτος!=2011')
1900
        self.assertEquals(objects, '')
1901

    
1902
    def test_groups(self):
1903
        #create a group
1904
        groups = {'σεφς':'chazapis,διογένης'}
1905
        self.client.set_account_groups(**groups)
1906
        groups.update(self.initial_groups)
1907
        self.assertEqual(groups['σεφς'],
1908
                         self.client.retrieve_account_groups()['σεφς'])
1909

    
1910
        #check read access
1911
        self.client.create_container('φάκελος')
1912
        o = self.upload_random_data('φάκελος', 'ο1')
1913
        self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
1914
        chef = Pithos_Client(get_url(),
1915
                            '0009',
1916
                            'διογένης')
1917
        self.assert_not_raises_fault(403, chef.retrieve_object_metadata,
1918
                                     'φάκελος', 'ο1', account=get_user())
1919

    
1920
        #check write access
1921
        self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1922
        new_data = get_random_data()
1923
        self.assert_not_raises_fault(403, chef.update_object,
1924
                                     'φάκελος', 'ο1', StringIO(new_data),
1925
                                     account=get_user())
1926

    
1927
        server_data = self.client.retrieve_object('φάκελος', 'ο1')
1928
        self.assertEqual(server_data[:len(o['data'])], o['data'])
1929
        self.assertEqual(server_data[len(o['data']):], new_data)
1930

    
1931
    def test_manifestation(self):
1932
        self.client.create_container('κουβάς')
1933
        prefix = 'μέρη/'
1934
        data = ''
1935
        for i in range(5):
1936
            part = '%s%d' %(prefix, i)
1937
            o = self.upload_random_data('κουβάς', part)
1938
            data += o['data']
1939

    
1940
        self.client.create_container('φάκελος')
1941
        manifest = '%s/%s' %('κουβάς', prefix)
1942
        self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
1943

    
1944
        self.assert_object_exists('φάκελος', 'άπαντα')
1945
        self.assertEqual(data, self.client.retrieve_object('φάκελος',
1946
                                                           'άπαντα'))
1947

    
1948
        #wrong manifestation
1949
        self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
1950
        self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
1951

    
1952
    def test_update_from_another_object(self):
1953
        self.client.create_container('κουβάς')
1954
        src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
1955
        initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
1956
        source_object = '/%s/%s' % ('κουβάς', 'πηγή')
1957
        self.client.update_from_other_source('κουβάς', 'νέο', source_object)
1958

    
1959
        self.assertEqual(
1960
            self.client.retrieve_object('κουβάς', 'νέο'),
1961
            '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
1962

    
1963
class TestPermissions(BaseTestCase):
1964
    def setUp(self):
1965
        BaseTestCase.setUp(self)
1966

    
1967
        #create a group
1968
        self.authorized = ['chazapis', 'verigak', 'gtsouk']
1969
        groups = {'pithosdev':','.join(self.authorized)}
1970
        self.client.set_account_groups(**groups)
1971

    
1972
        self.container = 'c'
1973
        self.object = 'o'
1974
        self.client.create_container(self.container)
1975
        self.upload_random_data(self.container, self.object)
1976
        self.upload_random_data(self.container, self.object+'/')
1977
        self.upload_random_data(self.container, self.object+'/a')
1978
        self.upload_random_data(self.container, self.object+'a')
1979
        self.upload_random_data(self.container, self.object+'a/')
1980
        self.dir_content_types = ('application/directory', 'application/folder')
1981

    
1982
    def assert_read(self, authorized=[], any=False, depth=0):
1983
        for token, account in OTHER_ACCOUNTS.items():
1984
            cl = Pithos_Client(get_url(), token, account)
1985
            if account in authorized or any:
1986
                self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1987
                                             self.container, self.object,
1988
                                             account=get_user())
1989
            else:
1990
                self.assert_raises_fault(403, cl.retrieve_object_metadata,
1991
                                         self.container, self.object,
1992
                                         account=get_user())
1993

    
1994
        #check inheritance
1995
        meta = self.client.retrieve_object_metadata(self.container, self.object)
1996
        type = meta['content-type']
1997
        derivatives = self.client.list_objects(self.container, prefix=self.object)
1998
        #exclude the self.object
1999
        del derivatives[derivatives.index(self.object)]
2000
        for o in derivatives:
2001
            for token, account in OTHER_ACCOUNTS.items():
2002
                cl = Pithos_Client(get_url(), token, account)
2003
                prefix = self.object if self.object.endswith('/') else self.object+'/'
2004
                if (account in authorized or any) and \
2005
                (type in self.dir_content_types) and \
2006
                o.startswith(prefix):
2007
                    self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
2008
                                             self.container, o, account=get_user())
2009
                else:
2010
                    self.assert_raises_fault(403, cl.retrieve_object_metadata,
2011
                                         self.container, o, account=get_user())
2012

    
2013
    def assert_write(self, authorized=[], any=False):
2014
        o_data = self.client.retrieve_object(self.container, self.object)
2015
        for token, account in OTHER_ACCOUNTS.items():
2016
            cl = Pithos_Client(get_url(), token, account)
2017
            new_data = get_random_data()
2018
            if account in authorized or any:
2019
                # test write access
2020
                self.assert_not_raises_fault(403, cl.update_object,
2021
                                             self.container, self.object, StringIO(new_data),
2022
                                             account=get_user())
2023
                try:
2024
                    # test read access
2025
                    server_data = cl.retrieve_object(self.container, self.object, account=get_user())
2026
                    self.assertEqual(o_data, server_data[:len(o_data)])
2027
                    self.assertEqual(new_data, server_data[len(o_data):])
2028
                    o_data = server_data
2029
                except Fault, f:
2030
                    self.failIf(f.status == 403)
2031
            else:
2032
                self.assert_raises_fault(403, cl.update_object,
2033
                                             self.container, self.object, StringIO(new_data),
2034
                                             account=get_user())
2035
        #check inheritance
2036
        meta = self.client.retrieve_object_metadata(self.container, self.object)
2037
        type = meta['content-type']
2038
        derivatives = self.client.list_objects(self.container, prefix=self.object)
2039
        #exclude the object
2040
        del derivatives[derivatives.index(self.object)]
2041
        for o in derivatives:
2042
            for token, account in OTHER_ACCOUNTS.items():
2043
                prefix = self.object if self.object.endswith('/') else self.object+'/'
2044
                cl = Pithos_Client(get_url(), token, account)
2045
                new_data = get_random_data()
2046
                if (account in authorized or any) and \
2047
                (type in self.dir_content_types) and \
2048
                o.startswith(prefix):
2049
                    # test write access
2050
                    self.assert_not_raises_fault(403, cl.update_object,
2051
                                                 self.container, o,
2052
                                                 StringIO(new_data),
2053
                                                 account=get_user())
2054
                    try:
2055
                        server_data = cl.retrieve_object(self.container, o, account=get_user())
2056
                        self.assertEqual(new_data, server_data[-len(new_data):])
2057
                    except Fault, f:
2058
                        self.failIf(f.status == 403)
2059
                else:
2060
                    self.assert_raises_fault(403, cl.update_object,
2061
                                                 self.container, o,
2062
                                                 StringIO(new_data),
2063
                                                 account=get_user())
2064

    
2065
    def test_group_read(self):
2066
        self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()])
2067
        self.assert_read(authorized=self.authorized)
2068

    
2069
    def test_read_many(self):
2070
        self.client.share_object(self.container, self.object, self.authorized)
2071
        self.assert_read(authorized=self.authorized)
2072

    
2073
    def test_read_by_everyone(self):
2074
        self.client.share_object(self.container, self.object, ['*'])
2075
        self.assert_read(any=True)
2076

    
2077
    def test_read_directory(self):
2078
        for type in self.dir_content_types:
2079
            #change content type
2080
            self.client.move_object(self.container, self.object, self.container, self.object, content_type=type)
2081
            self.client.share_object(self.container, self.object, ['*'])
2082
            self.assert_read(any=True)
2083
            self.client.share_object(self.container, self.object, self.authorized)
2084
            self.assert_read(authorized=self.authorized)
2085
            self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()])
2086
            self.assert_read(authorized=self.authorized)
2087

    
2088
    def test_group_write(self):
2089
        self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()], read=False)
2090
        self.assert_write(authorized=self.authorized)
2091

    
2092
    def test_write_many(self):
2093
        self.client.share_object(self.container, self.object, self.authorized, read=False)
2094
        self.assert_write(authorized=self.authorized)
2095

    
2096
    def test_write_by_everyone(self):
2097
        self.client.share_object(self.container, self.object, ['*'], read=False)
2098
        self.assert_write(any=True)
2099

    
2100
    def test_write_directory(self):
2101
        dir_content_types = ('application/directory', 'application/foler')
2102
        for type in dir_content_types:
2103
            #change content type
2104
            self.client.move_object(self.container, self.object, self.container, self.object, content_type='application/folder')
2105
            self.client.share_object(self.container, self.object, ['*'], read=False)
2106
            self.assert_write(any=True)
2107
            self.client.share_object(self.container, self.object, self.authorized, read=False)
2108
            self.assert_write(authorized=self.authorized)
2109
            self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()], read=False)
2110
            self.assert_write(authorized=self.authorized)
2111

    
2112
    def test_shared_listing(self):
2113
        self.client.share_object(self.container, self.object, self.authorized)
2114

    
2115
        my_shared_containers = self.client.list_containers(shared=True)
2116
        self.assertEqual(['c'], my_shared_containers)
2117
        my_shared_objects = self.client.list_objects('c', shared=True)
2118
        self.assertEqual(['o'], my_shared_objects)
2119

    
2120
        dir_content_types = ('application/directory', 'application/foler')
2121
        for type in dir_content_types:
2122
            #change content type
2123
            self.client.move_object(self.container, self.object, self.container, self.object, content_type='application/folder')
2124
            my_shared_objects = self.client.list_objects('c', shared=True)
2125
            self.assertEqual(['o', 'o/', 'o/a'], my_shared_objects)
2126

    
2127
        for token, account in OTHER_ACCOUNTS.items():
2128
            if account in self.authorized:
2129
                self.other = Pithos_Client(get_url(), token, account)
2130
                self.assertTrue(get_user() in self.other.list_shared_by_others())
2131

    
2132
class TestPublish(BaseTestCase):
2133
    def test_publish(self):
2134
        self.client.create_container('c')
2135
        o_data = self.upload_random_data('c', 'o')['data']
2136
        self.client.publish_object('c', 'o')
2137
        meta = self.client.retrieve_object_metadata('c', 'o')
2138
        self.assertTrue('x-object-public' in meta)
2139
        url = meta['x-object-public']
2140

    
2141
        p = urlparse(get_url())
2142
        if p.scheme == 'http':
2143
            conn = HTTPConnection(p.netloc)
2144
        elif p.scheme == 'https':
2145
            conn = HTTPSConnection(p.netloc)
2146
        else:
2147
            raise Exception('Unknown URL scheme')
2148

    
2149
        conn.request('GET', url)
2150
        resp = conn.getresponse()
2151
        length = resp.getheader('content-length', None)
2152
        data = resp.read(length)
2153
        self.assertEqual(o_data, data)
2154

    
2155
class TestPolicies(BaseTestCase):
2156
    def test_none_versioning(self):
2157
        self.client.create_container('c', policies={'versioning':'none'})
2158
        o = self.upload_random_data('c', 'o')
2159
        meta = self.client.retrieve_object_metadata('c', 'o')
2160
        v = meta['x-object-version']
2161
        more_data = get_random_data()
2162
        self.client.update_object('c', 'o', StringIO(more_data))
2163
        vlist = self.client.retrieve_object_versionlist('c', 'o')
2164
        self.assert_raises_fault(404, self.client.retrieve_object_version,
2165
                                 'c', 'o', v)
2166
        data = self.client.retrieve_object('c', 'o')
2167
        end = len(o['data'])
2168
        self.assertEqual(data[:end], o['data'])
2169
        self.assertEqual(data[end:], more_data)
2170

    
2171
    def test_quota(self):
2172
        self.client.create_container('c', policies={'quota':'1'})
2173
        meta = self.client.retrieve_container_metadata('c')
2174
        self.assertEqual(meta['x-container-policy-quota'], '1')
2175
        self.assert_raises_fault(413, self.upload_random_data, 'c', 'o',
2176
                                 length=1024*1024+1)
2177

    
2178
    def test_quota_none(self):
2179
        self.client.create_container('c', policies={'quota':'0'})
2180
        meta = self.client.retrieve_container_metadata('c')
2181
        self.assertEqual(meta['x-container-policy-quota'], '0')
2182
        self.assert_not_raises_fault(413, self.upload_random_data, 'c', 'o',
2183
                                 length=1024*1024+1)
2184

    
2185
class AssertUUidInvariant(object):
2186
    def __init__(self, callable, *args, **kwargs):
2187
        self.callable = callable
2188
        self.args = args
2189
        self.kwargs = kwargs
2190

    
2191
    def __enter__(self):
2192
        self.map = self.callable(*self.args, **self.kwargs)
2193
        assert('x-object-uuid' in self.map)
2194
        self.uuid = self.map['x-object-uuid']
2195
        return self.map
2196

    
2197
    def __exit__(self, type, value, tb):
2198
        map = self.callable(*self.args, **self.kwargs)
2199
        assert('x-object-uuid' in self.map)
2200
        uuid = map['x-object-uuid']
2201
        assert(uuid == self.uuid)
2202

    
2203
class AssertMappingInvariant(object):
2204
    def __init__(self, callable, *args, **kwargs):
2205
        self.callable = callable
2206
        self.args = args
2207
        self.kwargs = kwargs
2208

    
2209
    def __enter__(self):
2210
        self.map = self.callable(*self.args, **self.kwargs)
2211
        return self.map
2212

    
2213
    def __exit__(self, type, value, tb):
2214
        map = self.callable(*self.args, **self.kwargs)
2215
        for k, v in self.map.items():
2216
            if is_date(v):
2217
                continue
2218
            #print '#', k, v, map[k]
2219
            assert(k in map)
2220
            assert v == map[k]
2221

    
2222
class AssertContentInvariant(object):
2223
    def __init__(self, callable, *args, **kwargs):
2224
        self.callable = callable
2225
        self.args = args
2226
        self.kwargs = kwargs
2227

    
2228
    def __enter__(self):
2229
        self.content = self.callable(*self.args, **self.kwargs)[2]
2230
        return self.content
2231

    
2232
    def __exit__(self, type, value, tb):
2233
        content = self.callable(*self.args, **self.kwargs)[2]
2234
        assert self.content == content
2235

    
2236
def get_content_splitted(response):
2237
    if response:
2238
        return response.content.split('\n')
2239

    
2240
def compute_md5_hash(data):
2241
    md5 = hashlib.md5()
2242
    offset = 0
2243
    md5.update(data)
2244
    return md5.hexdigest().lower()
2245

    
2246
def compute_block_hash(data, algorithm):
2247
    h = hashlib.new(algorithm)
2248
    h.update(data.rstrip('\x00'))
2249
    return h.hexdigest()
2250

    
2251
def get_random_data(length=500):
2252
    char_set = string.ascii_uppercase + string.digits
2253
    return ''.join(random.choice(char_set) for x in xrange(length))
2254

    
2255
def is_date(date):
2256
    MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2257
    __D = r'(?P<day>\d{2})'
2258
    __D2 = r'(?P<day>[ \d]\d)'
2259
    __M = r'(?P<mon>\w{3})'
2260
    __Y = r'(?P<year>\d{4})'
2261
    __Y2 = r'(?P<year>\d{2})'
2262
    __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2263
    RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2264
    RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2265
    ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2266
    for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2267
        m = regex.match(date)
2268
        if m is not None:
2269
            return True
2270
    return False
2271

    
2272
o_names = ['kate.jpg',
2273
           'kate_beckinsale.jpg',
2274
           'How To Win Friends And Influence People.pdf',
2275
           'moms_birthday.jpg',
2276
           'poodle_strut.mov',
2277
           'Disturbed - Down With The Sickness.mp3',
2278
           'army_of_darkness.avi',
2279
           'the_mad.avi',
2280
           'photos/animals/dogs/poodle.jpg',
2281
           'photos/animals/dogs/terrier.jpg',
2282
           'photos/animals/cats/persian.jpg',
2283
           'photos/animals/cats/siamese.jpg',
2284
           'photos/plants/fern.jpg',
2285
           'photos/plants/rose.jpg',
2286
           'photos/me.jpg']
2287

    
2288

    
2289
def main():
2290
    if get_user() == 'test':
2291
        unittest.main(module='pithos.tools.test')
2292
    else:
2293
        print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()
2294

    
2295

    
2296
if __name__ == "__main__":
2297
    main()
2298