aws-s3 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/COPYING +19 -0
  2. data/INSTALL +35 -0
  3. data/README +529 -0
  4. data/Rakefile +284 -0
  5. data/bin/s3sh +4 -0
  6. data/bin/setup.rb +10 -0
  7. data/lib/aws/s3.rb +64 -0
  8. data/lib/aws/s3/acl.rb +631 -0
  9. data/lib/aws/s3/authentication.rb +218 -0
  10. data/lib/aws/s3/base.rb +232 -0
  11. data/lib/aws/s3/bittorrent.rb +58 -0
  12. data/lib/aws/s3/bucket.rb +323 -0
  13. data/lib/aws/s3/connection.rb +212 -0
  14. data/lib/aws/s3/error.rb +69 -0
  15. data/lib/aws/s3/exceptions.rb +130 -0
  16. data/lib/aws/s3/extensions.rb +186 -0
  17. data/lib/aws/s3/logging.rb +163 -0
  18. data/lib/aws/s3/object.rb +565 -0
  19. data/lib/aws/s3/owner.rb +44 -0
  20. data/lib/aws/s3/parsing.rb +138 -0
  21. data/lib/aws/s3/response.rb +180 -0
  22. data/lib/aws/s3/service.rb +43 -0
  23. data/lib/aws/s3/version.rb +12 -0
  24. data/support/faster-xml-simple/lib/faster_xml_simple.rb +115 -0
  25. data/support/faster-xml-simple/test/regression_test.rb +16 -0
  26. data/support/faster-xml-simple/test/xml_simple_comparison_test.rb +22 -0
  27. data/support/rdoc/code_info.rb +211 -0
  28. data/test/acl_test.rb +243 -0
  29. data/test/authentication_test.rb +96 -0
  30. data/test/base_test.rb +143 -0
  31. data/test/bucket_test.rb +48 -0
  32. data/test/connection_test.rb +120 -0
  33. data/test/error_test.rb +75 -0
  34. data/test/extensions_test.rb +282 -0
  35. data/test/fixtures.rb +89 -0
  36. data/test/fixtures/buckets.yml +102 -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/policies.yml +16 -0
  41. data/test/logging_test.rb +36 -0
  42. data/test/mocks/base.rb +89 -0
  43. data/test/object_test.rb +177 -0
  44. data/test/parsing_test.rb +82 -0
  45. data/test/remote/acl_test.rb +117 -0
  46. data/test/remote/bittorrent_test.rb +45 -0
  47. data/test/remote/bucket_test.rb +127 -0
  48. data/test/remote/logging_test.rb +82 -0
  49. data/test/remote/object_test.rb +267 -0
  50. data/test/remote/test_file.data +0 -0
  51. data/test/remote/test_helper.rb +30 -0
  52. data/test/response_test.rb +70 -0
  53. data/test/service_test.rb +26 -0
  54. data/test/test_helper.rb +82 -0
  55. metadata +125 -0
