Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-app / pithos / api / test / objects.py @ 3a19e99b

History | View | Annotate | Download (45.7 kB)

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

    
4
# Copyright 2011-2013 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 collections import defaultdict
38
from urllib import quote
39
import time as _time
40

    
41
from pithos.api.test import (PithosAPITest, pithos_settings,
42
                             AssertMappingInvariant, AssertUUidInvariant,
43
                             DATE_FORMATS)
44
from pithos.api.test.util import compute_md5_hash, strnextling, get_random_data
45
from pithos.api.test.util.hashmap import merkle
46

    
47
from synnefo.lib import join_urls
48

    
49
import django.utils.simplejson as json
50

    
51
import random
52
import re
53
import datetime
54

    
55

    
56
class ObjectGet(PithosAPITest):
57
    def setUp(self):
58
        PithosAPITest.setUp(self)
59
        self.containers = ['c1', 'c2']
60

    
61
        # create some containers
62
        for c in self.containers:
63
            self.create_container(c)
64

    
65
        # upload files
66
        self.objects = defaultdict(list)
67
        self.objects['c1'].append(self.upload_object('c1')[0])
68

    
69
    def test_versions(self):
70
        c = 'c1'
71
        o = self.objects[c][0]
72
        url = join_urls(self.pithos_path, self.user, c, o)
73

    
74
        meta = {'HTTP_X_OBJECT_META_QUALITY': 'AAA'}
75
        r = self.post(url, content_type='', **meta)
76
        self.assertEqual(r.status_code, 202)
77

    
78
        url = join_urls(self.pithos_path, self.user, c, o)
79
        r = self.get('%s?version=list&format=json' % url)
80
        self.assertEqual(r.status_code, 200)
81
        l1 = json.loads(r.content)['versions']
82
        self.assertEqual(len(l1), 2)
83

    
84
        # update meta
85
        meta = {'HTTP_X_OBJECT_META_QUALITY': 'AB',
86
                'HTTP_X_OBJECT_META_STOCK': 'True'}
87
        r = self.post(url, content_type='', **meta)
88
        self.assertEqual(r.status_code, 202)
89

    
90
        # assert a newly created version has been created
91
        r = self.get('%s?version=list&format=json' % url)
92
        self.assertEqual(r.status_code, 200)
93
        l2 = json.loads(r.content)['versions']
94
        self.assertEqual(len(l2), len(l1) + 1)
95
        self.assertEqual(l2[:-1], l1)
96

    
97
        vserial, _ = l2[-2]
98
        self.assertEqual(self.get_object_meta(c, o, version=vserial),
99
                         {'X-Object-Meta-Quality': 'AAA'})
100

    
101
        # update data
102
        self.append_object_data(c, o)
103

    
104
        # assert a newly created version has been created
105
        r = self.get('%s?version=list&format=json' % url)
106
        self.assertEqual(r.status_code, 200)
107
        l3 = json.loads(r.content)['versions']
108
        self.assertEqual(len(l3), len(l2) + 1)
109
        self.assertEqual(l3[:-1], l2)
110

    
111
    def test_objects_with_trailing_spaces(self):
112
        # create object
113
        oname = self.upload_object('c1')[0]
114
        url = join_urls(self.pithos_path, self.user, 'c1', oname)
115

    
116
        r = self.get(quote('%s ' % url))
117
        self.assertEqual(r.status_code, 404)
118

    
119
        # delete object
120
        self.delete(url)
121

    
122
        r = self.get(url)
123
        self.assertEqual(r.status_code, 404)
124

    
125
        # upload object with trailing space
126
        oname = self.upload_object('c1', quote('%s ' % get_random_data(8)))[0]
127

    
128
        url = join_urls(self.pithos_path, self.user, 'c1', oname)
129
        r = self.get(url)
130
        self.assertEqual(r.status_code, 200)
131

    
132
        url = join_urls(self.pithos_path, self.user, 'c1', oname[:-1])
133
        r = self.get(url)
134
        self.assertEqual(r.status_code, 404)
135

    
136
    def test_get_partial(self):
137
        cname = self.containers[0]
138
        oname, odata = self.upload_object(cname, length=512)[:-1]
139
        url = join_urls(self.pithos_path, self.user, cname, oname)
140
        r = self.get(url, HTTP_RANGE='bytes=0-499')
141
        self.assertEqual(r.status_code, 206)
142
        data = r.content
143
        self.assertEqual(data, odata[:500])
144
        self.assertTrue('Content-Range' in r)
145
        self.assertEqual(r['Content-Range'], 'bytes 0-499/%s' % len(odata))
146
        self.assertTrue('Content-Type' in r)
147
        self.assertTrue(r['Content-Type'], 'application/octet-stream')
148

    
149
    def test_get_final_500(self):
150
        cname = self.containers[0]
151
        oname, odata = self.upload_object(cname, length=512)[:-1]
152
        size = len(odata)
153
        url = join_urls(self.pithos_path, self.user, cname, oname)
154
        r = self.get(url, HTTP_RANGE='bytes=-500')
155
        self.assertEqual(r.status_code, 206)
156
        self.assertEqual(r.content, odata[-500:])
157
        self.assertTrue('Content-Range' in r)
158
        self.assertEqual(r['Content-Range'],
159
                         'bytes %s-%s/%s' % (size - 500, size - 1, size))
160
        self.assertTrue('Content-Type' in r)
161
        self.assertTrue(r['Content-Type'], 'application/octet-stream')
162

    
163
    def test_get_rest(self):
164
        cname = self.containers[0]
165
        oname, odata = self.upload_object(cname, length=512)[:-1]
166
        size = len(odata)
167
        url = join_urls(self.pithos_path, self.user, cname, oname)
168
        offset = len(odata) - random.randint(1, 512)
169
        r = self.get(url, HTTP_RANGE='bytes=%s-' % offset)
170
        self.assertEqual(r.status_code, 206)
171
        self.assertEqual(r.content, odata[offset:])
172
        self.assertTrue('Content-Range' in r)
173
        self.assertEqual(r['Content-Range'],
174
                         'bytes %s-%s/%s' % (offset, size - 1, size))
175
        self.assertTrue('Content-Type' in r)
176
        self.assertTrue(r['Content-Type'], 'application/octet-stream')
177

    
178
    def test_get_range_not_satisfiable(self):
179
        cname = self.containers[0]
180
        oname, odata = self.upload_object(cname, length=512)[:-1]
