att-swift 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,564 @@
1
+ require 'minitest/autorun'
2
+ require 'att/swift'
3
+
4
+ class TestATTSwift < MiniTest::Unit::TestCase
5
+
6
+ def setup
7
+ @auth_uri = URI 'http://example/auth/'
8
+ @store_uri = URI 'http://storage.example/'
9
+
10
+ @swift = ATT::Swift.new @auth_uri, 'username', 'key'
11
+ @swift.http = @http = FakeHTTP.new
12
+
13
+ @http.expected_response(200, nil,
14
+ 'X-Auth-Token' => 'token',
15
+ 'X-Storage-Url' => 'http://storage.example/')
16
+ end
17
+
18
+ def test_initialize
19
+ swift = ATT::Swift.new @auth_uri.to_s, 'username', 'key'
20
+ swift.http = @http
21
+
22
+ swift.authenticate
23
+
24
+ assert_equal 'token', swift.auth_token
25
+ end
26
+
27
+ def test_authenticate
28
+ @swift.authenticate
29
+
30
+ assert_equal 'token', @swift.auth_token
31
+ assert_equal URI('http://storage.example/'), @swift.storage_uri
32
+ assert_equal 'token', @http.override_headers['X-Auth-Token']
33
+
34
+ uri, req = @http.requests.shift
35
+
36
+ assert_equal @auth_uri + 'v1.0', uri
37
+
38
+ assert_equal '/auth/v1.0', req.path
39
+ assert_equal %w[username], req.to_hash['x-auth-user']
40
+ assert_equal %w[key], req.to_hash['x-auth-key']
41
+
42
+ @swift.authenticate
43
+
44
+ assert_empty @http.requests, 'authenticate did not cache the auth token'
45
+ end
46
+
47
+ def test_chunk_objects
48
+ expected_body_1 = [
49
+ { 'name' => 'hello', 'hash' => '5d41402abc4b2a76b9719d911017c592',
50
+ 'bytes' => 5 }
51
+ ]
52
+
53
+ expected_body_2 = [
54
+ { 'name' => 'world', 'hash' => '7d793037a0760186574b0282f2f435e7',
55
+ 'bytes' => 5 },
56
+ ]
57
+
58
+ @http.expected_response 200, JSON.dump(expected_body_1)
59
+ @http.expected_response 200, JSON.dump(expected_body_2)
60
+ @http.expected_response 200, JSON.dump([])
61
+
62
+ chunks = []
63
+
64
+ @swift.chunk_objects 'container', nil, 1 do |objects|
65
+ chunks << objects
66
+ end
67
+
68
+ assert_equal [expected_body_1, expected_body_2], chunks
69
+
70
+ @http.requests.shift # auth
71
+
72
+ uri, = @http.requests.shift
73
+ assert_equal @store_uri + 'container?format=json&limit=1', uri
74
+
75
+ uri, = @http.requests.shift
76
+ assert_equal @store_uri + 'container?format=json&marker=hello&limit=1', uri
77
+
78
+ uri, = @http.requests.shift
79
+ assert_equal @store_uri + 'container?format=json&marker=world&limit=1', uri
80
+ end
81
+
82
+ def test_containers
83
+ expected_body = [
84
+ { 'name' => 'a', 'count' => 12, 'bytes' => 345 },
85
+ ]
86
+
87
+ @http.expected_response 200, JSON.dump(expected_body)
88
+
89
+ containers = @swift.containers
90
+
91
+ assert_equal expected_body, containers
92
+
93
+ @http.requests.shift # auth
94
+
95
+ uri, = @http.requests.shift
96
+
97
+ assert_equal @store_uri + '?format=json', uri
98
+ end
99
+
100
+ def test_containers_limit
101
+ expected_body = [
102
+ { 'name' => 'b', 'count' => 12, 'bytes' => 345 },
103
+ ]
104
+
105
+ @http.expected_response 200, JSON.dump(expected_body)
106
+
107
+ @swift.containers nil, 2
108
+
109
+ @http.requests.shift # auth
110
+
111
+ uri, = @http.requests.shift
112
+
113
+ assert_equal @store_uri + '?format=json&limit=2', uri
114
+ end
115
+
116
+ def test_containers_marker
117
+ expected_body = [
118
+ { 'name' => 'b', 'count' => 12, 'bytes' => 345 },
119
+ ]
120
+
121
+ @http.expected_response 200, JSON.dump(expected_body)
122
+
123
+ @swift.containers 'a'
124
+
125
+ @http.requests.shift # auth
126
+
127
+ uri, = @http.requests.shift
128
+
129
+ assert_equal @store_uri + '?format=json&marker=a', uri
130
+ end
131
+
132
+ def test_copy_object
133
+ @http.expected_response 201
134
+
135
+ assert @swift.copy_object('src_container', 'src_object',
136
+ 'dst_container', 'dst_object')
137
+
138
+ @http.requests.shift # auth
139
+
140
+ uri, req = @http.requests.shift
141
+
142
+ assert_equal @store_uri + 'dst_container/dst_object', uri
143
+
144
+ assert_kind_of Net::HTTP::Put, req
145
+ assert_equal '/src_container/src_object', req['x-copy-from']
146
+ end
147
+
148
+ def test_create_container
149
+ @http.expected_response 201
150
+
151
+ assert @swift.create_container 'test'
152
+
153
+ @http.requests.shift # auth
154
+
155
+ uri, req = @http.requests.shift
156
+
157
+ assert_equal @store_uri + 'test', uri
158
+
159
+ assert_kind_of Net::HTTP::Put, req
160
+ end
161
+
162
+ def test_create_container_exists
163
+ @http.expected_response 200
164
+
165
+ refute @swift.create_container 'test'
166
+
167
+ @http.requests.shift # auth
168
+
169
+ uri, req = @http.requests.shift
170
+
171
+ assert_equal @store_uri + 'test', uri
172
+
173
+ assert_kind_of Net::HTTP::Put, req
174
+ end
175
+
176
+ def test_delete_container
177
+ @http.expected_response 204
178
+
179
+ assert @swift.delete_container 'test'
180
+
181
+ @http.requests.shift # auth
182
+
183
+ uri, req = @http.requests.shift
184
+
185
+ assert_equal @store_uri + 'test', uri
186
+
187
+ assert_kind_of Net::HTTP::Delete, req
188
+ end
189
+
190
+ def test_delete_container_nonexistent
191
+ @http.expected_response 404
192
+
193
+ refute @swift.delete_container 'test'
194
+
195
+ @http.requests.shift # auth
196
+
197
+ uri, req = @http.requests.shift
198
+
199
+ assert_equal @store_uri + 'test', uri
200
+
201
+ assert_kind_of Net::HTTP::Delete, req
202
+ end
203
+
204
+ def test_delete_container_not_empty
205
+ @http.expected_response 409
206
+
207
+ e = assert_raises ATT::Swift::Error do
208
+ @swift.delete_container 'test'
209
+ end
210
+
211
+ assert_equal 'container test is not empty - 409', e.message
212
+
213
+ @http.requests.shift # auth
214
+
215
+ uri, req = @http.requests.shift
216
+
217
+ assert_equal @store_uri + 'test', uri
218
+
219
+ assert_kind_of Net::HTTP::Delete, req
220
+ end
221
+
222
+ def test_delete_object
223
+ @http.expected_response 204
224
+
225
+ assert @swift.delete_object 'container', 'object'
226
+
227
+ @http.requests.shift # auth
228
+
229
+ uri, req = @http.requests.shift
230
+
231
+ assert_equal @store_uri + 'container/object', uri
232
+
233
+ assert_kind_of Net::HTTP::Delete, req
234
+ end
235
+
236
+ def test_delete_object_nonexistent
237
+ @http.expected_response 404
238
+
239
+ refute @swift.delete_object 'container', 'object'
240
+
241
+ @http.requests.shift # auth
242
+
243
+ uri, req = @http.requests.shift
244
+
245
+ assert_equal @store_uri + 'container/object', uri
246
+
247
+ assert_kind_of Net::HTTP::Delete, req
248
+ end
249
+
250
+ def test_objects
251
+ expected_body = [
252
+ { 'name' => 'hello', 'hash' => '5d41402abc4b2a76b9719d911017c592',
253
+ 'bytes' => 5 },
254
+ { 'name' => 'world', 'hash' => '7d793037a0760186574b0282f2f435e7',
255
+ 'bytes' => 5 },
256
+ ]
257
+
258
+ @http.expected_response 200, JSON.dump(expected_body)
259
+
260
+ objects = @swift.objects 'container'
261
+
262
+ assert_equal expected_body, objects
263
+
264
+ @http.requests.shift # auth
265
+
266
+ uri, = @http.requests.shift
267
+
268
+ assert_equal @store_uri + 'container?format=json', uri
269
+ end
270
+
271
+ def test_objects_limit
272
+ expected_body = [
273
+ { 'name' => 'hello', 'hash' => '5d41402abc4b2a76b9719d911017c592',
274
+ 'bytes' => 5 },
275
+ ]
276
+
277
+ @http.expected_response 200, JSON.dump(expected_body)
278
+
279
+ objects = @swift.objects 'container', nil, 2
280
+
281
+ assert_equal expected_body, objects
282
+
283
+ @http.requests.shift # auth
284
+
285
+ uri, = @http.requests.shift
286
+
287
+ assert_equal @store_uri + 'container?format=json&limit=2', uri
288
+ end
289
+
290
+ def test_objects_marker
291
+ expected_body = [
292
+ { 'name' => 'hello', 'hash' => '5d41402abc4b2a76b9719d911017c592',
293
+ 'bytes' => 5 },
294
+ ]
295
+
296
+ @http.expected_response 200, JSON.dump(expected_body)
297
+
298
+ objects = @swift.objects 'container', 'h'
299
+
300
+ assert_equal expected_body, objects
301
+
302
+ @http.requests.shift # auth
303
+
304
+ uri, = @http.requests.shift
305
+
306
+ assert_equal @store_uri + 'container?format=json&marker=h', uri
307
+ end
308
+
309
+ def test_object_info
310
+ @http.expected_response(200, nil,
311
+ 'Content-Length' => '3072',
312
+ 'Content-Type' => 'application/octet-stream',
313
+ 'ETag' => 'a2a5648d09a83c6a85ddd62e4a22309c',
314
+ 'Last-Modified' => 'Wed, 22 Aug 2012 22:51:28 GMT',
315
+ 'X-Object-Meta-Meat' => 'Bacon')
316
+
317
+ info = @swift.object_info 'container', 'object'
318
+
319
+ expected = {
320
+ 'metadata' => {
321
+ 'meat' => 'Bacon',
322
+ },
323
+
324
+ 'content-length' => '3072',
325
+ 'content-type' => 'application/octet-stream',
326
+ 'etag' => 'a2a5648d09a83c6a85ddd62e4a22309c',
327
+ 'last-modified' => 'Wed, 22 Aug 2012 22:51:28 GMT',
328
+ }
329
+
330
+ assert_equal expected, info
331
+
332
+ @http.requests.shift # auth
333
+
334
+ uri, req = @http.requests.shift
335
+
336
+ assert_equal @store_uri + 'container/object', uri
337
+ assert_kind_of Net::HTTP::Head, req
338
+ end
339
+
340
+ def test_object_info_missing
341
+ @http.expected_response 404
342
+
343
+ info = @swift.object_info 'container', 'object'
344
+
345
+ assert_nil info
346
+
347
+ @http.requests.shift # auth
348
+
349
+ uri, req = @http.requests.shift
350
+
351
+ assert_equal @store_uri + 'container/object', uri
352
+ assert_kind_of Net::HTTP::Head, req
353
+ end
354
+
355
+ def test_object_metadata
356
+ @http.expected_response(200, nil,
357
+ 'Content-Length' => '3072',
358
+ 'Content-Type' => 'application/octet-stream',
359
+ 'ETag' => 'a2a5648d09a83c6a85ddd62e4a22309c',
360
+ 'Last-Modified' => 'Wed, 22 Aug 2012 22:51:28 GMT',
361
+ 'X-Object-Meta-Meat' => 'Bacon')
362
+
363
+ metadata = @swift.object_metadata 'container', 'object'
364
+
365
+ expected = {
366
+ 'meat' => 'Bacon',
367
+ }
368
+
369
+ assert_equal expected, metadata
370
+
371
+ @http.requests.shift # auth
372
+
373
+ uri, req = @http.requests.shift
374
+
375
+ assert_equal @store_uri + 'container/object', uri
376
+ assert_kind_of Net::HTTP::Head, req
377
+ end
378
+
379
+ def test_object_metadata_missing
380
+ @http.expected_response 404
381
+
382
+ metadata = @swift.object_metadata 'container', 'object'
383
+
384
+ assert_nil metadata
385
+
386
+ @http.requests.shift # auth
387
+
388
+ uri, req = @http.requests.shift
389
+
390
+ assert_equal @store_uri + 'container/object', uri
391
+ assert_kind_of Net::HTTP::Head, req
392
+ end
393
+
394
+ def test_paginate_objects
395
+ expected_body_1 = [
396
+ { 'name' => 'hello', 'hash' => '5d41402abc4b2a76b9719d911017c592',
397
+ 'bytes' => 5 }
398
+ ]
399
+
400
+ expected_body_2 = [
401
+ { 'name' => 'world', 'hash' => '7d793037a0760186574b0282f2f435e7',
402
+ 'bytes' => 5 },
403
+ ]
404
+
405
+ @http.expected_response 200, JSON.dump(expected_body_1)
406
+ @http.expected_response 200, JSON.dump(expected_body_2)
407
+ @http.expected_response 200, JSON.dump([])
408
+
409
+ paginated = []
410
+
411
+ @swift.paginate_objects 'container', nil, 1 do |object|
412
+ paginated << object
413
+ end
414
+
415
+ assert_equal [expected_body_1, expected_body_2].flatten, paginated
416
+
417
+ @http.requests.shift # auth
418
+
419
+ uri, = @http.requests.shift
420
+ assert_equal @store_uri + 'container?format=json&limit=1', uri
421
+
422
+ uri, = @http.requests.shift
423
+ assert_equal @store_uri + 'container?format=json&marker=hello&limit=1', uri
424
+
425
+ uri, = @http.requests.shift
426
+ assert_equal @store_uri + 'container?format=json&marker=world&limit=1', uri
427
+ end
428
+
429
+ def test_read_object
430
+ @http.expected_response(200, 'hello',
431
+ 'ETag' => Digest::MD5.hexdigest('hello'))
432
+
433
+ object = @swift.read_object 'container', 'object'
434
+
435
+ assert_equal 'hello', object
436
+
437
+ @http.requests.shift # auth
438
+
439
+ uri, = @http.requests.shift
440
+
441
+ assert_equal @store_uri + 'container/object', uri
442
+ end
443
+
444
+ def test_read_object_not_found
445
+ @http.expected_response 404
446
+
447
+ e = assert_raises ATT::Swift::Error do
448
+ @swift.read_object 'container', 'object'
449
+ end
450
+
451
+ assert_equal 'object object in container not found - 404', e.message
452
+
453
+ @http.requests.shift # auth
454
+
455
+ uri, = @http.requests.shift
456
+
457
+ assert_equal @store_uri + 'container/object', uri
458
+ end
459
+
460
+ def test_set_object_metadata
461
+ @http.expected_response 202
462
+
463
+ @swift.set_object_metadata 'container', 'object', 'meat' => 'bacon'
464
+
465
+ @http.requests.shift # auth
466
+
467
+ uri, req = @http.requests.shift
468
+
469
+ assert_equal @store_uri + 'container/object', uri
470
+ assert_kind_of Net::HTTP::Post, req
471
+
472
+ expected = {
473
+ 'x-object-meta-meat' => %w[bacon]
474
+ }
475
+
476
+ assert_equal expected,
477
+ req.to_hash.select { |key,| key =~ /^x-object-meta-/ }
478
+ end
479
+
480
+ def test_write_object
481
+ digest = Digest::MD5.hexdigest 'hello'
482
+ @http.expected_response 201, nil, 'ETag' => digest
483
+
484
+ md5 = @swift.write_object 'container', 'object', 'hello'
485
+
486
+ assert_equal digest, md5
487
+
488
+ @http.requests.shift # auth
489
+
490
+ uri, req = @http.requests.shift
491
+
492
+ assert_equal @store_uri + 'container/object', uri
493
+ assert_kind_of Net::HTTP::Put, req
494
+ assert_equal 'hello', req.body
495
+ end
496
+
497
+ def test_write_object_block
498
+ digest = Digest::MD5.hexdigest 'hello'
499
+ @http.expected_response 201, nil, 'ETag' => digest
500
+
501
+ md5 = @swift.write_object 'container', 'object' do |io|
502
+ io.write 'hello'
503
+ end
504
+
505
+ assert_equal digest, md5
506
+
507
+ @http.requests.shift # auth
508
+
509
+ uri, req = @http.requests.shift
510
+
511
+ assert_equal @store_uri + 'container/object', uri
512
+ assert_kind_of Net::HTTP::Put, req
513
+ assert_kind_of IO, req.body_stream
514
+ end
515
+
516
+ class FakeHTTP
517
+ attr_accessor :requests, :responses, :override_headers
518
+
519
+ def initialize
520
+ @requests = []
521
+ @responses = []
522
+
523
+ @override_headers = {}
524
+ end
525
+
526
+ def debug_output= ignored
527
+ end
528
+
529
+ def expected_response code, body = nil, headers = {}
530
+ code = code.to_s
531
+ klass = Net::HTTPResponse::CODE_TO_OBJ[code]
532
+
533
+ response = klass.new '1.1', code, ''
534
+ response.initialize_http_header headers
535
+
536
+ @override_headers.each do |name, value|
537
+ response[name] = value
538
+ end
539
+
540
+ if body then
541
+ response.instance_variable_set :@read, true
542
+ response.instance_variable_set :@body, body
543
+ end
544
+
545
+ @responses << response
546
+ end
547
+
548
+ def request uri, req = nil
549
+ @requests << [uri, req]
550
+
551
+ raise "response list empty for #{uri}" if @responses.empty?
552
+
553
+ response = @responses.shift
554
+
555
+ if block_given? then
556
+ yield response
557
+ else
558
+ response
559
+ end
560
+ end
561
+ end
562
+
563
+ end
564
+