@@ -0,0 +1,96 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class HeaderAuthenticationTest < Test::Unit::TestCase
4
+ def test_encoded_canonical
5
+ signature = Authentication::Signature.new(request, key_id, secret)
6
+ assert_equal AmazonDocExampleData::Example1.canonical_string, signature.send(:canonical_string)
7
+ assert_equal AmazonDocExampleData::Example1.signature, signature.send(:encoded_canonical)
8
+ end
9
+
10
+ def test_authorization_header
11
+ header = Authentication::Header.new(request, key_id, secret)
12
+ assert_equal AmazonDocExampleData::Example1.canonical_string, header.send(:canonical_string)
13
+ assert_equal AmazonDocExampleData::Example1.authorization_header, header
14
+ end
15
+
16
+ private
17
+ def request; AmazonDocExampleData::Example1.request end
18
+ def key_id ; AmazonDocExampleData::Example1.access_key_id end
19
+ def secret ; AmazonDocExampleData::Example1.secret_access_key end
20
+ end
21
+
22
+ class QueryStringAuthenticationTest < Test::Unit::TestCase
23
+ def test_query_string
24
+ query_string = Authentication::QueryString.new(request, key_id, secret, :expires_in => 60)
25
+ assert_equal AmazonDocExampleData::Example3.canonical_string, query_string.send(:canonical_string)
26
+ assert_equal AmazonDocExampleData::Example3.query_string, query_string
27
+ end
28
+
29
+ def test_query_string_with_explicit_expiry
30
+ query_string = Authentication::QueryString.new(request, key_id, secret, :expires => expires)
31
+ assert_equal expires, query_string.send(:canonical_string).instance_variable_get(:@options)[:expires]
32
+ assert_equal AmazonDocExampleData::Example3.query_string, query_string
33
+ end
34
+
35
+ private
36
+ def request; AmazonDocExampleData::Example3.request end
37
+ def key_id ; AmazonDocExampleData::Example3.access_key_id end
38
+ def secret ; AmazonDocExampleData::Example3.secret_access_key end
39
+ def expires; AmazonDocExampleData::Example3.expires end
40
+ end
41
+
42
+ class CanonicalStringTest < Test::Unit::TestCase
43
+ def setup
44
+ @request = Net::HTTP::Post.new('/test')
45
+ @canonical_string = Authentication::CanonicalString.new(@request)
46
+ end
47
+
48
+ def test_path_does_not_include_query_string
49
+ request = Net::HTTP::Get.new('/test/query/string?foo=bar&baz=quux')
50
+ assert_equal '/test/query/string', Authentication::CanonicalString.new(request).send(:path)
51
+
52
+ # Make sure things still work when there is *no* query string
53
+ request = Net::HTTP::Get.new('/')
54
+ assert_equal '/', Authentication::CanonicalString.new(request).send(:path)
55
+ request = Net::HTTP::Get.new('/foo/bar')
56
+ assert_equal '/foo/bar', Authentication::CanonicalString.new(request).send(:path)
57
+ end
58
+
59
+ def test_path_includes_significant_query_strings
60
+ significant_query_strings = [
61
+ ['/test/query/string?acl', '/test/query/string?acl'],
62
+ ['/test/query/string?acl&foo=bar', '/test/query/string?acl'],
63
+ ['/test/query/string?foo=bar&acl', '/test/query/string?acl'],
64
+ ['/test/query/string?acl=foo', '/test/query/string?acl'],
65
+ ['/test/query/string?torrent=foo', '/test/query/string?torrent'],
66
+ ['/test/query/string?logging=foo', '/test/query/string?logging'],
67
+ ['/test/query/string?bar=baz&acl=foo', '/test/query/string?acl']
68
+ ]
69
+
70
+ significant_query_strings.each do |uncleaned_path, expected_cleaned_path|
71
+ assert_equal expected_cleaned_path, Authentication::CanonicalString.new(Net::HTTP::Get.new(uncleaned_path)).send(:path)
72
+ end
73
+ end
74
+
75
+ def test_default_headers_set
76
+ Authentication::CanonicalString.default_headers.each do |header|
77
+ assert @canonical_string.headers.include?(header)
78
+ end
79
+ end
80
+
81
+ def test_interesting_headers_are_copied_over
82
+ an_interesting_header = 'content-md5'
83
+ string_without_interesting_header = Authentication::CanonicalString.new(@request)
84
+ assert string_without_interesting_header.headers[an_interesting_header].empty?
85
+
86
+ # Add an interesting header
87
+ @request[an_interesting_header] = 'foo'
88
+ string_with_interesting_header = Authentication::CanonicalString.new(@request)
89
+ assert_equal 'foo', string_with_interesting_header.headers[an_interesting_header]
90
+ end
91
+
92
+ def test_canonical_string
93
+ request = AmazonDocExampleData::Example1.request
94
+ assert_equal AmazonDocExampleData::Example1.canonical_string, Authentication::CanonicalString.new(request)
95
+ end
96
+ end
@@ -0,0 +1,143 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class BaseTest < Test::Unit::TestCase
4
+ def test_connection_established
5
+ assert_raises(NoConnectionEstablished) do
6
+ Base.connection
7
+ end
8
+
9
+ Base.establish_connection!(:access_key_id => '123', :secret_access_key => 'abc')
10
+ assert_kind_of Connection, Base.connection
11
+
12
+ instance = Base.new
13
+ assert_equal instance.send(:connection), Base.connection
14
+ assert_equal instance.send(:http), Base.connection.http
15
+ end
16
+
17
+ def test_respond_with
18
+ assert_equal Base::Response, Base.send(:response_class)
19
+ Base.send(:respond_with, Bucket::Response) do
20
+ assert_equal Bucket::Response, Base.send(:response_class)
21
+ end
22
+ assert_equal Base::Response, Base.send(:response_class)
23
+ end
24
+
25
+ def test_request_tries_again_when_encountering_an_internal_error
26
+ Bucket.in_test_mode do
27
+ Bucket.request_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
+ end
41
+
42
+ def test_request_tries_up_to_three_times
43
+ Bucket.in_test_mode do
44
+ Bucket.request_returns [
45
+ # First request is an internal error
46
+ {:body => Fixtures::Errors.internal_error, :code => 500, :error => true},
47
+ # Second request is also an internal error
48
+ {:body => Fixtures::Errors.internal_error, :code => 500, :error => true},
49
+ # Ditto third
50
+ {:body => Fixtures::Errors.internal_error, :code => 500, :error => true},
51
+ # Fourth works
52
+ {:body => Fixtures::Buckets.empty_bucket, :code => 200}
53
+ ]
54
+ bucket = nil # Block scope hack
55
+ assert_nothing_raised do
56
+ bucket = Bucket.find('marcel')
57
+ end
58
+ # Don't call objects 'cause we don't want to make another request
59
+ assert bucket.object_cache.empty?
60
+ end
61
+ end
62
+
63
+ def test_request_tries_again_three_times_and_gives_up
64
+ Bucket.in_test_mode do
65
+ Bucket.request_returns [
66
+ # First request is an internal error
67
+ {:body => Fixtures::Errors.internal_error, :code => 500, :error => true},
68
+ # Second request is also an internal error
69
+ {:body => Fixtures::Errors.internal_error, :code => 500, :error => true},
70
+ # Ditto third
71
+ {:body => Fixtures::Errors.internal_error, :code => 500, :error => true},
72
+ # Ditto fourth
73
+ {:body => Fixtures::Errors.internal_error, :code => 500, :error => true},
74
+ ]
75
+ assert_raises(InternalError) do
76
+ Bucket.find('marcel')
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ class MultiConnectionsTest < Test::Unit::TestCase
83
+ class ClassToTestSettingCurrentBucket < Base
84
+ set_current_bucket_to 'foo'
85
+ end
86
+
87
+ def setup
88
+ Base.send(:class_variable_get, :@@connections).clear
89
+ end
90
+ alias_method :teardown, :setup
91
+
92
+ def test_default_connection_options_are_used_for_subsequent_connections
93
+ assert !Base.connected?
94
+
95
+ assert_raises(MissingAccessKey) do
96
+ Base.establish_connection!
97
+ end
98
+
99
+ assert !Base.connected?
100
+
101
+ assert_raises(NoConnectionEstablished) do
102
+ Base.connection
103
+ end
104
+
105
+ assert_nothing_raised do
106
+ Base.establish_connection!(:access_key_id => '123', :secret_access_key => 'abc')
107
+ end
108
+
109
+ assert Base.connected?
110
+
111
+ assert_nothing_raised do
112
+ Base.connection
113
+ end
114
+
115
+ # All subclasses are currently using the default connection
116
+ assert Base.connection == Bucket.connection
117
+
118
+ # No need to pass in the required options. The default connection will supply them
119
+ assert_nothing_raised do
120
+ Bucket.establish_connection!(:server => 'foo.s3.amazonaws.com')
121
+ end
122
+
123
+ assert Base.connection != Bucket.connection
124
+ assert_equal '123', Bucket.connection.access_key_id
125
+ assert_equal 'foo', Bucket.connection.subdomain
126
+ end
127
+
128
+ def test_current_bucket
129
+ Base.establish_connection!(:access_key_id => '123', :secret_access_key => 'abc')
130
+ assert_raises(CurrentBucketNotSpecified) do
131
+ Base.current_bucket
132
+ end
133
+
134
+ S3Object.establish_connection!(:server => 'foo-bucket.s3.amazonaws.com')
135
+ assert_nothing_raised do
136
+ assert_equal 'foo-bucket', S3Object.current_bucket
137
+ end
138
+ end
139
+
140
+ def test_setting_the_current_bucket
141
+ assert_equal 'foo', ClassToTestSettingCurrentBucket.current_bucket
142
+ end
143
+ end
@@ -0,0 +1,48 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class BucketTest < Test::Unit::TestCase
4
+ def test_bucket_name_validation
5
+ valid_names = %w(123 joe step-one step_two step3 step_4 step-5)
6
+ invalid_names = ['12', 'jo', 'kevin spacey', 'larry@wall', '', 'a' * 256]
7
+ validate_name = Proc.new {|name| Bucket.send(:validate_name!, name)}
8
+ valid_names.each do |valid_name|
9
+ assert_nothing_raised { validate_name[valid_name] }
10
+ end
11
+
12
+ invalid_names.each do |invalid_name|
13
+ assert_raises(InvalidBucketName) { validate_name[invalid_name] }
14
+ end
15
+ end
16
+
17
+ def test_empty_bucket
18
+ Bucket.request_always_returns :body => Fixtures::Buckets.empty_bucket, :code => 200 do
19
+ bucket = Bucket.find('marcel_molina')
20
+ assert bucket.empty?
21
+ end
22
+ end
23
+
24
+ def test_bucket_with_one_file
25
+ Bucket.request_always_returns :body => Fixtures::Buckets.bucket_with_one_key, :code => 200 do
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
+ end
33
+
34
+ def test_bucket_with_more_than_one_file
35
+ Bucket.request_always_returns :body => Fixtures::Buckets.bucket_with_more_than_one_key, :code => 200 do
36
+ bucket = Bucket.find('marcel_molina')
37
+ assert !bucket.empty?
38
+ assert_equal 2, bucket.size
39
+ assert_equal %w(beluga_baby.jpg tongue_overload.jpg), bucket.objects.map {|object| object.key}.sort
40
+ assert bucket['tongue_overload.jpg']
41
+ end
42
+ end
43
+
44
+ def test_bucket_path
45
+ assert_equal '/bucket_name?max-keys=2', Bucket.send(:path, 'bucket_name', :max_keys => 2)
46
+ assert_equal '/bucket_name', Bucket.send(:path, 'bucket_name', {})
47
+ end
48
+ end
@@ -0,0 +1,120 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ConnectionTest < Test::Unit::TestCase
4
+ def setup
5
+ @keys = {:access_key_id => '123', :secret_access_key => 'abc'}
6
+ end
7
+
8
+ def test_creating_a_connection
9
+ connection = Connection.new(@keys)
10
+ assert_kind_of Net::HTTP, connection.http
11
+ end
12
+
13
+ def test_use_ssl_option_is_set_in_connection
14
+ connection = Connection.new(@keys.merge(:use_ssl => true))
15
+ assert connection.http.use_ssl?
16
+ end
17
+
18
+ def test_setting_port_to_443_implies_use_ssl
19
+ connection = Connection.new(@keys.merge(:port => 443))
20
+ assert connection.http.use_ssl?
21
+ end
22
+
23
+ def test_protocol
24
+ connection = Connection.new(@keys)
25
+ assert_equal 'http://', connection.protocol
26
+ connection = Connection.new(@keys.merge(:use_ssl => true))
27
+ assert_equal 'https://', connection.protocol
28
+ end
29
+
30
+ def test_server_and_port_are_passed_onto_connection
31
+ connection = Connection.new(@keys)
32
+ options = connection.instance_variable_get('@options')
33
+ assert_equal connection.http.address, options[:server]
34
+ assert_equal connection.http.port, options[:port]
35
+ end
36
+
37
+ def test_not_including_required_access_keys_raises
38
+ assert_raises(MissingAccessKey) do
39
+ Connection.new
40
+ end
41
+
42
+ assert_raises(MissingAccessKey) do
43
+ Connection.new(:access_key_id => '123')
44
+ end
45
+
46
+ assert_nothing_raised do
47
+ Connection.new(@keys)
48
+ end
49
+ end
50
+
51
+ def test_access_keys_extracted
52
+ connection = Connection.new(@keys)
53
+ assert_equal '123', connection.access_key_id
54
+ assert_equal 'abc', connection.secret_access_key
55
+ end
56
+
57
+ def test_request_method_class_lookup
58
+ c = Connection.new(@keys)
59
+ expectations = {
60
+ :get => Net::HTTP::Get, :post => Net::HTTP::Post,
61
+ :put => Net::HTTP::Put, :delete => Net::HTTP::Delete,
62
+ :head => Net::HTTP::Head
63
+ }
64
+
65
+ expectations.each do |verb, klass|
66
+ assert_equal klass, c.send(:request_method, verb)
67
+ end
68
+ end
69
+ end
70
+
71
+ class ConnectionOptionsTest < Test::Unit::TestCase
72
+
73
+ def setup
74
+ @options = generate_options(:server => 'example.org', :port => 555)
75
+ @default_options = generate_options
76
+ end
77
+
78
+ def test_server_extracted
79
+ assert_key_transfered(:server, 'example.org', @options)
80
+ end
81
+
82
+ def test_port_extracted
83
+ assert_key_transfered(:port, 555, @options)
84
+ end
85
+
86
+ def test_server_defaults_to_default_host
87
+ assert_equal DEFAULT_HOST, @default_options[:server]
88
+ end
89
+
90
+ def test_port_defaults_to_80_if_use_ssl_is_false
91
+ assert_equal 80, @default_options[:port]
92
+ end
93
+
94
+ def test_port_is_set_to_443_if_use_ssl_is_true
95
+ options = generate_options(:use_ssl => true)
96
+ assert_equal 443, options[:port]
97
+ end
98
+
99
+ def test_explicit_port_trumps_use_ssl
100
+ options = generate_options(:port => 555, :use_ssl => true)
101
+ assert_equal 555, options[:port]
102
+ end
103
+
104
+ def test_invalid_options_raise
105
+ assert_raises(InvalidConnectionOption) do
106
+ generate_options(:host => 'campfire.s3.amazonaws.com')
107
+ end
108
+ end
109
+
110
+ private
111
+
112
+ def assert_key_transfered(key, value, options)
113
+ assert_equal value, options[key]
114
+ assert !options.instance_variable_get('@options').has_key?(key)
115
+ end
116
+
117
+ def generate_options(options = {})
118
+ Connection::Options.new(options)
119
+ end
120
+ end
@@ -0,0 +1,75 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ErrorTest < Test::Unit::TestCase
4
+ def setup
5
+ @container = AWS::S3
6
+ @error = Error.new(Parsing::XmlParser.new(Fixtures::Errors.access_denied))
7
+ end
8
+
9
+ def teardown
10
+ @container.send(:remove_const, :NotImplemented) if @container.const_defined?(:NotImplemented)
11
+ end
12
+
13
+ def test_error_class_is_automatically_generated
14
+ assert !@container.const_defined?('NotImplemented')
15
+ error = Error.new(Parsing::XmlParser.new(Fixtures::Errors.not_implemented))
16
+ assert @container.const_defined?('NotImplemented')
17
+ end
18
+
19
+ def test_error_contains_attributes
20
+ assert_equal 'Access Denied', @error.message
21
+ end
22
+
23
+ def test_error_is_raisable_as_exception
24
+ assert_raises(@container::AccessDenied) do
25
+ @error.raise
26
+ end
27
+ end
28
+
29
+ def test_error_message_is_passed_along_to_exception
30
+ @error.raise
31
+ rescue @container::AccessDenied => e
32
+ assert_equal 'Access Denied', e.message
33
+ end
34
+
35
+ def test_response_is_passed_along_to_exception
36
+ response = Error::Response.new(FakeResponse.new(:code => 409, :body => Fixtures::Errors.access_denied))
37
+ response.error.raise
38
+ rescue @container::ResponseError => e
39
+ assert e.response
40
+ assert_kind_of Error::Response, e.response
41
+ assert_equal response.error, e.response.error
42
+ end
43
+
44
+ def test_exception_class_clash
45
+ assert !@container.const_defined?(:NotImplemented)
46
+ # Create a class that does not inherit from exception that has the same name as the class
47
+ # the Error instance is about to attempt to find or create
48
+ @container.const_set(:NotImplemented, Class.new)
49
+ assert @container.const_defined?(:NotImplemented)
50
+
51
+ assert_raises(ExceptionClassClash) do
52
+ Error.new(Parsing::XmlParser.new(Fixtures::Errors.not_implemented))
53
+ end
54
+ end
55
+
56
+ def test_error_response_handles_attributes_with_no_value
57
+ Bucket.in_test_mode do
58
+ Bucket.request_returns :body => Fixtures::Errors.error_with_no_message, :code => 500
59
+
60
+ begin
61
+ Bucket.create('foo', 'invalid-argument' => 'bad juju')
62
+ rescue ResponseError => error
63
+ end
64
+
65
+ assert_nothing_raised do
66
+ error.response.error.message
67
+ end
68
+ assert_nil error.response.error.message
69
+
70
+ assert_raises(NoMethodError) do
71
+ error.response.error.non_existant_method
72
+ end
73
+ end
74
+ end
75
+ end