att-swift 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+