181
        url = join_urls(self.pithos_path, self.user, cname, oname)
182

    
183
        # TODO
184
        #r = self.get(url, HTTP_RANGE='bytes=50-10')
185
        #self.assertEqual(r.status_code, 416)
186

    
187
        offset = len(odata) + 1
188
        r = self.get(url, HTTP_RANGE='bytes=0-%s' % offset)
189
        self.assertEqual(r.status_code, 416)
190

    
191
    def test_multiple_range(self):
192
        cname = self.containers[0]
193
        oname, odata = self.upload_object(cname, length=1024)[:-1]
194
        url = join_urls(self.pithos_path, self.user, cname, oname)
195

    
196
        l = ['0-499', '-500', '1000-']
197
        ranges = 'bytes=%s' % ','.join(l)
198
        r = self.get(url, HTTP_RANGE=ranges)
199
        self.assertEqual(r.status_code, 206)
200
        self.assertTrue('content-type' in r)
201
        p = re.compile(
202
            'multipart/byteranges; boundary=(?P<boundary>[0-9a-f]{32}\Z)',
203
            re.I)
204
        m = p.match(r['content-type'])
205
        if m is None:
206
            self.fail('Invalid multiple range content type')
207
        boundary = m.groupdict()['boundary']
208
        cparts = r.content.split('--%s' % boundary)[1:-1]
209

    
210
        # assert content parts length
211
        self.assertEqual(len(cparts), len(l))
212

    
213
        # for each content part assert headers
214
        i = 0
215
        for cpart in cparts:
216
            content = cpart.split('\r\n')
217
            headers = content[1:3]
218
            content_range = headers[0].split(': ')
219
            self.assertEqual(content_range[0], 'Content-Range')
220

    
221
            r = l[i].split('-')
222
            if not r[0] and not r[1]:
223
                pass
224
            elif not r[0]:
225
                start = len(odata) - int(r[1])
226
                end = len(odata)
227
            elif not r[1]:
228
                start = int(r[0])
229
                end = len(odata)
230
            else:
231
                start = int(r[0])
232
                end = int(r[1]) + 1
233
            fdata = odata[start:end]
234
            sdata = '\r\n'.join(content[4:-1])
235
            self.assertEqual(len(fdata), len(sdata))
236
            self.assertEquals(fdata, sdata)
237
            i += 1
238

    
239
    def test_multiple_range_not_satisfiable(self):
240
        # perform get with multiple range
241
        cname = self.containers[0]
242
        oname, odata = self.upload_object(cname, length=1024)[:-1]
243
        out_of_range = len(odata) + 1
244
        l = ['0-499', '-500', '%d-' % out_of_range]
245
        ranges = 'bytes=%s' % ','.join(l)
246
        url = join_urls(self.pithos_path, self.user, cname, oname)
247
        r = self.get(url, HTTP_RANGE=ranges)
248
        self.assertEqual(r.status_code, 416)
249

    
250
    def test_get_with_if_match_with_md5(self):
251
        cname = self.containers[0]
252
        oname, odata = self.upload_object(cname, length=1024)[:-1]
253

    
254
        # perform get with If-Match
255
        url = join_urls(self.pithos_path, self.user, cname, oname)
256

    
257
        if pithos_settings.UPDATE_MD5:
258
            etag = compute_md5_hash(odata)
259
        else:
260
            etag = merkle(odata)
261

    
262
        r = self.get(url, HTTP_IF_MATCH=etag)
263

    
264
        # assert get success
265
        self.assertEqual(r.status_code, 200)
266

    
267
        # assert response content
268
        self.assertEqual(r.content, odata)
269

    
270
    def test_get_with_if_match_star_with_md5(self):
271
        cname = self.containers[0]
272
        oname, odata = self.upload_object(cname, length=1024)[:-1]
273

    
274
        # perform get with If-Match *
275
        url = join_urls(self.pithos_path, self.user, cname, oname)
276
        r = self.get(url, HTTP_IF_MATCH='*')
277

    
278
        # assert get success
279
        self.assertEqual(r.status_code, 200)
280

    
281
        # assert response content
282
        self.assertEqual(r.content, odata)
283

    
284
    def test_get_with_multiple_if_match_without_md5(self):
285
        cname = self.containers[0]
286
        oname, odata = self.upload_object(cname, length=1024)[:-1]
287

    
288
        # perform get with If-Match
289
        url = join_urls(self.pithos_path, self.user, cname, oname)
290

    
291
        if pithos_settings.UPDATE_MD5:
292
            etag = compute_md5_hash(odata)
293
        else:
294
            etag = merkle(odata)
295

    
296
        r = self.get(url, HTTP_IF_MATCH=','.join([etag,
297
                                                  get_random_data(8)]))
298

    
299
        # assert get success
300
        self.assertEqual(r.status_code, 200)
301

    
302
        # assert response content
303
        self.assertEqual(r.content, odata)
304

    
305
    def test_if_match_precondition_failed(self):
306
        cname = self.containers[0]
307
        oname, odata = self.upload_object(cname, length=1024)[:-1]
308

    
309
        # perform get with If-Match
310
        url = join_urls(self.pithos_path, self.user, cname, oname)
311
        r = self.get(url, HTTP_IF_MATCH=get_random_data(8))
312
        self.assertEqual(r.status_code, 412)
313

    
314
    def test_if_none_match_without_md5(self):
315
        # upload object
316
        cname = self.containers[0]
317
        oname, odata = self.upload_object(cname, length=1024)[:-1]
318

    
319
        if pithos_settings.UPDATE_MD5:
320
            etag = compute_md5_hash(odata)
321
        else:
322
            etag = merkle(odata)
323

    
324
        # perform get with If-None-Match
325
        url = join_urls(self.pithos_path, self.user, cname, oname)
326
        r = self.get(url, HTTP_IF_NONE_MATCH=etag)
327

    
328
        # assert precondition_failed
329
        self.assertEqual(r.status_code, 304)
330

    
331
        # update object data
332
        r = self.append_object_data(cname, oname)[-1]
333
        self.assertTrue(etag != r['ETag'])
334

    
335
        # perform get with If-None-Match
336
        url = join_urls(self.pithos_path, self.user, cname, oname)
337
        r = self.get(url, HTTP_IF_NONE_MATCH=etag)
338

    
339
        # assert get success
340
        self.assertEqual(r.status_code, 200)
341

    
342
    def test_if_none_match_star_without_md5(self):
