Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (45.1 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
from functools import partial
40

    
41
from pithos.api.test import (PithosAPITest, pithos_settings,
42
                             AssertMappingInvariant, AssertUUidInvariant,
43
                             TEST_BLOCK_SIZE, TEST_HASH_ALGORITHM,
44
                             DATE_FORMATS)
45
from pithos.api.test.util import (md5_hash, merkle, strnextling,
46
                                  get_random_data, get_random_name)
47

    
48
from synnefo.lib import join_urls
49

    
50
import django.utils.simplejson as json
51

    
52
import random
53
import re
54
import datetime
55
import time as _time
56

    
57
merkle = partial(merkle,
58
                 blocksize=TEST_BLOCK_SIZE,
59
                 blockhash=TEST_HASH_ALGORITHM)
60

    
61

    
62
class ObjectGet(PithosAPITest):
63
    def setUp(self):
64
        PithosAPITest.setUp(self)
65
        self.containers = ['c1', 'c2']
66

    
67
        # create some containers
68
        for c in self.containers:
69
            self.create_container(c)
70

    
71
        # upload files
72
        self.objects = defaultdict(list)
73
        self.objects['c1'].append(self.upload_object('c1')[0])
74

    
75
    def test_versions(self):
76
        c = 'c1'
77
        o = self.objects[c][0]
78
        url = join_urls(self.pithos_path, self.user, c, o)
79

    
80
        meta = {'HTTP_X_OBJECT_META_QUALITY': 'AAA'}
81
        r = self.post(url, content_type='', **meta)
82
        self.assertEqual(r.status_code, 202)
83

    
84
        url = join_urls(self.pithos_path, self.user, c, o)
85
        r = self.get('%s?version=list&format=json' % url)
86
        self.assertEqual(r.status_code, 200)
87
        l1 = json.loads(r.content)['versions']
88
        self.assertEqual(len(l1), 2)
89

    
90
        # update meta
91
        meta = {'HTTP_X_OBJECT_META_QUALITY': 'AB',
92
                'HTTP_X_OBJECT_META_STOCK': 'True'}
93
        r = self.post(url, content_type='', **meta)
94
        self.assertEqual(r.status_code, 202)
95

    
96
        # assert a newly created version has been created
97
        r = self.get('%s?version=list&format=json' % url)
98
        self.assertEqual(r.status_code, 200)
99
        l2 = json.loads(r.content)['versions']
100
        self.assertEqual(len(l2), len(l1) + 1)
101
        self.assertEqual(l2[:-1], l1)
102

    
103
        vserial, _ = l2[-2]
104
        self.assertEqual(self.get_object_meta(c, o, version=vserial),
105
                         {'X-Object-Meta-Quality': 'AAA'})
106

    
107
        # update data
108
        self.append_object_data(c, o)
109

    
110
        # assert a newly created version has been created
111
        r = self.get('%s?version=list&format=json' % url)
112
        self.assertEqual(r.status_code, 200)
113
        l3 = json.loads(r.content)['versions']
114
        self.assertEqual(len(l3), len(l2) + 1)
115
        self.assertEqual(l3[:-1], l2)
116

    
117
    def test_objects_with_trailing_spaces(self):
118
        # create object
119
        oname = self.upload_object('c1')[0]
120
        url = join_urls(self.pithos_path, self.user, 'c1', oname)
121

    
122
        r = self.get(quote('%s ' % url))
123
        self.assertEqual(r.status_code, 404)
124

    
125
        # delete object
126
        self.delete(url)
127

    
128
        r = self.get(url)
129
        self.assertEqual(r.status_code, 404)
130

    
131
        # upload object with trailing space
132
        oname = self.upload_object('c1', quote('%s ' % get_random_name()))[0]
133

    
134
        url = join_urls(self.pithos_path, self.user, 'c1', oname)
135
        r = self.get(url)
136
        self.assertEqual(r.status_code, 200)
137

    
138
        url = join_urls(self.pithos_path, self.user, 'c1', oname[:-1])
139
        r = self.get(url)
140
        self.assertEqual(r.status_code, 404)
141

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

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

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

    
184
    def test_get_range_not_satisfiable(self):
185
        cname = self.containers[0]
186
        oname, odata = self.upload_object(cname, length=512)[:-1]
187
        url = join_urls(self.pithos_path, self.user, cname, oname)
188

    
189
        # TODO
190
        #r = self.get(url, HTTP_RANGE='bytes=50-10')
191
        #self.assertEqual(r.status_code, 416)
192

    
193
        offset = len(odata) + 1
194
        r = self.get(url, HTTP_RANGE='bytes=0-%s' % offset)
195
        self.assertEqual(r.status_code, 416)
196

    
197
    def test_multiple_range(self):
198
        cname = self.containers[0]
199
        oname, odata = self.upload_object(cname)[:-1]
200
        url = join_urls(self.pithos_path, self.user, cname, oname)
201

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

    
216
        # assert content parts length
217
        self.assertEqual(len(cparts), len(l))
218

    
219
        # for each content part assert headers
220
        i = 0
221
        for cpart in cparts:
222
            content = cpart.split('\r\n')
223
            headers = content[1:3]
224
            content_range = headers[0].split(': ')
225
            self.assertEqual(content_range[0], 'Content-Range')
226

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

    
245
    def test_multiple_range_not_satisfiable(self):
246
        # perform get with multiple range
247
        cname = self.containers[0]
248
        oname, odata = self.upload_object(cname)[:-1]
249
        out_of_range = len(odata) + 1
250
        l = ['0-499', '-500', '%d-' % out_of_range]
251
        ranges = 'bytes=%s' % ','.join(l)
252
        url = join_urls(self.pithos_path, self.user, cname, oname)
253
        r = self.get(url, HTTP_RANGE=ranges)
254
        self.assertEqual(r.status_code, 416)
255

    
256
    def test_get_if_match(self):
257
        cname = self.containers[0]
258
        oname, odata = self.upload_object(cname)[:-1]
259

    
260
        # perform get with If-Match
261
        url = join_urls(self.pithos_path, self.user, cname, oname)
262

    
263
        if pithos_settings.UPDATE_MD5:
264
            etag = md5_hash(odata)
265
        else:
266
            etag = merkle(odata)
267

    
268
        r = self.get(url, HTTP_IF_MATCH=etag)
269

    
270
        # assert get success
271
        self.assertEqual(r.status_code, 200)
272

    
273
        # assert response content
274
        self.assertEqual(r.content, odata)
275

    
276
    def test_get_if_match_star(self):
277
        cname = self.containers[0]
278
        oname, odata = self.upload_object(cname)[:-1]
279

    
280
        # perform get with If-Match *
281
        url = join_urls(self.pithos_path, self.user, cname, oname)
282
        r = self.get(url, HTTP_IF_MATCH='*')
283

    
284
        # assert get success
285
        self.assertEqual(r.status_code, 200)
286

    
287
        # assert response content
288
        self.assertEqual(r.content, odata)
289

    
290
    def test_get_multiple_if_match(self):
291
        cname = self.containers[0]
292
        oname, odata = self.upload_object(cname)[:-1]
293

    
294
        # perform get with If-Match
295
        url = join_urls(self.pithos_path, self.user, cname, oname)
296

    
297
        if pithos_settings.UPDATE_MD5:
298
            etag = md5_hash(odata)
299
        else:
300
            etag = merkle(odata)
301

    
302
        quoted = lambda s: '"%s"' % s
303
        r = self.get(url, HTTP_IF_MATCH=','.join(
304
            [quoted(etag), quoted(get_random_data(64))]))
305

    
306
        # assert get success
307
        self.assertEqual(r.status_code, 200)
308

    
309
        # assert response content
310
        self.assertEqual(r.content, odata)
311

    
312
    def test_if_match_precondition_failed(self):
313
        cname = self.containers[0]
314
        oname, odata = self.upload_object(cname)[:-1]
315

    
316
        # perform get with If-Match
317
        url = join_urls(self.pithos_path, self.user, cname, oname)
318
        r = self.get(url, HTTP_IF_MATCH=get_random_name())
319
        self.assertEqual(r.status_code, 412)
320

    
321
    def test_if_none_match(self):
322
        # upload object
323
        cname = self.containers[0]
324
        oname, odata = self.upload_object(cname)[:-1]
325

    
326
        if pithos_settings.UPDATE_MD5:
327
            etag = md5_hash(odata)
328
        else:
329
            etag = merkle(odata)
330

    
331
        # perform get with If-None-Match
332
        url = join_urls(self.pithos_path, self.user, cname, oname)
333
        r = self.get(url, HTTP_IF_NONE_MATCH=etag)
334

    
335
        # assert precondition_failed
336
        self.assertEqual(r.status_code, 304)
337

    
338
        # update object data
339
        r = self.append_object_data(cname, oname)[-1]
340
        self.assertTrue(etag != r['ETag'])
341

    
342
        # perform get with If-None-Match
343
        url = join_urls(self.pithos_path, self.user, cname, oname)
344
        r = self.get(url, HTTP_IF_NONE_MATCH=etag)
345

    
346
        # assert get success
347
        self.assertEqual(r.status_code, 200)
348

    
349
    def test_if_none_match_star(self):
350
        # upload object
351
        cname = self.containers[0]
352
        oname, odata = self.upload_object(cname)[:-1]
353

    
354
        # perform get with If-None-Match with star
355
        url = join_urls(self.pithos_path, self.user, cname, oname)
356
        r = self.get(url, HTTP_IF_NONE_MATCH='*')
357

    
358
        self.assertEqual(r.status_code, 304)
359

    
360
    def test_if_modified_since(self):
361
        # upload object
362
        cname = self.containers[0]
363
        oname, odata = self.upload_object(cname)[:-1]
364
        object_info = self.get_object_info(cname, oname)
365
        last_modified = object_info['Last-Modified']
366
        t1 = datetime.datetime.strptime(last_modified, DATE_FORMATS[-1])
367
        t1_formats = map(t1.strftime, DATE_FORMATS)
368

    
369
        # Check not modified since
370
        url = join_urls(self.pithos_path, self.user, cname, oname)
371
        for t in t1_formats:
372
            r = self.get(url, HTTP_IF_MODIFIED_SINCE=t)
373
            self.assertEqual(r.status_code, 304)
374

    
375
        _time.sleep(1)
376

    
377
        # update object data
378
        appended_data = self.append_object_data(cname, oname)[1]
379

    
380
        # Check modified since
381
        url = join_urls(self.pithos_path, self.user, cname, oname)
382
        for t in t1_formats:
383
            r = self.get(url, HTTP_IF_MODIFIED_SINCE=t)
384
            self.assertEqual(r.status_code, 200)
385
            self.assertEqual(r.content, odata + appended_data)
386

    
387
    def test_if_modified_since_invalid_date(self):
388
        cname = self.containers[0]
389
        oname, odata = self.upload_object(cname)[:-1]
390
        url = join_urls(self.pithos_path, self.user, cname, oname)
391
        r = self.get(url, HTTP_IF_MODIFIED_SINCE='Monday')
392
        self.assertEqual(r.status_code, 200)
393
        self.assertEqual(r.content, odata)
394

    
395
    def test_if_not_modified_since(self):
396
        cname = self.containers[0]
397
        oname, odata = self.upload_object(cname)[:-1]
398
        url = join_urls(self.pithos_path, self.user, cname, oname)
399
        object_info = self.get_object_info(cname, oname)
400
        last_modified = object_info['Last-Modified']
401
        t = datetime.datetime.strptime(last_modified, DATE_FORMATS[-1])
402

    
403
        # Check unmodified
404
        t1 = t + datetime.timedelta(seconds=1)
405
        t1_formats = map(t1.strftime, DATE_FORMATS)
406
        for t in t1_formats:
407
            r = self.get(url, HTTP_IF_UNMODIFIED_SINCE=t)
408
            self.assertEqual(r.status_code, 200)
409
            self.assertEqual(odata, odata)
410

    
411
        # modify object
412
        _time.sleep(2)
413
        self.append_object_data(cname, oname)
414

    
415
        object_info = self.get_object_info(cname, oname)
416
        last_modified = object_info['Last-Modified']
417
        t = datetime.datetime.strptime(last_modified, DATE_FORMATS[-1])
418
        t2 = t - datetime.timedelta(seconds=1)
419
        t2_formats = map(t2.strftime, DATE_FORMATS)
420

    
421
        # check modified
422
        for t in t2_formats:
423
            r = self.get(url, HTTP_IF_UNMODIFIED_SINCE=t)
424
            self.assertEqual(r.status_code, 412)
425

    
426
        # modify account: update object meta
427
        _time.sleep(1)
428
        self.update_object_meta(cname, oname, {'foo': 'bar'})
429

    
430
        object_info = self.get_object_info(cname, oname)
431
        last_modified = object_info['Last-Modified']
432
        t = datetime.datetime.strptime(last_modified, DATE_FORMATS[-1])
433
        t3 = t - datetime.timedelta(seconds=1)
434
        t3_formats = map(t3.strftime, DATE_FORMATS)
435

    
436
        # check modified
437
        for t in t3_formats:
438
            r = self.get(url, HTTP_IF_UNMODIFIED_SINCE=t)
439
            self.assertEqual(r.status_code, 412)
440

    
441
    def test_if_unmodified_since(self):
442
        cname = self.containers[0]
443
        oname, odata = self.upload_object(cname)[:-1]
444
        url = join_urls(self.pithos_path, self.user, cname, oname)
445
        object_info = self.get_object_info(cname, oname)
446
        last_modified = object_info['Last-Modified']
447
        t = datetime.datetime.strptime(last_modified, DATE_FORMATS[-1])
448
        t = t + datetime.timedelta(seconds=1)
449
        t_formats = map(t.strftime, DATE_FORMATS)
450

    
451
        for tf in t_formats:
452
            r = self.get(url, HTTP_IF_UNMODIFIED_SINCE=tf)
453
            self.assertEqual(r.status_code, 200)
454
            self.assertEqual(r.content, odata)
455

    
456
    def test_if_unmodified_since_precondition_failed(self):
457
        cname = self.containers[0]
458
        oname, odata = self.upload_object(cname)[:-1]
459
        url = join_urls(self.pithos_path, self.user, cname, oname)
460
        object_info = self.get_object_info(cname, oname)
461
        last_modified = object_info['Last-Modified']
462
        t = datetime.datetime.strptime(last_modified, DATE_FORMATS[-1])
463
        t = t - datetime.timedelta(seconds=1)
464
        t_formats = map(t.strftime, DATE_FORMATS)
465

    
466
        for tf in t_formats:
467
            r = self.get(url, HTTP_IF_UNMODIFIED_SINCE=tf)
468
            self.assertEqual(r.status_code, 412)
469

    
470
    def test_hashes(self):
471
        l = random.randint(2, 5) * pithos_settings.BACKEND_BLOCK_SIZE
472
        cname = self.containers[0]
473
        oname, odata = self.upload_object(cname, length=l)[:-1]
474
        size = len(odata)
475

    
476
        url = join_urls(self.pithos_path, self.user, cname, oname)
477
        r = self.get('%s?format=json&hashmap' % url)
478
        self.assertEqual(r.status_code, 200)
479
        body = json.loads(r.content)
480

    
481
        hashes = body['hashes']
482
        block_size = body['block_size']
483
        block_num = size / block_size if size / block_size == 0 else\
484
            size / block_size + 1
485
        self.assertTrue(len(hashes), block_num)
486
        i = 0
487
        for h in hashes:
488
            start = i * block_size
489
            end = (i + 1) * block_size
490
            hash = merkle(odata[start:end])
491
            self.assertEqual(h, hash)
492
            i += 1
493

    
494

    
495
class ObjectPut(PithosAPITest):
496
    def setUp(self):
497
        PithosAPITest.setUp(self)
498
        self.container = get_random_name()
499
        self.create_container(self.container)
500

    
501
    def test_upload(self):
502
        cname = self.container
503
        oname = get_random_name()
504
        data = get_random_data()
505
        meta = {'test': 'test1'}
506
        headers = dict(('HTTP_X_OBJECT_META_%s' % k.upper(), v)
507
                       for k, v in meta.iteritems())
508
        url = join_urls(self.pithos_path, self.user, cname, oname)
509
        r = self.put(url, data=data, content_type='application/pdf', **headers)
510
        self.assertEqual(r.status_code, 201)
511
        self.assertTrue('ETag' in r)
512
        self.assertTrue('X-Object-Version' in r)
513

    
514
        info = self.get_object_info(cname, oname)
515

    
516
        # assert object meta
517
        self.assertTrue('X-Object-Meta-Test' in info)
518
        self.assertEqual(info['X-Object-Meta-Test'], 'test1')
519

    
520
        # assert content-type
521
        self.assertEqual(info['content-type'], 'application/pdf')
522

    
523
        # assert uploaded content
524
        r = self.get(url)
525
        self.assertEqual(r.status_code, 200)
526
        self.assertEqual(r.content, data)
527

    
528
    def test_maximum_upload_size_exceeds(self):
529
        cname = self.container
530
        oname = get_random_name()
531

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
623
    def test_create_object_by_hashmap(self):
624
        cname = self.container
625
        block_size = pithos_settings.BACKEND_BLOCK_SIZE
626

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

    
633
        oname = get_random_name()
634
        url = join_urls(self.pithos_path, self.user, cname, oname)
635
        r = self.put('%s?hashmap=' % url, data=r.content)
636
        self.assertEqual(r.status_code, 201)
637

    
638
        r = self.get(url)
639
        self.assertEqual(r.status_code, 200)
640
        self.assertEqual(r.content, data)
641

    
642

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

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

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

    
665
            # assert copy success
666
            self.assertEqual(r.status_code, 201)
667

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

    
674
            # assert etag is the same
675
            self.assertTrue('X-Object-Hash' in r)
676
            self.assertEqual(r['X-Object-Hash'], self.etag)
677

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

    
689
            # assert copy success
690
            self.assertEqual(r.status_code, 201)
691

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

    
698
            # assert etag is the same
699
            self.assertTrue('X-Object-Hash' in r)
700
            self.assertEqual(r['X-Object-Hash'], self.etag)
701

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

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

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

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

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

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

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

    
761

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

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

    
774
    def test_move(self):
775
        # move object
776
        oname = get_random_name()
777
        url = join_urls(self.pithos_path, self.user, self.container, oname)
778
        r = self.put(url, data='', HTTP_X_OBJECT_META_TEST='testcopy',
779
                     HTTP_X_MOVE_FROM='/%s/%s' % (
780
                         self.container, self.object))
781

    
782
        # assert move success
783
        self.assertEqual(r.status_code, 201)
784

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

    
791
        # assert etag is the same
792
        self.assertTrue('X-Object-Hash' in r)
793

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

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

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

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

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

    
838
#            # assert metadata
839
#            for k in meta[obj].keys():
840
#                self.assertTrue(k in r)
841
#                self.assertEqual(r[k], meta[obj][k])
842

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

    
850

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

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

    
871
            # assert metadata have been updated
872
            meta = self.get_object_meta(self.container, self.object)
873

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

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

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

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

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

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

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

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

    
952
        # check modified object
953
        r = self.get(url)
954

    
955
        self.assertEqual(r.status_code, 200)
956
        self.assertEqual(r.content, updated_data)
957
        self.assertEqual(etag, r['ETag'])
958

    
959
    def test_update_object_divided_by_blocksize(self):
960
        block_size = pithos_settings.BACKEND_BLOCK_SIZE
961
        oname, odata = self.upload_object(self.container,
962
                                          length=2 * block_size)[:2]
963

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

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

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

    
986
        # check modified object
987
        r = self.get(url)
988

    
989
        self.assertEqual(r.status_code, 200)
990
        self.assertEqual(r.content, updated_data)
991
        self.assertEqual(etag, r['ETag'])
992

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

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

    
1009
        url = join_urls(self.pithos_path, self.user, self.container, oname)
1010
        r = self.post(url, data=data, **kwargs)
1011

    
1012
        self.assertEqual(r.status_code, 400)
1013

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

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

    
1027
        url = join_urls(self.pithos_path, self.user, self.container, oname)
1028
        r = self.post(url, data=get_random_data(), **kwargs)
1029

    
1030
        self.assertEqual(r.status_code, 416)
1031

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

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

    
1045
        url = join_urls(self.pithos_path, self.user, self.container, oname)
1046
        r = self.post(url, data=get_random_data(), **kwargs)
1047

    
1048
        self.assertEqual(r.status_code, 416)
1049

    
1050
    def test_append(self):
1051
        data = get_random_data()
1052
        length = len(data)
1053
        url = join_urls(self.pithos_path, self.user, self.container,
1054
                        self.object)
1055
        r = self.post(url, data=data, content_type='application/octet-stream',
1056
                      HTTP_CONTENT_LENGTH=str(length),
1057
                      HTTP_CONTENT_RANGE='bytes */*')
1058
        self.assertEqual(r.status_code, 204)
1059

    
1060
        r = self.get(url)
1061
        content = r.content
1062
        self.assertEqual(len(content), len(self.object_data) + length)
1063
        self.assertEqual(content, self.object_data + data)
1064

    
1065
    # TODO Fix the test
1066
    def _test_update_with_chunked_transfer(self):
1067
        data = get_random_data()
1068
        length = len(data)
1069

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

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

    
1083
    def test_update_from_other_object(self):
1084
        src = self.object
1085
        dest = get_random_name()
1086

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

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

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

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

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

    
1113
    def test_update_range_from_other_object(self):
1114
        src = self.object
1115
        dest = get_random_name()
1116

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

    
1121
        # update zero length object
1122
        url = join_urls(self.pithos_path, self.user, self.container, dest)
1123
        initial_data = get_random_data()
1124
        length = len(initial_data)
1125
        r = self.put(url, data=initial_data)
1126
        self.assertEqual(r.status_code, 201)
1127

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

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

    
1141

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

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

    
1155
        r = self.head(url)
1156
        self.assertEqual(r.status_code, 404)
1157

    
1158
    def test_delete_non_existent(self):
1159
        url = join_urls(self.pithos_path, self.user, self.container,
1160
                        get_random_name())
1161
        r = self.delete(url)
1162
        self.assertEqual(r.status_code, 404)
1163

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

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

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

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