aliyun-sdk 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,338 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+ require 'yaml'
5
+ require 'nokogiri'
6
+
7
+ module Aliyun
8
+ module OSS
9
+
10
+ describe Bucket do
11
+
12
+ before :all do
13
+ @endpoint = 'oss-cn-hangzhou.aliyuncs.com'
14
+ @bucket_name = 'rubysdk-bucket'
15
+ @bucket = Client.new(
16
+ :endpoint => @endpoint,
17
+ :access_key_id => 'xxx',
18
+ :access_key_secret => 'yyy').get_bucket(@bucket_name)
19
+ end
20
+
21
+ def bucket_url
22
+ "#{@bucket_name}.#{@endpoint}"
23
+ end
24
+
25
+ def object_url(object)
26
+ "#{@bucket_name}.#{@endpoint}/#{object}"
27
+ end
28
+
29
+ def resource_path(object)
30
+ "/#{@bucket_name}/#{object}"
31
+ end
32
+
33
+ def mock_objects(objects, more = {})
34
+ Nokogiri::XML::Builder.new do |xml|
35
+ xml.ListBucketResult {
36
+ {
37
+ :prefix => 'Prefix',
38
+ :delimiter => 'Delimiter',
39
+ :limit => 'MaxKeys',
40
+ :marker => 'Marker',
41
+ :next_marker => 'NextMarker',
42
+ :truncated => 'IsTruncated',
43
+ :encoding => 'EncodingType'
44
+ }.map do |k, v|
45
+ xml.send(v, more[k]) if more[k] != nil
46
+ end
47
+
48
+ objects.each do |o|
49
+ xml.Contents {
50
+ xml.Key o.key
51
+ xml.Size o.size
52
+ xml.ETag o.etag
53
+ }
54
+ end
55
+
56
+ (more[:common_prefixes] || []).each do |p|
57
+ xml.CommonPrefixes {
58
+ xml.Prefix p
59
+ }
60
+ end
61
+ }
62
+ end.to_xml
63
+ end
64
+
65
+ def mock_acl(acl)
66
+ Nokogiri::XML::Builder.new do |xml|
67
+ xml.AccessControlPolicy {
68
+ xml.Owner {
69
+ xml.ID 'owner_id'
70
+ xml.DisplayName 'owner_name'
71
+ }
72
+
73
+ xml.AccessControlList {
74
+ xml.Grant acl
75
+ }
76
+ }
77
+ end.to_xml
78
+ end
79
+
80
+ context "bucket operations" do
81
+ it "should get acl" do
82
+ query = {'acl' => ''}
83
+ return_acl = ACL::PUBLIC_READ
84
+
85
+ stub_request(:get, bucket_url)
86
+ .with(:query => query)
87
+ .to_return(:body => mock_acl(return_acl))
88
+
89
+ acl = @bucket.acl
90
+
91
+ expect(WebMock).to have_requested(:get, bucket_url)
92
+ .with(:query => query, :body => nil)
93
+ expect(acl).to eq(return_acl)
94
+ end
95
+
96
+ it "should set acl" do
97
+ query = {'acl' => ''}
98
+
99
+ stub_request(:put, bucket_url).with(:query => query)
100
+
101
+ @bucket.acl = ACL::PUBLIC_READ
102
+
103
+ expect(WebMock).to have_requested(:put, bucket_url)
104
+ .with(:query => query, :body => nil)
105
+ end
106
+
107
+ it "should delete logging setting" do
108
+ query = {'logging' => ''}
109
+
110
+ stub_request(:delete, bucket_url).with(:query => query)
111
+
112
+ @bucket.logging = BucketLogging.new(:enable => false)
113
+
114
+ expect(WebMock).to have_requested(:delete, bucket_url)
115
+ .with(:query => query, :body => nil)
116
+ end
117
+
118
+ it "should get bucket url" do
119
+ expect(@bucket.bucket_url)
120
+ .to eq('http://rubysdk-bucket.oss-cn-hangzhou.aliyuncs.com')
121
+ end
122
+
123
+ it "should get access key id" do
124
+ expect(@bucket.access_key_id).to eq('xxx')
125
+ end
126
+ end # bucket operations
127
+
128
+ context "object operations" do
129
+ it "should list objects" do
130
+ query_1 = {
131
+ :prefix => 'list-',
132
+ :delimiter => '-'
133
+ }
134
+ return_obj_1 = (1..5).map{ |i| Object.new(
135
+ :key => "obj-#{i}",
136
+ :size => 1024 * i,
137
+ :etag => "etag-#{i}")}
138
+ return_more_1 = {
139
+ :next_marker => 'foo',
140
+ :truncated => true,
141
+ :common_prefixes => ['hello', 'world']
142
+ }
143
+
144
+ query_2 = {
145
+ :prefix => 'list-',
146
+ :delimiter => '-',
147
+ :marker => 'foo'
148
+ }
149
+ return_obj_2 = (6..8).map{ |i| Object.new(
150
+ :key => "obj-#{i}",
151
+ :size => 1024 * i,
152
+ :etag => "etag-#{i}")}
153
+ return_more_2 = {
154
+ :next_marker => 'bar',
155
+ :truncated => false,
156
+ :common_prefixes => ['rock', 'suck']
157
+ }
158
+
159
+ stub_request(:get, bucket_url)
160
+ .with(:query => query_1)
161
+ .to_return(:body => mock_objects(return_obj_1, return_more_1))
162
+
163
+ stub_request(:get, bucket_url)
164
+ .with(:query => query_2)
165
+ .to_return(:body => mock_objects(return_obj_2, return_more_2))
166
+
167
+ list = @bucket.list_objects :prefix => 'list-', :delimiter => '-'
168
+ all = list.to_a
169
+
170
+ expect(WebMock).to have_requested(:get, bucket_url)
171
+ .with(:query => query_1).times(1)
172
+ expect(WebMock).to have_requested(:get, bucket_url)
173
+ .with(:query => query_2).times(1)
174
+
175
+ objs = all.select{ |x| x.is_a?(Object) }
176
+ common_prefixes = all.select{ |x| x.is_a?(String) }
177
+ all_objs = (1..8).map{ |i| Object.new(
178
+ :key => "obj-#{i}",
179
+ :size => 1024 * i,
180
+ :etag => "etag-#{i}")}
181
+ expect(objs.map{ |o| o.to_s }).to match_array(all_objs.map{ |o| o.to_s })
182
+ all_prefixes = ['hello', 'world', 'rock', 'suck']
183
+ expect(common_prefixes).to match_array(all_prefixes)
184
+ end
185
+
186
+ it "should put object from file" do
187
+ key = 'ruby'
188
+ stub_request(:put, object_url(key))
189
+
190
+ content = (1..10).map{ |i| i.to_s.rjust(9, '0') }.join("\n")
191
+ File.open('/tmp/x', 'w'){ |f| f.write(content) }
192
+
193
+ @bucket.put_object(key, :file => '/tmp/x')
194
+
195
+ expect(WebMock).to have_requested(:put, object_url(key))
196
+ .with(:body => content, :query => {})
197
+ end
198
+
199
+ it "should get object to file" do
200
+ key = 'ruby'
201
+ # 100 KB
202
+ content = (1..1024).map{ |i| i.to_s.rjust(99, '0') }.join(",")
203
+
204
+ stub_request(:get, object_url(key)).to_return(:body => content)
205
+
206
+ @bucket.get_object(key, :file => '/tmp/x')
207
+
208
+ expect(WebMock).to have_requested(:get, object_url(key))
209
+ .with(:body => nil, :query => {})
210
+ expect(File.read('/tmp/x')).to eq(content)
211
+ end
212
+
213
+ it "should only get meta when get object without :file or block" do
214
+ key = 'ruby'
215
+
216
+ last_modified = Time.now.rfc822
217
+ return_headers = {
218
+ 'x-oss-object-type' => 'Normal',
219
+ 'ETag' => 'xxxyyyzzz',
220
+ 'Content-Length' => 1024,
221
+ 'Last-Modified' => last_modified,
222
+ 'x-oss-meta-year' => '2015',
223
+ 'x-oss-meta-people' => 'mary'
224
+ }
225
+ stub_request(:head, object_url(key))
226
+ .to_return(:headers => return_headers)
227
+
228
+ obj = @bucket.get_object(key)
229
+
230
+ expect(WebMock).to have_requested(:head, object_url(key))
231
+ .with(:body => nil, :query => {})
232
+
233
+ expect(obj.key).to eq(key)
234
+ expect(obj.type).to eq('Normal')
235
+ expect(obj.etag).to eq('xxxyyyzzz')
236
+ expect(obj.size).to eq(1024)
237
+ expect(obj.last_modified.rfc822).to eq(last_modified)
238
+ expect(obj.metas).to eq({'year' => '2015', 'people' => 'mary'})
239
+ end
240
+
241
+ it "should append object from file" do
242
+ key = 'ruby'
243
+ query = {'append' => '', 'position' => 11}
244
+ stub_request(:post, object_url(key)).with(:query => query)
245
+
246
+ content = (1..10).map{ |i| i.to_s.rjust(9, '0') }.join("\n")
247
+ content = "<html>" + content + "</html>"
248
+ File.open('/tmp/x.html', 'w'){ |f| f.write(content) }
249
+
250
+ @bucket.append_object(key, 11, :file => '/tmp/x.html')
251
+
252
+ expect(WebMock).to have_requested(:post, object_url(key))
253
+ .with(:query => query, :body => content,
254
+ :headers => {'Content-Type' => 'text/html'})
255
+ end
256
+
257
+ it "should answer object exists?" do
258
+ key = 'ruby'
259
+
260
+ stub_request(:head, object_url(key))
261
+ .to_return(:status => 404).times(3)
262
+
263
+ expect {
264
+ @bucket.get_object(key)
265
+ }.to raise_error(Exception, "UnknownError, HTTP Code: 404")
266
+
267
+ expect(@bucket.object_exists?(key)).to be false
268
+ expect(@bucket.object_exist?(key)).to be false
269
+
270
+ last_modified = Time.now.rfc822
271
+ return_headers = {
272
+ 'x-oss-object-type' => 'Normal',
273
+ 'ETag' => 'xxxyyyzzz',
274
+ 'Content-Length' => 1024,
275
+ 'Last-Modified' => last_modified,
276
+ 'x-oss-meta-year' => '2015',
277
+ 'x-oss-meta-people' => 'mary'
278
+ }
279
+
280
+ stub_request(:head, object_url(key))
281
+ .to_return(:headers => return_headers).times(2)
282
+
283
+ expect(@bucket.object_exists?(key)).to be true
284
+ expect(@bucket.object_exist?(key)).to be true
285
+
286
+ stub_request(:head, object_url(key))
287
+ .to_return(:status => 500)
288
+
289
+ expect {
290
+ @bucket.object_exists?(key)
291
+ }.to raise_error(Exception, "UnknownError, HTTP Code: 500")
292
+ end
293
+
294
+ it "should update object metas" do
295
+ key = 'ruby'
296
+
297
+ stub_request(:put, object_url(key))
298
+
299
+ @bucket.update_object_metas(
300
+ key, {'people' => 'mary', 'year' => '2016'})
301
+
302
+ expect(WebMock).to have_requested(:put, object_url(key))
303
+ .with(:body => nil,
304
+ :headers => {
305
+ 'x-oss-copy-source' => resource_path(key),
306
+ 'x-oss-metadata-directive' => 'REPLACE',
307
+ 'x-oss-meta-year' => '2016',
308
+ 'x-oss-meta-people' => 'mary'})
309
+ end
310
+
311
+ it "should get object url" do
312
+ url = @bucket.object_url('yeah', false)
313
+ object_url = 'http://rubysdk-bucket.oss-cn-hangzhou.aliyuncs.com/yeah'
314
+ expect(url).to eq(object_url)
315
+
316
+ url = @bucket.object_url('yeah')
317
+ path = url[0, url.index('?')]
318
+ expect(path).to eq(object_url)
319
+
320
+ query = {}
321
+ url[url.index('?') + 1, url.size].split('&')
322
+ .each { |s| k, v = s.split('='); query[k] = v }
323
+
324
+ expect(query.key?('Expires')).to be true
325
+ expect(query['OSSAccessKeyId']).to eq('xxx')
326
+ expires = query['Expires']
327
+ signature = CGI.unescape(query['Signature'])
328
+
329
+ string_to_sign =
330
+ "GET\n" + "\n\n" + "#{expires}\n" + "/rubysdk-bucket/yeah"
331
+ sig = Util.sign('yyy', string_to_sign)
332
+ expect(signature).to eq(sig)
333
+ end
334
+ end # object operations
335
+
336
+ end # Bucket
337
+ end # OSS
338
+ end # Aliyun
@@ -0,0 +1,228 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+ require 'yaml'
5
+ require 'nokogiri'
6
+
7
+ module Aliyun
8
+ module OSS
9
+
10
+ describe Client do
11
+
12
+ context "construct" do
13
+ it "should setup endpoint and a/k", :focus => true do
14
+ endpoint = 'oss-cn-hangzhou.aliyuncs.com'
15
+ client = Client.new(
16
+ :endpoint => endpoint,
17
+ :access_key_id => 'xxx', :access_key_secret => 'yyy')
18
+
19
+ config = client.instance_variable_get('@config')
20
+ expect(config.endpoint.to_s).to eq("http://#{endpoint}")
21
+ expect(config.access_key_id).to eq('xxx')
22
+ expect(config.access_key_secret).to eq('yyy')
23
+ end
24
+
25
+ it "should not set Authorization with anonymous client" do
26
+ endpoint = 'oss-cn-hangzhou.aliyuncs.com'
27
+ bucket = 'rubysdk-bucket'
28
+ object = 'rubysdk-object'
29
+ client = Client.new(:endpoint => endpoint)
30
+
31
+ stub_request(:get, "#{bucket}.#{endpoint}/#{object}")
32
+
33
+ client.get_bucket(bucket).get_object(object) {}
34
+
35
+ expect(WebMock)
36
+ .to have_requested(:get, "#{bucket}.#{endpoint}/#{object}")
37
+ .with{ |req| not req.headers.has_key?('Authorization') }
38
+ end
39
+
40
+ it "should construct different client" do
41
+ bucket = 'rubysdk-bucket'
42
+ object = 'rubysdk-object'
43
+ ep1 = 'oss-cn-hangzhou.aliyuncs.com'
44
+ c1 = Client.new(
45
+ :endpoint => ep1,
46
+ :access_key_id => 'xxx', :access_key_secret => 'yyy')
47
+ ep2 = 'oss-cn-beijing.aliyuncs.com'
48
+ c2 = Client.new(
49
+ :endpoint => ep2,
50
+ :access_key_id => 'aaa', :access_key_secret => 'bbb')
51
+
52
+ stub_request(:get, "#{bucket}.#{ep1}/#{object}")
53
+ stub_request(:put, "#{bucket}.#{ep2}/#{object}")
54
+
55
+ c1.get_bucket(bucket).get_object(object) {}
56
+ c2.get_bucket(bucket).put_object(object)
57
+
58
+ expect(WebMock).to have_requested(:get, "#{bucket}.#{ep1}/#{object}")
59
+ expect(WebMock).to have_requested(:put, "#{bucket}.#{ep2}/#{object}")
60
+ end
61
+ end # construct
62
+
63
+ def mock_buckets(buckets, more = {})
64
+ Nokogiri::XML::Builder.new do |xml|
65
+ xml.ListAllMyBucketsResult {
66
+ xml.Owner {
67
+ xml.ID 'owner_id'
68
+ xml.DisplayName 'owner_name'
69
+ }
70
+ xml.Buckets {
71
+ buckets.each do |b|
72
+ xml.Bucket {
73
+ xml.Location b.location
74
+ xml.Name b.name
75
+ xml.CreationDate b.creation_time.to_s
76
+ }
77
+ end
78
+ }
79
+
80
+ unless more.empty?
81
+ xml.Prefix more[:prefix]
82
+ xml.Marker more[:marker]
83
+ xml.MaxKeys more[:limit].to_s
84
+ xml.NextMarker more[:next_marker]
85
+ xml.IsTruncated more[:truncated]
86
+ end
87
+ }
88
+ end.to_xml
89
+ end
90
+
91
+ def mock_location(location)
92
+ Nokogiri::XML::Builder.new do |xml|
93
+ xml.CreateBucketConfiguration {
94
+ xml.LocationConstraint location
95
+ }
96
+ end.to_xml
97
+ end
98
+
99
+ def mock_acl(acl)
100
+ Nokogiri::XML::Builder.new do |xml|
101
+ xml.AccessControlPolicy {
102
+ xml.Owner {
103
+ xml.ID 'owner_id'
104
+ xml.DisplayName 'owner_name'
105
+ }
106
+
107
+ xml.AccessControlList {
108
+ xml.Grant acl
109
+ }
110
+ }
111
+ end.to_xml
112
+ end
113
+
114
+ context "bucket operations" do
115
+ before :all do
116
+ @endpoint = 'oss.aliyuncs.com'
117
+ @client = Client.new(
118
+ :endpoint => @endpoint,
119
+ :access_key_id => 'xxx',
120
+ :access_key_secret => 'yyy')
121
+ @bucket = 'rubysdk-bucket'
122
+ end
123
+
124
+ def bucket_url
125
+ @bucket + "." + @endpoint
126
+ end
127
+
128
+ it "should create bucket" do
129
+ location = 'oss-cn-hangzhou'
130
+
131
+ stub_request(:put, bucket_url).with(:body => mock_location(location))
132
+
133
+ @client.create_bucket(@bucket, :location => 'oss-cn-hangzhou')
134
+
135
+ expect(WebMock).to have_requested(:put, bucket_url)
136
+ .with(:body => mock_location(location), :query => {})
137
+ end
138
+
139
+ it "should delete bucket" do
140
+ stub_request(:delete, bucket_url)
141
+
142
+ @client.delete_bucket(@bucket)
143
+
144
+ expect(WebMock).to have_requested(:delete, bucket_url)
145
+ .with(:body => nil, :query => {})
146
+ end
147
+
148
+ it "should paging list buckets" do
149
+ return_buckets_1 = (1..5).map do |i|
150
+ name = "rubysdk-bucket-#{i.to_s.rjust(3, '0')}"
151
+ Bucket.new(
152
+ :name => name,
153
+ :location => 'oss-cn-hangzhou',
154
+ :creation_time => Time.now)
155
+ end
156
+
157
+ more_1 = {:next_marker => return_buckets_1.last.name, :truncated => true}
158
+
159
+ return_buckets_2 = (6..10).map do |i|
160
+ name = "rubysdk-bucket-#{i.to_s.rjust(3, '0')}"
161
+ Bucket.new(
162
+ :name => name,
163
+ :location => 'oss-cn-hangzhou',
164
+ :creation_time => Time.now)
165
+ end
166
+
167
+ more_2 = {:truncated => false}
168
+
169
+ stub_request(:get, /#{@endpoint}.*/)
170
+ .to_return(:body => mock_buckets(return_buckets_1, more_1)).then
171
+ .to_return(:body => mock_buckets(return_buckets_2, more_2))
172
+
173
+ buckets = @client.list_buckets
174
+
175
+ expect(buckets.map {|b| b.to_s}.join(";"))
176
+ .to eq((return_buckets_1 + return_buckets_2).map {|b| b.to_s}.join(";"))
177
+ expect(WebMock).to have_requested(:get, /#{@endpoint}.*/).times(2)
178
+ end
179
+
180
+ it "should test bucket existence" do
181
+ query = {'acl' => ''}
182
+ return_acl = ACL::PUBLIC_READ
183
+ stub_request(:get, bucket_url)
184
+ .with(:query => query)
185
+ .to_return(:body => mock_acl(return_acl)).then
186
+ .to_return(:status => 404)
187
+
188
+ exist = @client.bucket_exists?(@bucket)
189
+ expect(exist).to be true
190
+
191
+ exist = @client.bucket_exists?(@bucket)
192
+ expect(exist).to be false
193
+
194
+ expect(WebMock).to have_requested(:get, bucket_url)
195
+ .with(:query => query, :body => nil).times(2)
196
+ end
197
+
198
+ it "should not list buckets when endpoint is cname" do
199
+ cname_client = Client.new(
200
+ :endpoint => @endpoint,
201
+ :access_key_id => 'xxx',
202
+ :access_key_secret => 'yyy',
203
+ :cname => true)
204
+
205
+ expect {
206
+ cname_client.list_buckets
207
+ }.to raise_error(ClientError)
208
+ end
209
+
210
+ it "should use HTTPS" do
211
+ stub_request(:put, "https://#{bucket_url}")
212
+
213
+ https_client = Client.new(
214
+ :endpoint => "https://#{@endpoint}",
215
+ :access_key_id => 'xxx',
216
+ :access_key_secret => 'yyy',
217
+ :cname => false)
218
+
219
+ https_client.create_bucket(@bucket)
220
+
221
+ expect(WebMock).to have_requested(:put, "https://#{bucket_url}")
222
+ end
223
+ end # bucket operations
224
+
225
+ end # Client
226
+
227
+ end # OSS
228
+ end # Aliyun