343
        # upload object
344
        cname = self.containers[0]
345
        oname, odata = self.upload_object(cname, length=1024)[:-1]
346

    
347
        # perform get with If-None-Match with star
348
        url = join_urls(self.pithos_path, self.user, cname, oname)
349
        r = self.get(url, HTTP_IF_NONE_MATCH='*')
350

    
351
        self.assertEqual(r.status_code, 304)
352

    
353
    def test_if_modified_since(self):
354
        # upload object
355
        cname = self.containers[0]
356
        oname, odata = self.upload_object(cname, length=1024)[:-1]
357
        object_info = self.get_object_info(cname, oname)
358
        last_modified = object_info['Last-Modified']
359
        t1 = datetime.datetime.strptime(last_modified, DATE_FORMATS[-1])
360
        t1_formats = map(t1.strftime, DATE_FORMATS)
361

    
362
        # Check not modified since
363
        url = join_urls(self.pithos_path, self.user, cname, oname)
364
        for t in t1_formats:
365
            r = self.get(url, HTTP_IF_MODIFIED_SINCE=t)
366
            self.assertEqual(r.status_code, 304)
367

    
368
        _time.sleep(1)
369

    
370
        # update object data
371
        appended_data = self.append_object_data(cname, oname)[1]
372

    
373
        # Check modified since
374
        url = join_urls(self.pithos_path, self.user, cname, oname)
375
        for t in t1_formats:
376
            r = self.get(url, HTTP_IF_MODIFIED_SINCE=t)
377
            self.assertEqual(r.status_code, 200)
378
            self.assertEqual(r.content, odata + appended_data)
379

    
380
    def test_if_modified_since_invalid_date(self):
381
        cname = self.containers[0]
382
        oname, odata = self.upload_object(cname, length=1024)[:-1]
383
        url = join_urls(self.pithos_path, self.user, cname, oname)
384
        r = self.get(url, HTTP_IF_MODIFIED_SINCE='Monday')
385
        self.assertEqual(r.status_code, 200)
386
        self.assertEqual(r.content, odata)
387

    
388
    def test_if_not_modified_since(self):
389
        cname = self.containers[0]
390
        oname, odata = self.upload_object(cname, length=1024)[:-1]
391
        url = join_urls(self.pithos_path, self.user, cname, oname)
392
        object_info = self.get_object_info(cname, oname)
393
        last_modified = object_info['Last-Modified']
394
        t = datetime.datetime.strptime(last_modified, DATE_FORMATS[-1])
395

    
396
        # Check unmodified
397
        t1 = t + datetime.timedelta(seconds=1)
398
        t1_formats = map(t1.strftime, DATE_FORMATS)
399
        for t in t1_formats:
400
            r = self.get(url, HTTP_IF_UNMODIFIED_SINCE=t)
401
            self.assertEqual(r.status_code, 200)
402
            self.assertEqual(odata, odata)
403

    
404
        # modify object
405
        _time.sleep(2)
406
        self.append_object_data(cname, oname)
407

    
408
        object_info = self.get_object_info(cname, oname)
409
        last_modified = object_info['Last-Modified']
410
        t = datetime.datetime.strptime(last_modified, DATE_FORMATS[-1])
411
        t2 = t - datetime.timedelta(seconds=1)
412
        t2_formats = map(t2.strftime, DATE_FORMATS)
413

    
414
        # Check modified
415
        for t in t2_formats:
416
            r = self.get(url, HTTP_IF_UNMODIFIED_SINCE=t)
417
            self.assertEqual(r.status_code, 412)
418

    
419
        # modify account: update account meta
420
        _time.sleep(1)
421
        self.update_object_meta(cname, oname, {'foo': 'bar'})
422

    
423
        object_info = self.get_object_info(cname, oname)
424
        last_modified = object_info['Last-Modified']
425
        t = datetime.datetime.strptime(last_modified, DATE_FORMATS[-1])
426
        t3 = t - datetime.timedelta(seconds=1)
427
        t3_formats = map(t3.strftime, DATE_FORMATS)
428

    
429
        # Check modified
430
        for t in t3_formats:
431
            r = self.get(url, HTTP_IF_UNMODIFIED_SINCE=t)
432
            self.assertEqual(r.status_code, 412)
433

    
434
    def test_if_unmodified_since(self):
435
        cname = self.containers[0]
436
        oname, odata = self.upload_object(cname, length=1024)[:-1]
437
        url = join_urls(self.pithos_path, self.user, cname, oname)
438
        object_info = self.get_object_info(cname, oname)
439
        last_modified = object_info['Last-Modified']
440
        t = datetime.datetime.strptime(last_modified, DATE_FORMATS[-1])
441
        t = t + datetime.timedelta(seconds=1)
442
        t_formats = map(t.strftime, DATE_FORMATS)
443

    
444
        for tf in t_formats:
445
            r = self.get(url, HTTP_IF_UNMODIFIED_SINCE=tf)
446
            self.assertEqual(r.status_code, 200)
447
            self.assertEqual(r.content, odata)
448

    
449
    def test_if_unmodified_since_precondition_failed(self):
450
        cname = self.containers[0]
451
        oname, odata = self.upload_object(cname, length=1024)[:-1]
452
        url = join_urls(self.pithos_path, self.user, cname, oname)
453
        object_info = self.get_object_info(cname, oname)
454
        last_modified = object_info['Last-Modified']
455
        t = datetime.datetime.strptime(last_modified, DATE_FORMATS[-1])
456
        t = t - datetime.timedelta(seconds=1)
457
        t_formats = map(t.strftime, DATE_FORMATS)
458

    
459
        for tf in t_formats:
460
            r = self.get(url, HTTP_IF_UNMODIFIED_SINCE=tf)
461
            self.assertEqual(r.status_code, 412)
462

    
463
    def test_hashes(self):
464
        l = random.randint(2, 5) * pithos_settings.BACKEND_BLOCK_SIZE
465
        cname = self.containers[0]
466
        oname, odata = self.upload_object(cname, length=l)[:-1]
467
        size = len(odata)
468

    
469
        url = join_urls(self.pithos_path, self.user, cname, oname)
470
        r = self.get('%s?format=json&hashmap' % url)
471
        self.assertEqual(r.status_code, 200)
472
        body = json.loads(r.content)
473

    
474
        hashes = body['hashes']
475
        block_size = body['block_size']
476
        block_hash = body['block_hash']
