aliyun-oss 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/COPYING +19 -0
  2. data/INSTALL +35 -0
  3. data/README +443 -0
  4. data/Rakefile +333 -0
  5. data/bin/oss +6 -0
  6. data/bin/setup.rb +11 -0
  7. data/lib/aliyun/oss.rb +55 -0
  8. data/lib/aliyun/oss/acl.rb +132 -0
  9. data/lib/aliyun/oss/authentication.rb +222 -0
  10. data/lib/aliyun/oss/base.rb +241 -0
  11. data/lib/aliyun/oss/bucket.rb +320 -0
  12. data/lib/aliyun/oss/connection.rb +279 -0
  13. data/lib/aliyun/oss/error.rb +70 -0
  14. data/lib/aliyun/oss/exceptions.rb +134 -0
  15. data/lib/aliyun/oss/extensions.rb +364 -0
  16. data/lib/aliyun/oss/logging.rb +304 -0
  17. data/lib/aliyun/oss/object.rb +612 -0
  18. data/lib/aliyun/oss/owner.rb +45 -0
  19. data/lib/aliyun/oss/parsing.rb +100 -0
  20. data/lib/aliyun/oss/response.rb +181 -0
  21. data/lib/aliyun/oss/service.rb +52 -0
  22. data/lib/aliyun/oss/version.rb +13 -0
  23. data/support/faster-xml-simple/lib/faster_xml_simple.rb +188 -0
  24. data/support/faster-xml-simple/test/regression_test.rb +48 -0
  25. data/support/faster-xml-simple/test/test_helper.rb +18 -0
  26. data/support/faster-xml-simple/test/xml_simple_comparison_test.rb +47 -0
  27. data/support/rdoc/code_info.rb +212 -0
  28. data/test/acl_test.rb +70 -0
  29. data/test/authentication_test.rb +114 -0
  30. data/test/base_test.rb +137 -0
  31. data/test/bucket_test.rb +75 -0
  32. data/test/connection_test.rb +218 -0
  33. data/test/error_test.rb +71 -0
  34. data/test/extensions_test.rb +346 -0
  35. data/test/fixtures.rb +90 -0
  36. data/test/fixtures/buckets.yml +133 -0
  37. data/test/fixtures/errors.yml +34 -0
  38. data/test/fixtures/headers.yml +3 -0
  39. data/test/fixtures/logging.yml +15 -0
  40. data/test/fixtures/loglines.yml +5 -0
  41. data/test/fixtures/logs.yml +7 -0
  42. data/test/fixtures/policies.yml +16 -0
  43. data/test/logging_test.rb +90 -0
  44. data/test/mocks/fake_response.rb +27 -0
  45. data/test/object_test.rb +221 -0
  46. data/test/parsing_test.rb +67 -0
  47. data/test/remote/acl_test.rb +28 -0
  48. data/test/remote/bucket_test.rb +147 -0
  49. data/test/remote/logging_test.rb +86 -0
  50. data/test/remote/object_test.rb +350 -0
  51. data/test/remote/test_file.data +0 -0
  52. data/test/remote/test_helper.rb +34 -0
  53. data/test/response_test.rb +69 -0
  54. data/test/service_test.rb +24 -0
  55. data/test/test_helper.rb +110 -0
  56. metadata +185 -0