477
        block_num = size / block_size if size / block_size == 0 else\
478
            size / block_size + 1
479
        self.assertTrue(len(hashes), block_num)
480
        i = 0
481
        for h in hashes:
482
            start = i * block_size
483
            end = (i + 1) * block_size
484
            hash = merkle(odata[start:end], int(block_size), block_hash)
485
            self.assertEqual(h, hash)
486
            i += 1
487

    
488

    
489
class ObjectPut(PithosAPITest):
490
    def setUp(self):
491
        PithosAPITest.setUp(self)
492
        self.container = get_random_data(8)
493
        self.create_container(self.container)
494

    
495
    def test_upload(self):
496
        cname = self.container
497
        oname = get_random_data(8)
498
        data = get_random_data(length=1024)
499
        meta = {'test': 'test1'}
500
        headers = dict(('HTTP_X_OBJECT_META_%s' % k.upper(), v)
501
                       for k, v in meta.iteritems())
502
        url = join_urls(self.pithos_path, self.user, cname, oname)
503
        r = self.put(url, data=data, content_type='application/pdf', **headers)
504
        self.assertEqual(r.status_code, 201)
505
        self.assertTrue('ETag' in r)
506
        self.assertTrue('X-Object-Version' in r)
507

    
508
        info = self.get_object_info(cname, oname)
509

    
510
        # assert object meta
511
        self.assertTrue('X-Object-Meta-Test' in info)
512
        self.assertEqual(info['X-Object-Meta-Test'], 'test1')
513

    
514
        # assert content-type
515
        self.assertEqual(info['content-type'], 'application/pdf')
516

    
517
        # assert uploaded content
518
        r = self.get(url)
519
        self.assertEqual(r.status_code, 200)
520
        self.assertEqual(r.content, data)
521

    
522
    def test_maximum_upload_size_exceeds(self):
523
        cname = self.container
524
        oname = get_random_data(8)
525

    
526
#        info = self.get_account_info()
527
#        length = int(info['X-Account-Policy-Quota']) + 1
528
#        data = get_random_data(length)
529
#        url = join_urls(self.pithos_path, self.user, cname, oname)
530
#        r = self.put(url, data=data)
531
#        self.assertEqual(r.status_code, 413)
532

    
533
        # set container quota to 100
534
        url = join_urls(self.pithos_path, self.user, cname)
535
        r = self.post(url, HTTP_X_CONTAINER_POLICY_QUOTA='100')
536
        self.assertEqual(r.status_code, 202)
537

    
538
        info = self.get_container_info(cname)
539
        length = int(info['X-Container-Policy-Quota']) + 1
540

    
541
        data = get_random_data(length)
542
        url = join_urls(self.pithos_path, self.user, cname, oname)
543
        r = self.put(url, data=data)
544
        self.assertEqual(r.status_code, 413)
545

    
546
    def test_upload_with_name_containing_slash(self):
547
        cname = self.container
548
        oname = '/%s' % get_random_data(8)
549
        data = get_random_data(1024)
550
        url = join_urls(self.pithos_path, self.user, cname, oname)
551
        r = self.put(url, data=data)
552
        self.assertEqual(r.status_code, 201)
553
        self.assertTrue('ETag' in r)
554
        self.assertTrue('X-Object-Version' in r)
555

    
556
        r = self.get(url)
557
        self.assertEqual(r.status_code, 200)
558
        self.assertEqual(r.content, data)
559

    
560
    def test_upload_unprocessable_entity(self):
561
        cname = self.container
562
        oname = get_random_data(8)
563
        data = get_random_data(1024)
564
        url = join_urls(self.pithos_path, self.user, cname, oname)
565
        r = self.put(url, data=data, HTTP_ETAG='123')
566
        self.assertEqual(r.status_code, 422)
567

    
568
#    def test_chunked_transfer(self):
569
#        cname = self.container
570
#        oname = '/%s' % get_random_data(8)
571
#        data = get_random_data(1024)
572
#        url = join_urls(self.pithos_path, self.user, cname, oname)
573
#        r = self.put(url, data=data, HTTP_TRANSFER_ENCODING='chunked')
574
#        self.assertEqual(r.status_code, 201)
575
#        self.assertTrue('ETag' in r)
576
#        self.assertTrue('X-Object-Version' in r)
577

    
578
    def test_manifestation(self):
579
        cname = self.container
580
        prefix = 'myobject/'
581
        data = ''
582
        for i in range(random.randint(2, 10)):
583
            part = '%s%d' % (prefix, i)
584
            data += self.upload_object(cname, oname=part)[1]
585

    
586
        manifest = '%s/%s' % (cname, prefix)
587
        oname = get_random_data(8)
588
        url = join_urls(self.pithos_path, self.user, cname, oname)
589
        r = self.put(url, data='', HTTP_X_OBJECT_MANIFEST=manifest)
590
        self.assertEqual(r.status_code, 201)
591

    
592
        # assert object exists
593
        r = self.get(url)
594
        self.assertEqual(r.status_code, 200)
595

    
596
        # assert its content
597
        self.assertEqual(r.content, data)
598

    
599
        # invalid manifestation
600
        invalid_manifestation = '%s/%s' % (cname, get_random_data(8))
601
        self.put(url, data='', HTTP_X_OBJECT_MANIFEST=invalid_manifestation)
602
        r = self.get(url)
603
        self.assertEqual(r.content, '')
604

    
605
    def test_create_zero_length_object(self):
606
        cname = self.container
607
        oname = get_random_data(8)
608
        url = join_urls(self.pithos_path, self.user, cname, oname)
609
        r = self.put(url, data='')
610
        self.assertEqual(r.status_code, 201)
611

    
612
        r = self.get(url)
613
        self.assertEqual(r.status_code, 200)
614
        self.assertEqual(int(r['Content-Length']), 0)
615
        self.assertEqual(r.content, '')
616

    
617
        r = self.get('%s?hashmap=&format=json' % url)
618
        self.assertEqual(r.status_code, 200)
619
        body = json.loads(r.content)
620
        hashes = body['hashes']
621
        block_size = body['block_size']
622
        block_hash = body['block_hash']
623
        hash = merkle('', int(block_size), block_hash)
624
        self.assertEqual(hashes, [hash])
625

    
626
    def test_create_object_by_hashmap(self):
627
        cname = self.container
628
        block_size = pithos_settings.BACKEND_BLOCK_SIZE
629

    
630
        # upload an object
631
        oname, data = self.upload_object(cname, length=block_size + 1)[:-1]
632
        # get it hashmap
633
        url = join_urls(self.pithos_path, self.user, cname, oname)
634
        r = self.get('%s?hashmap=&format=json' % url)
635

    
636
        oname = get_random_data(8)
637
        url = join_urls(self.pithos_path, self.user, cname, oname)
638
        r = self.put('%s?hashmap=' % url, data=r.content)
639
        self.assertEqual(r.status_code, 201)
640

    
641
        r = self.get(url)
642
        self.assertEqual(r.status_code, 200)
643
        self.assertEqual(r.content, data)
644

    
645

    
646
class ObjectCopy(PithosAPITest):
647
    def setUp(self):
648
        PithosAPITest.setUp(self)
649
        self.container = 'c1'
650
        self.create_container(self.container)
651
        self.object, self.data = self.upload_object(self.container)[:-1]
652

    
653
        url = join_urls(
654
            self.pithos_path, self.user, self.container, self.object)
655
        r = self.head(url)
656
        self.etag = r['X-Object-Hash']
657

    
658
    def test_copy(self):
659
        with AssertMappingInvariant(self.get_object_info, self.container,
660
                                    self.object):
661
            # copy object
662
            oname = get_random_data(8)
663
            url = join_urls(self.pithos_path, self.user, self.container, oname)
664
            r = self.put(url, data='', HTTP_X_OBJECT_META_TEST='testcopy',
665
                         HTTP_X_COPY_FROM='/%s/%s' % (
666
                             self.container, self.object))
667

    
668
            # assert copy success
669
            self.assertEqual(r.status_code, 201)
670

    
671
            # assert access the new object
672
            r = self.head(url)
673
            self.assertEqual(r.status_code, 200)
674
            self.assertTrue('X-Object-Meta-Test' in r)
675
            self.assertEqual(r['X-Object-Meta-Test'], 'testcopy')
676

    
677
            # assert etag is the same
678
            self.assertTrue('X-Object-Hash' in r)
679
            self.assertEqual(r['X-Object-Hash'], self.etag)
680

    
681
    def test_copy_from_different_container(self):
682
        cname = 'c2'
683
        self.create_container(cname)
684
        with AssertMappingInvariant(self.get_object_info, self.container,
685
                                    self.object):
686
            oname = get_random_data(8)
687
            url = join_urls(self.pithos_path, self.user, cname, oname)
688
            r = self.put(url, data='', HTTP_X_OBJECT_META_TEST='testcopy',
689
                         HTTP_X_COPY_FROM='/%s/%s' % (
690
                             self.container, self.object))
691

    
692
            # assert copy success
693
            self.assertEqual(r.status_code, 201)
694

    
695
            # assert access the new object
696
            r = self.head(url)
697
            self.assertEqual(r.status_code, 200)
698
            self.assertTrue('X-Object-Meta-Test' in r)
699
            self.assertEqual(r['X-Object-Meta-Test'], 'testcopy')
700

    
701
            # assert etag is the same
702
            self.assertTrue('X-Object-Hash' in r)
703
            self.assertEqual(r['X-Object-Hash'], self.etag)
704

    
705
    def test_copy_invalid(self):
706
        # copy from non-existent object
707
        oname = get_random_data(8)
708
        url = join_urls(self.pithos_path, self.user, self.container, oname)
709
        r = self.put(url, data='', HTTP_X_OBJECT_META_TEST='testcopy',
710
                     HTTP_X_COPY_FROM='/%s/%s' % (
711
                         self.container, get_random_data(8)))
712
        self.assertEqual(r.status_code, 404)
713

    
714
        # copy from non-existent container
715
        oname = get_random_data(8)
716
        url = join_urls(self.pithos_path, self.user, self.container, oname)
717
        r = self.put(url, data='', HTTP_X_OBJECT_META_TEST='testcopy',
718
                     HTTP_X_COPY_FROM='/%s/%s' % (
719
                         get_random_data(8), self.object))
720
        self.assertEqual(r.status_code, 404)
721

    
722
    def test_copy_dir(self):
723
        folder = self.create_folder(self.container)[0]
724
        subfolder = self.create_folder(
725
            self.container, oname='%s/%s' % (folder, get_random_data(8)))[0]
726
        objects = [subfolder]
727
        append = objects.append
728
        append(self.upload_object(self.container,
729
                                  '%s/%s' % (folder, get_random_data(8)),
730
                                  HTTP_X_OBJECT_META_DEPTH='1')[0])
731
        append(self.upload_object(self.container,
732
                                  '%s/%s' % (subfolder, get_random_data(8)),
733
                                  HTTP_X_OBJECT_META_DEPTH='2')[0])
734
        other = self.upload_object(self.container, strnextling(folder))[0]
735

    
736
        # copy dir
737
        copy_folder = self.create_folder(self.container)[0]
738
        url = join_urls(self.pithos_path, self.user, self.container,
739
                        copy_folder)
740
        r = self.put('%s?delimiter=/' % url, data='',
741
                     HTTP_X_COPY_FROM='/%s/%s' % (self.container, folder))
742
        self.assertEqual(r.status_code, 201)
743

    
744
        for obj in objects:
745
            # assert object exists
746
            url = join_urls(self.pithos_path, self.user, self.container,
747
                            obj.replace(folder, copy_folder))
748
            r = self.head(url)
749
            self.assertEqual(r.status_code, 200)
750

    
751
            # assert metadata
752
            meta = self.get_object_meta(self.container, obj)
753
            for k in meta.keys():
754
                self.assertTrue(k in r)
755
                self.assertEqual(r[k], meta[k])
756

    
757
        # assert other has not been created under copy folder
758
        url = join_urls(self.pithos_path, self.user, self.container,
759
                        '%s/%s' % (copy_folder,
760
                                   other.replace(folder, copy_folder)))
761
        r = self.head(url)
762
        self.assertEqual(r.status_code, 404)
763

    
764

    
765
class ObjectMove(PithosAPITest):
766
    def setUp(self):
767
        PithosAPITest.setUp(self)
768
        self.container = 'c1'
769
        self.create_container(self.container)
770
        self.object, self.data = self.upload_object(self.container)[:-1]
771

    
772
        url = join_urls(
773
            self.pithos_path, self.user, self.container, self.object)
774
        r = self.head(url)
775
        self.etag = r['X-Object-Hash']
776

    
777
    def test_move(self):