data/test/base_test.rb ADDED
@@ -0,0 +1,137 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require File.dirname(__FILE__) + '/test_helper'
3
+
4
+ class BaseTest < Test::Unit::TestCase
5
+ def test_connection_established
6
+ assert_raises(NoConnectionEstablished) do
7
+ Base.connection
8
+ end
9
+
10
+ Base.establish_connection!(:access_key_id => '123', :secret_access_key => 'abc')
11
+ assert_kind_of Connection, Base.connection
12
+
13
+ instance = Base.new
14
+ assert_equal instance.send(:connection), Base.connection
15
+ assert_equal instance.send(:http), Base.connection.http
16
+ end
17
+
18
+ def test_respond_with
19
+ assert_equal Base::Response, Base.send(:response_class)
20
+ Base.send(:respond_with, Bucket::Response) do
21
+ assert_equal Bucket::Response, Base.send(:response_class)
22
+ end
23
+ assert_equal Base::Response, Base.send(:response_class)
24
+ end
25
+
26
+ def test_request_tries_again_when_encountering_an_internal_error
27
+ mock_connection_for(Bucket, :returns => [
28
+ # First request is an internal error
29
+ {:body => Fixtures::Errors.internal_error, :code => 500, :error => true},
30
+ # Second request is a success
31
+ {:body => Fixtures::Buckets.empty_bucket, :code => 200}
32
+ ])
33
+ bucket = nil # Block scope hack
34
+ assert_nothing_raised do
35
+ bucket = Bucket.find('marcel')
36
+ end
37
+ # Don't call objects 'cause we don't want to make another request
38
+ assert bucket.object_cache.empty?
39
+ end
40
+
41
+ def test_request_tries_up_to_three_times
42
+ mock_connection_for(Bucket, :returns => [
43
+ # First request is an internal error
44
+ {:body => Fixtures::Errors.internal_error, :code => 500, :error => true},
45
+ # Second request is also an internal error
46
+ {:body => Fixtures::Errors.internal_error, :code => 500, :error => true},
47
+ # Ditto third
48
+ {:body => Fixtures::Errors.internal_error, :code => 500, :error => true},
49
+ # Fourth works
50
+ {:body => Fixtures::Buckets.empty_bucket, :code => 200}
51
+ ])
52
+ bucket = nil # Block scope hack
53
+ assert_nothing_raised do
54
+ bucket = Bucket.find('marcel')
55
+ end
56
+ # Don't call objects 'cause we don't want to make another request
57
+ assert bucket.object_cache.empty?
58
+ end
59
+
60
+ def test_request_tries_again_three_times_and_gives_up
61
+ mock_connection_for(Bucket, :returns => [
62
+ # First request is an internal error
63
+ {:body => Fixtures::Errors.internal_error, :code => 500, :error => true},
64
+ # Second request is also an internal error
65
+ {:body => Fixtures::Errors.internal_error, :code => 500, :error => true},
66
+ # Ditto third
67
+ {:body => Fixtures::Errors.internal_error, :code => 500, :error => true},
68
+ # Ditto fourth
69
+ {:body => Fixtures::Errors.internal_error, :code => 500, :error => true},
70
+ ])
71
+ assert_raises(InternalError) do
72
+ Bucket.find('marcel')
73
+ end
74
+ end
75
+ end
76
+
77
+ class MultiConnectionsTest < Test::Unit::TestCase
78
+ class ClassToTestSettingCurrentBucket < Base
79
+ set_current_bucket_to 'foo'
80
+ end
81
+
82
+ def setup
83
+ Base.send(:connections).clear
84
+ end
85
+
86
+ def test_default_connection_options_are_used_for_subsequent_connections
87
+ assert !Base.connected?
88
+
89
+ assert_raises(MissingAccessKey) do
90
+ Base.establish_connection!
91
+ end
92
+
93
+ assert !Base.connected?
94
+
95
+ assert_raises(NoConnectionEstablished) do
96
+ Base.connection
97
+ end
98
+
99
+ assert_nothing_raised do
100
+ Base.establish_connection!(:access_key_id => '123', :secret_access_key => 'abc')
101
+ end
102
+
103
+ assert Base.connected?
104
+
105
+ assert_nothing_raised do
106
+ Base.connection
107
+ end
108
+
109
+ # All subclasses are currently using the default connection
110
+ assert_equal Base.connection, Bucket.connection
111
+
112
+ # No need to pass in the required options. The default connection will supply them
113
+ assert_nothing_raised do
114
+ Bucket.establish_connection!(:server => 'foo.oss.aliyuncs.com')
115
+ end
116
+
117
+ assert Base.connection != Bucket.connection
118
+ assert_equal '123', Bucket.connection.access_key_id
119
+ assert_equal 'foo', Bucket.connection.subdomain
120
+ end
121
+
122
+ def test_current_bucket
123
+ Base.establish_connection!(:access_key_id => '123', :secret_access_key => 'abc')
124
+ assert_raises(CurrentBucketNotSpecified) do
125
+ Base.current_bucket
126
+ end
127
+
128
+ OSSObject.establish_connection!(:server => 'foo-bucket.oss.aliyuncs.com')
129
+ assert_nothing_raised do
130
+ assert_equal 'foo-bucket', OSSObject.current_bucket
131
+ end
132
+ end
133
+
134
+ def test_setting_the_current_bucket
135
+ assert_equal 'foo', ClassToTestSettingCurrentBucket.current_bucket
136
+ end
137
+ end
@@ -0,0 +1,75 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require File.dirname(__FILE__) + '/test_helper'
3
+
4
+ class BucketTest < Test::Unit::TestCase
5
+ def test_bucket_name_validation
6
+ valid_names = %w(123 joe step-one step_two step3 step_4 step-5 step.six)
7
+ invalid_names = ['12', 'jo', 'kevin spacey', 'larry@wall', '', 'a' * 256]
8
+ validate_name = Proc.new {|name| Bucket.send(:validate_name!, name)}
9
+ valid_names.each do |valid_name|
10
+ assert_nothing_raised { validate_name[valid_name] }
11
+ end
12
+
13
+ invalid_names.each do |invalid_name|
14
+ assert_raises(InvalidBucketName) { validate_name[invalid_name] }
15
+ end
16
+ end
17
+
18
+ def test_empty_bucket
19
+ mock_connection_for(Bucket, :returns => {:body => Fixtures::Buckets.empty_bucket, :code => 200})
20
+ bucket = Bucket.find('marcel_molina')
21
+ assert bucket.empty?
22
+ end
23
+
24
+ def test_bucket_with_one_file
25
+ mock_connection_for(Bucket, :returns => {:body => Fixtures::Buckets.bucket_with_one_key, :code => 200})
26
+ bucket = Bucket.find('marcel_molina')
27
+ assert !bucket.empty?
28
+ assert_equal 1, bucket.size
29
+ assert_equal %w(tongue_overload.jpg), bucket.objects.map {|object| object.key}
30
+ assert bucket['tongue_overload.jpg']
31
+ end
32
+
33
+ def test_bucket_with_more_than_one_file
34
+ mock_connection_for(Bucket, :returns => {:body => Fixtures::Buckets.bucket_with_more_than_one_key, :code => 200})
35
+ bucket = Bucket.find('marcel_molina')
36
+ assert !bucket.empty?
37
+ assert_equal 2, bucket.size
38
+ assert_equal %w(beluga_baby.jpg tongue_overload.jpg), bucket.objects.map {|object| object.key}.sort
39
+ assert bucket['tongue_overload.jpg']
40
+ end
41
+
42
+ def test_bucket_path
43
+ assert_equal '/bucket_name?max-keys=2', Bucket.send(:path, 'bucket_name', :max_keys => 2)
44
+ assert_equal '/bucket_name', Bucket.send(:path, 'bucket_name', {})
45
+ end
46
+
47
+ def test_should_not_be_truncated
48
+ mock_connection_for(Bucket, :returns => {:body => Fixtures::Buckets.bucket_with_more_than_one_key, :code => 200})
49
+ bucket = Bucket.find('marcel_molina')
50
+ assert !bucket.is_truncated
51
+ end
52
+
53
+ def test_should_be_truncated
54
+ mock_connection_for(Bucket, :returns => {:body => Fixtures::Buckets.truncated_bucket_with_more_than_one_key, :code => 200})
55
+ bucket = Bucket.find('marcel_molina')
56
+ assert bucket.is_truncated
57
+ end
58
+
59
+ def test_bucket_name_should_have_leading_slash_prepended_only_once_when_forcing_a_delete
60
+ # References bug: http://rubyforge.org/tracker/index.php?func=detail&aid=19158&group_id=2409&atid=9356
61
+ bucket_name = 'foo'
62
+ expected_bucket_path = "/#{bucket_name}"
63
+
64
+ mock_bucket = flexmock('Mock bucket') do |mock|
65
+ mock.should_receive(:delete_all).once
66
+ end
67
+ mock_response = flexmock('Mock delete response') do |mock|
68
+ mock.should_receive(:success?).once
69
+ end
70
+
71
+ flexmock(Bucket).should_receive(:find).with(bucket_name).once.and_return(mock_bucket)
72
+ flexmock(Base).should_receive(:delete).with(expected_bucket_path).once.and_return(mock_response)
73
+ Bucket.delete(bucket_name, :force => true)
74
+ end
75
+ end
@@ -0,0 +1,218 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require File.dirname(__FILE__) + '/test_helper'
3
+
4
+ class ConnectionTest < Test::Unit::TestCase
5
+ attr_reader :keys
6
+ def setup
7
+ @keys = {:access_key_id => '123', :secret_access_key => 'abc'}.freeze
8
+ end
9
+
10
+ def test_creating_a_connection
11
+ connection = Connection.new(keys)
12
+ assert_kind_of Net::HTTP, connection.http
13
+ end
14
+
15
+ def test_use_ssl_option_is_set_in_connection
16
+ connection = Connection.new(keys.merge(:use_ssl => true))
17
+ assert connection.http.use_ssl?
18
+ end
19
+
20
+ def test_setting_port_to_443_implies_use_ssl
21
+ connection = Connection.new(keys.merge(:port => 443))
22
+ assert connection.http.use_ssl?
23
+ end
24
+
25
+ def test_protocol
26
+ connection = Connection.new(keys)
27
+ assert_equal 'http://', connection.protocol
28
+ connection = Connection.new(keys.merge(:use_ssl => true))
29
+ assert_equal 'https://', connection.protocol
30
+ end
31
+
32
+ def test_url_for_honors_use_ssl_option_if_it_is_false_even_if_connection_has_use_ssl_option_set
33
+ # References bug: http://rubyforge.org/tracker/index.php?func=detail&aid=17628&group_id=2409&atid=9356
34
+ connection = Connection.new(keys.merge(:use_ssl => true))
35
+ assert_match %r(^http://), connection.url_for('/pathdoesnotmatter', :authenticated => false, :use_ssl => false)
36
+ end
37
+
38
+ def test_connection_is_not_persistent_by_default
39
+ connection = Connection.new(keys)
40
+ assert !connection.persistent?
41
+
42
+ connection = Connection.new(keys.merge(:persistent => true))
43
+ assert connection.persistent?
44
+ end
45
+
46
+ def test_server_and_port_are_passed_onto_connection
47
+ connection = Connection.new(keys)
48
+ options = connection.instance_variable_get('@options')
49
+ assert_equal connection.http.address, options[:server]
50
+ assert_equal connection.http.port, options[:port]
51
+ end
52
+
53
+ def test_not_including_required_access_keys_raises
54
+ assert_raises(MissingAccessKey) do
55
+ Connection.new
56
+ end
57
+
58
+ assert_raises(MissingAccessKey) do
59
+ Connection.new(:access_key_id => '123')
60
+ end
61
+
62
+ assert_nothing_raised do
63
+ Connection.new(keys)
64
+ end
65
+ end
66
+
67
+ def test_access_keys_extracted
68
+ connection = Connection.new(keys)
69
+ assert_equal '123', connection.access_key_id
70
+ assert_equal 'abc', connection.secret_access_key
71
+ end
72
+
73
+ def test_request_method_class_lookup
74
+ connection = Connection.new(keys)
75
+ expectations = {
76
+ :get => Net::HTTP::Get, :post => Net::HTTP::Post,
77
+ :put => Net::HTTP::Put, :delete => Net::HTTP::Delete,
78
+ :head => Net::HTTP::Head
79
+ }
80
+
81
+ expectations.each do |verb, klass|
82
+ assert_equal klass, connection.send(:request_method, verb)
83
+ end
84
+ end
85
+
86
+ def test_url_for_uses_default_protocol_server_and_port
87
+ connection = Connection.new(:access_key_id => '123', :secret_access_key => 'abc', :port => 80)
88
+ assert_match %r(^http://oss\.aliyuncs\.com/foo\?), connection.url_for('/foo')
89
+
90
+ connection = Connection.new(:access_key_id => '123', :secret_access_key => 'abc', :use_ssl => true, :port => 443)
91
+ assert_match %r(^https://oss\.aliyuncs\.com/foo\?), connection.url_for('/foo')
92
+ end
93
+
94
+ def test_url_for_remembers_custom_protocol_server_and_port
95
+ connection = Connection.new(:access_key_id => '123', :secret_access_key => 'abc', :server => 'example.org', :port => 555, :use_ssl => true)
96
+ assert_match %r(^https://example\.org:555/foo\?), connection.url_for('/foo')
97
+ end
98
+
99
+ def test_url_for_with_and_without_authenticated_urls
100
+ connection = Connection.new(:access_key_id => '123', :secret_access_key => 'abc', :server => 'example.org')
101
+ authenticated = lambda {|url| url['?OSSAccessKeyId']}
102
+ assert authenticated[connection.url_for('/foo')]
103
+ assert authenticated[connection.url_for('/foo', :authenticated => true)]
104
+ assert !authenticated[connection.url_for('/foo', :authenticated => false)]
105
+ end
106
+
107
+ def test_connecting_through_a_proxy
108
+ connection = nil
109
+ assert_nothing_raised do
110
+ connection = Connection.new(keys.merge(:proxy => sample_proxy_settings))
111
+ end
112
+ assert connection.http.proxy?
113
+ end
114
+
115
+ def test_request_only_escapes_the_path_the_first_time_it_runs_and_not_subsequent_times
116
+ connection = Connection.new(@keys)
117
+ unescaped_path = 'path with spaces'
118
+ escaped_path = 'path%20with%20spaces'
119
+
120
+ flexmock(Connection).should_receive(:prepare_path).with(unescaped_path).once.and_return(escaped_path).ordered
121
+ flexmock(connection.http).should_receive(:request).and_raise(Errno::EPIPE).ordered
122
+ flexmock(connection.http).should_receive(:request).ordered
123
+ connection.request :put, unescaped_path
124
+ rescue Net::HTTPBadResponse
125
+ #TODO 阿里云tengine过滤非法请求返回为非标准Http响应
126
+ end
127
+
128
+ def test_if_request_has_no_body_then_the_content_length_is_set_to_zero
129
+ # References bug: http://rubyforge.org/tracker/index.php?func=detail&aid=13052&group_id=2409&atid=9356
130
+ connection = Connection.new(@keys)
131
+ flexmock(Net::HTTP::Put).new_instances.should_receive(:content_length=).once.with(0).ordered
132
+ flexmock(connection.http).should_receive(:request).once.ordered
133
+ connection.request :put, 'path does not matter'
134
+ end
135
+ end
136
+
137
+ class ConnectionOptionsTest < Test::Unit::TestCase
138
+
139
+ def setup
140
+ @options = generate_options(:server => 'example.org', :port => 555)
141
+ @default_options = generate_options
142
+ end
143
+
144
+ def test_server_extracted
145
+ assert_key_transfered(:server, 'example.org', @options)
146
+ end
147
+
148
+ def test_port_extracted
149
+ assert_key_transfered(:port, 555, @options)
150
+ end
151
+
152
+ def test_server_defaults_to_default_host
153
+ assert_equal DEFAULT_HOST, @default_options[:server]
154
+ end
155
+
156
+ def test_port_defaults_to_80_if_use_ssl_is_false
157
+ assert_equal 80, @default_options[:port]
158
+ end
159
+
160
+ def test_port_is_set_to_443_if_use_ssl_is_true
161
+ options = generate_options(:use_ssl => true)
162
+ assert_equal 443, options[:port]
163
+ end
164
+
165
+ def test_explicit_port_trumps_use_ssl
166
+ options = generate_options(:port => 555, :use_ssl => true)
167
+ assert_equal 555, options[:port]
168
+ end
169
+
170
+ def test_invalid_options_raise
171
+ assert_raises(InvalidConnectionOption) do
172
+ generate_options(:host => 'campfire.oss.aliyuncs.com')
173
+ end
174
+ end
175
+
176
+ def test_not_specifying_all_required_proxy_settings_raises
177
+ assert_raises(ArgumentError) do
178
+ generate_options(:proxy => {})
179
+ end
180
+ end
181
+
182
+ def test_not_specifying_proxy_option_at_all_does_not_raise
183
+ assert_nothing_raised do
184
+ generate_options
185
+ end
186
+ end
187
+
188
+ def test_specifying_all_required_proxy_settings
189
+ assert_nothing_raised do
190
+ generate_options(:proxy => sample_proxy_settings)
191
+ end
192
+ end
193
+
194
+ def test_only_host_setting_is_required
195
+ assert_nothing_raised do
196
+ generate_options(:proxy => {:host => 'http://google.com'})
197
+ end
198
+ end
199
+
200
+ def test_proxy_settings_are_extracted
201
+ options = generate_options(:proxy => sample_proxy_settings)
202
+ assert_equal sample_proxy_settings.values.map {|value| value.to_s}.sort, options.proxy_settings.map {|value| value.to_s}.sort
203
+ end
204
+
205
+ def test_recognizing_that_the_settings_want_to_connect_through_a_proxy
206
+ options = generate_options(:proxy => sample_proxy_settings)
207
+ assert options.connecting_through_proxy?
208
+ end
209
+
210
+ private
211
+ def assert_key_transfered(key, value, options)
212
+ assert_equal value, options[key]
213
+ end
214
+
215
+ def generate_options(options = {})
216
+ Connection::Options.new(options)
217
+ end
218
+ end
@@ -0,0 +1,71 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require File.dirname(__FILE__) + '/test_helper'
3
+
4
+ class ErrorTest < Test::Unit::TestCase
5
+ def setup
6
+ @container = Aliyun::OSS
7
+ @error = Error.new(Parsing::XmlParser.new(Fixtures::Errors.access_denied))
8
+ @container.send(:remove_const, :NotImplemented) if @container.const_defined?(:NotImplemented)
9
+ end
10
+
11
+ def test_error_class_is_automatically_generated
12
+ assert !@container.const_defined?('NotImplemented')
13
+ error = Error.new(Parsing::XmlParser.new(Fixtures::Errors.not_implemented))
14
+ assert @container.const_defined?('NotImplemented')
15
+ end
16
+
17
+ def test_error_contains_attributes
18
+ assert_equal 'Access Denied', @error.message
19
+ end
20
+
21
+ def test_error_is_raisable_as_exception
22
+ assert_raises(@container::AccessDenied) do
23
+ @error.raise
24
+ end
25
+ end
26
+
27
+ def test_error_message_is_passed_along_to_exception
28
+ @error.raise
29
+ rescue @container::AccessDenied => e
30
+ assert_equal 'Access Denied', e.message
31
+ end
32
+
33
+ def test_response_is_passed_along_to_exception
34
+ response = Error::Response.new(FakeResponse.new(:code => 409, :body => Fixtures::Errors.access_denied))
35
+ response.error.raise
36
+ rescue @container::ResponseError => e
37
+ assert e.response
38
+ assert_kind_of Error::Response, e.response
39
+ assert_equal response.error, e.response.error
40
+ end
41
+
42
+ def test_exception_class_clash
43
+ assert !@container.const_defined?(:NotImplemented)
44
+ # Create a class that does not inherit from exception that has the same name as the class
45
+ # the Error instance is about to attempt to find or create
46
+ @container.const_set(:NotImplemented, Class.new)
47
+ assert @container.const_defined?(:NotImplemented)
48
+
49
+ assert_raises(ExceptionClassClash) do
50
+ Error.new(Parsing::XmlParser.new(Fixtures::Errors.not_implemented))
51
+ end
52
+ end
53
+
54
+ def test_error_response_handles_attributes_with_no_value
55
+ mock_connection_for(Bucket, :returns => {:body => Fixtures::Errors.error_with_no_message, :code => 500})
56
+
57
+ begin
58
+ Bucket.create('foo', 'invalid-argument' => 'bad juju')
59
+ rescue ResponseError => error
60
+ end
61
+
62
+ assert_nothing_raised do
63
+ error.response.error.message
64
+ end
65
+ assert_nil error.response.error.message
66
+
67
+ assert_raises(NoMethodError) do
68
+ error.response.error.non_existant_method
69
+ end
70
+ end
71
+ end