778
        # move object
779
        oname = get_random_data(8)
780
        url = join_urls(self.pithos_path, self.user, self.container, oname)
781
        r = self.put(url, data='', HTTP_X_OBJECT_META_TEST='testcopy',
782
                     HTTP_X_MOVE_FROM='/%s/%s' % (
783
                         self.container, self.object))
784

    
785
        # assert move success
786
        self.assertEqual(r.status_code, 201)
787

    
788
        # assert access the new object
789
        r = self.head(url)
790
        self.assertEqual(r.status_code, 200)
791
        self.assertTrue('X-Object-Meta-Test' in r)
792
        self.assertEqual(r['X-Object-Meta-Test'], 'testcopy')
793

    
794
        # assert etag is the same
795
        self.assertTrue('X-Object-Hash' in r)
796

    
797
        # assert the initial object has been deleted
798
        url = join_urls(self.pithos_path, self.user, self.container,
799
                        self.object)
800
        r = self.head(url)
801
        self.assertEqual(r.status_code, 404)
802

    
803
    def test_move_dir(self):
804
        folder = self.create_folder(self.container)[0]
805
        subfolder = self.create_folder(
806
            self.container, oname='%s/%s' % (folder, get_random_data(8)))[0]
807
        objects = [subfolder]
808
        append = objects.append
809
        meta = {}
810
        meta[objects[0]] = {}
811
        append(self.upload_object(self.container,
812
                                  '%s/%s' % (folder, get_random_data(8)),
813
                                  HTTP_X_OBJECT_META_DEPTH='1')[0])
814
        meta[objects[1]] = {'X-Object-Meta-Depth': '1'}
815
        append(self.upload_object(self.container,
816
                                  '%s/%s' % (subfolder, get_random_data(8)),
817
                                  HTTP_X_OBJECT_META_DEPTH='2')[0])
818
        meta[objects[1]] = {'X-Object-Meta-Depth': '2'}
819
        other = self.upload_object(self.container, strnextling(folder))[0]
820

    
821
        # move dir
822
        copy_folder = self.create_folder(self.container)[0]
823
        url = join_urls(self.pithos_path, self.user, self.container,
824
                        copy_folder)
825
        r = self.put('%s?delimiter=/' % url, data='',
826
                     HTTP_X_MOVE_FROM='/%s/%s' % (self.container, folder))
827
        self.assertEqual(r.status_code, 201)
828

    
829
        for obj in objects:
830
            # assert initial object does not exist
831
            url = join_urls(self.pithos_path, self.user, self.container, obj)
832
            r = self.head(url)
833
            self.assertEqual(r.status_code, 404)
834

    
835
            # assert new object was created
836
            url = join_urls(self.pithos_path, self.user, self.container,
837
                            obj.replace(folder, copy_folder))
838
            r = self.head(url)
839
            self.assertEqual(r.status_code, 200)
840

    
841
#            # assert metadata
842
#            for k in meta[obj].keys():
843
#                self.assertTrue(k in r)
844
#                self.assertEqual(r[k], meta[obj][k])
845

    
846
        # assert other has not been created under copy folder
847
        url = join_urls(self.pithos_path, self.user, self.container,
848
                        '%s/%s' % (copy_folder,
849
                                   other.replace(folder, copy_folder)))
850
        r = self.head(url)
851
        self.assertEqual(r.status_code, 404)
852

    
853

    
854
class ObjectPost(PithosAPITest):
855
    def setUp(self):
856
        PithosAPITest.setUp(self)
857
        self.container = 'c1'
858
        self.create_container(self.container)
859
        self.object, self.object_data = self.upload_object(self.container)[:2]
860

    
861
    def test_update_meta(self):
862
        with AssertUUidInvariant(self.get_object_info,
863
                                 self.container,
864
                                 self.object):
865
            # update metadata
866
            d = {'a' * 114: 'b' * 256}
867
            kwargs = dict(('HTTP_X_OBJECT_META_%s' % k, v) for
868
                          k, v in d.items())
869
            url = join_urls(self.pithos_path, self.user, self.container,
870
                            self.object)
871
            r = self.post(url, content_type='', **kwargs)
872
            self.assertEqual(r.status_code, 202)
873

    
874
            # assert metadata have been updated
875
            meta = self.get_object_meta(self.container, self.object)
876

    
877
            for k, v in d.items():
878
                key = 'X-Object-Meta-%s' % k.title()
879
                self.assertTrue(key in meta)
880
                self.assertTrue(meta[key], v)
881

    
882
            # Header key too large
883
            d = {'a' * 115: 'b' * 256}
884
            kwargs = dict(('HTTP_X_OBJECT_META_%s' % k, v) for
885
                          k, v in d.items())
886
            r = self.post(url, content_type='', **kwargs)
887
            self.assertEqual(r.status_code, 400)
888

    
889
            # Header value too large
890
            d = {'a' * 114: 'b' * 257}
891
            kwargs = dict(('HTTP_X_OBJECT_META_%s' % k, v) for
892
                          k, v in d.items())
893
            r = self.post(url, content_type='', **kwargs)
894
            self.assertEqual(r.status_code, 400)
895

    
896
#            # Check utf-8 meta
897
#            d = {'α' * (114 / 2): 'β' * (256 / 2)}
898
#            kwargs = dict(('HTTP_X_OBJECT_META_%s' % quote(k), quote(v)) for
899
#                          k, v in d.items())
900
#            url = join_urls(self.pithos_path, self.user, self.container,
901
#                            self.object)
902
#            r = self.post(url, content_type='', **kwargs)
903
#            self.assertEqual(r.status_code, 202)
904
#
905
#            # assert metadata have been updated
906
#            meta = self.get_object_meta(self.container, self.object)
907
#
908
#            for k, v in d.items():
909
#                key = 'X-Object-Meta-%s' % k.title()
910
#                self.assertTrue(key in meta)
911
#                self.assertTrue(meta[key], v)
912
#
913
#            # Header key too large
914
#            d = {'α' * 114: 'β' * (256 / 2)}
915
#            kwargs = dict(('HTTP_X_OBJECT_META_%s' % quote(k), quote(v)) for
916
#                          k, v in d.items())
917
#            r = self.post(url, content_type='', **kwargs)
918
#            self.assertEqual(r.status_code, 400)
919
#
920
#            # Header value too large
921
#            d = {'α' * 114: 'β' * 256}
922
#            kwargs = dict(('HTTP_X_OBJECT_META_%s' % quote(k), quote(v)) for
923
#                          k, v in d.items())
924
#            r = self.udpate(url, content_type='', **kwargs)
925
#            self.assertEqual(r.status_code, 400)
926

    
927
    def test_update_object(self):
928
        block_size = pithos_settings.BACKEND_BLOCK_SIZE
929
        oname, odata = self.upload_object(
930
            self.container, length=random.randint(
931
                block_size + 1, 2 * block_size))[:2]
932

    
933
        length = len(odata)
934
        first_byte_pos = random.randint(1, block_size)
935
        last_byte_pos = random.randint(block_size + 1, length - 1)
936
        range = 'bytes %s-%s/%s' % (first_byte_pos, last_byte_pos, length)
937
        kwargs = {'content_type': 'application/octet-stream',
938
                  'HTTP_CONTENT_RANGE': range}
939

    
940
        url = join_urls(self.pithos_path, self.user, self.container, oname)
941
        partial = last_byte_pos - first_byte_pos + 1
942
        data = get_random_data(partial)
943
        r = self.post(url, data=data, **kwargs)
944

    
945
        self.assertEqual(r.status_code, 204)
946
        self.assertTrue('ETag' in r)
947
        updated_data = odata.replace(odata[first_byte_pos: last_byte_pos + 1],
948
                                     data)
949
        if pithos_settings.UPDATE_MD5:
950
            etag = compute_md5_hash(updated_data)
951
        else:
952
            etag = merkle(updated_data)
953
        #self.assertEqual(r['ETag'], etag)
954

    
955
        # check modified object
956
        r = self.get(url)
957

    
958
        self.assertEqual(r.status_code, 200)
959
        self.assertEqual(r.content, updated_data)
960
        self.assertEqual(etag, r['ETag'])
961

    
962
    def test_update_object_divided_by_blocksize(self):
963
        block_size = pithos_settings.BACKEND_BLOCK_SIZE
964
        oname, odata = self.upload_object(self.container,
965
                                          length=2 * block_size)[:2]
966

    
967
        length = len(odata)
968
        first_byte_pos = block_size
969
        last_byte_pos = 2 * block_size - 1
970
        range = 'bytes %s-%s/%s' % (first_byte_pos, last_byte_pos, length)
971
        kwargs = {'content_type': 'application/octet-stream',
972
                  'HTTP_CONTENT_RANGE': range}
973

    
974
        url = join_urls(self.pithos_path, self.user, self.container, oname)
975
        partial = last_byte_pos - first_byte_pos + 1
976
        data = get_random_data(partial)
977
        r = self.post(url, data=data, **kwargs)
978

    
979
        self.assertEqual(r.status_code, 204)
980
        self.assertTrue('ETag' in r)
981
        updated_data = odata.replace(odata[first_byte_pos: last_byte_pos + 1],
982
                                     data)
983
        if pithos_settings.UPDATE_MD5:
984
            etag = compute_md5_hash(updated_data)
985
        else:
986
            etag = merkle(updated_data)
987
        #self.assertEqual(r['ETag'], etag)
988

    
989
        # check modified object
990
        r = self.get(url)
991

    
992
        self.assertEqual(r.status_code, 200)
993
        self.assertEqual(r.content, updated_data)
994
        self.assertEqual(etag, r['ETag'])
995

    
996
    def test_update_object_invalid_content_length(self):
997
        block_size = pithos_settings.BACKEND_BLOCK_SIZE
998
        oname, odata = self.upload_object(
999
            self.container, length=random.randint(
1000
                block_size + 1, 2 * block_size))[:2]
1001

    
1002
        length = len(odata)
1003
        first_byte_pos = random.randint(1, block_size)
1004
        last_byte_pos = random.randint(block_size + 1, length - 1)
1005
        partial = last_byte_pos - first_byte_pos + 1
1006
        data = get_random_data(partial)
1007
        range = 'bytes %s-%s/%s' % (first_byte_pos, last_byte_pos, length)
1008
        kwargs = {'content_type': 'application/octet-stream',
1009
                  'HTTP_CONTENT_RANGE': range,
1010
                  'CONTENT_LENGTH': partial + 1}
1011

    
1012
        url = join_urls(self.pithos_path, self.user, self.container, oname)
1013
        r = self.post(url, data=data, **kwargs)
1014

    
1015
        self.assertEqual(r.status_code, 400)
1016

    
1017
    def test_update_object_invalid_range(self):
1018
        block_size = pithos_settings.BACKEND_BLOCK_SIZE
1019
        oname, odata = self.upload_object(
1020
            self.container, length=random.randint(block_size + 1,
1021
                                                  2 * block_size))[:2]
1022

    
1023
        length = len(odata)
1024
        first_byte_pos = random.randint(1, block_size)
1025
        last_byte_pos = first_byte_pos - 1
1026
        range = 'bytes %s-%s/%s' % (first_byte_pos, last_byte_pos, length)
1027
        kwargs = {'content_type': 'application/octet-stream',
1028
                  'HTTP_CONTENT_RANGE': range}
1029

    
1030
        url = join_urls(self.pithos_path, self.user, self.container, oname)
1031
        r = self.post(url, data=get_random_data(1024), **kwargs)
1032

    
1033
        self.assertEqual(r.status_code, 416)
1034

    
1035
    def test_update_object_out_of_limits(self):
1036
        block_size = pithos_settings.BACKEND_BLOCK_SIZE
1037
        oname, odata = self.upload_object(
1038
            self.container, length=random.randint(block_size + 1,
1039
                                                  2 * block_size))[:2]
1040

    
1041
        length = len(odata)
1042
        first_byte_pos = random.randint(1, block_size)
1043
        last_byte_pos = length + 1
1044
        range = 'bytes %s-%s/%s' % (first_byte_pos, last_byte_pos, length)
1045
        kwargs = {'content_type': 'application/octet-stream',
1046
                  'HTTP_CONTENT_RANGE': range}
1047

    
1048
        url = join_urls(self.pithos_path, self.user, self.container, oname)
1049
        r = self.post(url, data=get_random_data(1024), **kwargs)
1050

    
1051
        self.assertEqual(r.status_code, 416)
1052

    
1053
    def test_append(self):
1054
        length = random.randint(1, 1024)
1055
        data = get_random_data(length)
1056
        url = join_urls(self.pithos_path, self.user, self.container,
1057
                        self.object)
1058
        r = self.post(url, data=data, content_type='application/octet-stream',
1059
                      HTTP_CONTENT_LENGTH=str(length),
1060
                      HTTP_CONTENT_RANGE='bytes */*')
1061
        self.assertEqual(r.status_code, 204)
1062

    
1063
        r = self.get(url)
1064
        content = r.content
1065
        self.assertEqual(len(content), len(self.object_data) + length)
1066
        self.assertEqual(content, self.object_data + data)
1067

    
1068
    def test_update_with_chunked_transfer(self):
1069
        length = random.randint(1, 1024)
1070
        data = get_random_data(length)
1071

    
1072
        url = join_urls(self.pithos_path, self.user, self.container,
1073
                        self.object)
1074
        r = self.post(url, data=data, content_type='application/octet-stream',
1075
                      HTTP_CONTENT_RANGE='bytes 0-/*',
1076
                      HTTP_TRANSFER_ENCODING='chunked')
1077
        self.assertEqual(r.status_code, 204)
1078

    
1079
        # check modified object
1080
        r = self.get(url)
1081
        content = r.content
1082
        self.assertEqual(content[0:length], data)
1083
        self.assertEqual(content[length:], self.object_data[length:])
1084

    
1085
    def test_update_from_other_object(self):
1086
        src = self.object
1087
        dest = get_random_data(8)
1088

    
1089
        url = join_urls(self.pithos_path, self.user, self.container, src)
1090
        r = self.get(url)
1091
        source_data = r.content
1092
        source_meta = self.get_object_info(self.container, src)
1093

    
1094
        # update zero length object
1095
        url = join_urls(self.pithos_path, self.user, self.container, dest)
1096
        r = self.put(url, data='')
1097
        self.assertEqual(r.status_code, 201)
1098

    
1099
        r = self.post(url,
1100
                      HTTP_CONTENT_RANGE='bytes */*',
1101
                      HTTP_X_SOURCE_OBJECT='/%s/%s' % (self.container, src))
1102
        self.assertEqual(r.status_code, 204)
1103

    
1104
        r = self.get(url)
1105
        dest_data = r.content
1106
        dest_meta = self.get_object_info(self.container, dest)
1107

    
1108
        self.assertEqual(source_data, dest_data)
1109
        #self.assertEqual(source_meta['ETag'], dest_meta['ETag'])
1110
        self.assertEqual(source_meta['X-Object-Hash'],
1111
                         dest_meta['X-Object-Hash'])
1112
        self.assertTrue(
1113
            source_meta['X-Object-UUID'] != dest_meta['X-Object-UUID'])
1114

    
1115
    def test_update_range_from_other_object(self):
1116
        src = self.object
1117
        dest = get_random_data(8)
1118

    
1119
        url = join_urls(self.pithos_path, self.user, self.container, src)
1120
        r = self.get(url)
1121
        source_data = r.content
1122

    
1123
        # update zero length object
1124
        url = join_urls(self.pithos_path, self.user, self.container, dest)
1125
        length = random.randint(1, 1024)
1126
        r = self.put(url, data=get_random_data(length))
1127
        self.assertEqual(r.status_code, 201)
1128

    
1129
        offset = random.randint(1, length - 2)
1130
        upto = random.randint(offset, length - 1)
1131
        r = self.post(url,
1132
                      HTTP_CONTENT_RANGE='bytes %s-%s/*' % (offset, upto),
1133
                      HTTP_X_SOURCE_OBJECT='/%s/%s' % (self.container, src))
1134
        self.assertEqual(r.status_code, 204)
1135

    
1136
        r = self.get(url)
1137
        content = r.content
1138
        self.assertEqual(content[:offset], source_data[:offset])
1139
        self.assertEqual(content[offset:upto + 1],
1140
                         source_data[:upto - offset + 1])
1141
        self.assertEqual(content[upto + 1:], source_data[upto + 1:])
1142

    
1143

    
1144
class ObjectDelete(PithosAPITest):
1145
    def setUp(self):
1146
        PithosAPITest.setUp(self)
1147
        self.container = 'c1'
1148
        self.create_container(self.container)
1149
        self.object, self.object_data = self.upload_object(self.container)[:2]
1150

    
1151
    def test_delete(self):
1152
        url = join_urls(self.pithos_path, self.user, self.container,
1153
                        self.object)
1154
        r = self.delete(url)
1155
        self.assertEqual(r.status_code, 204)
1156

    
1157
        r = self.head(url)
1158
        self.assertEqual(r.status_code, 404)
1159

    
1160
    def test_delete_non_existent(self):
1161
        url = join_urls(self.pithos_path, self.user, self.container,
1162
                        get_random_data(8))
1163
        r = self.delete(url)
1164
        self.assertEqual(r.status_code, 404)
1165

    
1166
    def test_delete_dir(self):
1167
        folder = self.create_folder(self.container)[0]
1168
        subfolder = self.create_folder(
1169
            self.container, oname='%s/%s' % (folder, get_random_data(8)))[0]
1170
        objects = [subfolder]
1171
        append = objects.append
1172
        meta = {}
1173
        meta[objects[0]] = {}
1174
        append(self.upload_object(self.container,
1175
                                  '%s/%s' % (folder, get_random_data(8)),
1176
                                  HTTP_X_OBJECT_META_DEPTH='1')[0])
1177
        meta[objects[1]] = {'X-Object-Meta-Depth': '1'}
1178
        append(self.upload_object(self.container,
1179
                                  '%s/%s' % (subfolder, get_random_data(8)),
1180
                                  HTTP_X_OBJECT_META_DEPTH='2')[0])
1181
        meta[objects[1]] = {'X-Object-Meta-Depth': '2'}
1182
        other = self.upload_object(self.container, strnextling(folder))[0]
1183

    
1184
        # move dir
1185
        url = join_urls(self.pithos_path, self.user, self.container, folder)
1186
        r = self.delete('%s?delimiter=/' % url)
1187
        self.assertEqual(r.status_code, 204)
1188

    
1189
        for obj in objects:
1190
            # assert object does not exist
1191
            url = join_urls(self.pithos_path, self.user, self.container, obj)
1192
            r = self.head(url)
1193
            self.assertEqual(r.status_code, 404)
1194

    
1195
        # assert other has not been deleted
1196
        url = join_urls(self.pithos_path, self.user, self.container, other)
1197
        r = self.head(url)
1198
        self.assertEqual(r.status_code, 200)