aws-s3 0.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.
- data/COPYING +19 -0
- data/INSTALL +35 -0
- data/README +529 -0
- data/Rakefile +284 -0
- data/bin/s3sh +4 -0
- data/bin/setup.rb +10 -0
- data/lib/aws/s3.rb +64 -0
- data/lib/aws/s3/acl.rb +631 -0
- data/lib/aws/s3/authentication.rb +218 -0
- data/lib/aws/s3/base.rb +232 -0
- data/lib/aws/s3/bittorrent.rb +58 -0
- data/lib/aws/s3/bucket.rb +323 -0
- data/lib/aws/s3/connection.rb +212 -0
- data/lib/aws/s3/error.rb +69 -0
- data/lib/aws/s3/exceptions.rb +130 -0
- data/lib/aws/s3/extensions.rb +186 -0
- data/lib/aws/s3/logging.rb +163 -0
- data/lib/aws/s3/object.rb +565 -0
- data/lib/aws/s3/owner.rb +44 -0
- data/lib/aws/s3/parsing.rb +138 -0
- data/lib/aws/s3/response.rb +180 -0
- data/lib/aws/s3/service.rb +43 -0
- data/lib/aws/s3/version.rb +12 -0
- data/support/faster-xml-simple/lib/faster_xml_simple.rb +115 -0
- data/support/faster-xml-simple/test/regression_test.rb +16 -0
- data/support/faster-xml-simple/test/xml_simple_comparison_test.rb +22 -0
- data/support/rdoc/code_info.rb +211 -0
- data/test/acl_test.rb +243 -0
- data/test/authentication_test.rb +96 -0
- data/test/base_test.rb +143 -0
- data/test/bucket_test.rb +48 -0
- data/test/connection_test.rb +120 -0
- data/test/error_test.rb +75 -0
- data/test/extensions_test.rb +282 -0
- data/test/fixtures.rb +89 -0
- data/test/fixtures/buckets.yml +102 -0
- data/test/fixtures/errors.yml +34 -0
- data/test/fixtures/headers.yml +3 -0
- data/test/fixtures/logging.yml +15 -0
- data/test/fixtures/policies.yml +16 -0
- data/test/logging_test.rb +36 -0
- data/test/mocks/base.rb +89 -0
- data/test/object_test.rb +177 -0
- data/test/parsing_test.rb +82 -0
- data/test/remote/acl_test.rb +117 -0
- data/test/remote/bittorrent_test.rb +45 -0
- data/test/remote/bucket_test.rb +127 -0
- data/test/remote/logging_test.rb +82 -0
- data/test/remote/object_test.rb +267 -0
- data/test/remote/test_file.data +0 -0
- data/test/remote/test_helper.rb +30 -0
- data/test/response_test.rb +70 -0
- data/test/service_test.rb +26 -0
- data/test/test_helper.rb +82 -0
- metadata +125 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
not_implemented: >
|
2
|
+
<Error>
|
3
|
+
<Code>NotImplemented</Code>
|
4
|
+
<Message>A header you provided implies functionality that is not implemented</Message>
|
5
|
+
<RequestId>D1D13A09AC92427F</RequestId>
|
6
|
+
<Header>Host</Header>
|
7
|
+
<HostId>oNZgzTTmWiovwGGwHXAzz+1vRmAJVAplS9TF7B0cuOGfEwoi7DYSTa/1Qhv90CfW</HostId>
|
8
|
+
</Error>
|
9
|
+
|
10
|
+
access_denied: >
|
11
|
+
<Error>
|
12
|
+
<Code>AccessDenied</Code>
|
13
|
+
<Message>Access Denied</Message>
|
14
|
+
<RequestId>F99F6D58B96C98E0</RequestId>
|
15
|
+
<HostId>XwCF7k3llrcEwtoHR7MusZ6ilCdF5DKDmwYpglvjKNjvwo24INCeXlEpo1M03Wxm</HostId>
|
16
|
+
</Error>
|
17
|
+
|
18
|
+
internal_error: >
|
19
|
+
<Error>
|
20
|
+
<Code>InternalError</Code>
|
21
|
+
<Message>Internal Error</Message>
|
22
|
+
<RequestId>F99F6D223B96C98E0</RequestId>
|
23
|
+
<HostId>XwCF7k3llrcEwtoHR7MusZ6ilCdF5DKDmwYpglvjKNjvwo24INCeXlEpo1M03Wxm</HostId>
|
24
|
+
</Error>
|
25
|
+
|
26
|
+
error_with_no_message: >
|
27
|
+
<Error>
|
28
|
+
<Code>InvalidArgument</Code>
|
29
|
+
<Message></Message>
|
30
|
+
<ArgumentValue>READ</ArgumentValue>
|
31
|
+
<RequestId>74A377B1C0FA2BCF</RequestId>
|
32
|
+
<HostId>cP4rqsAEtHpN6Ckv08Hr3LXjLzx15/YgyoSqzs779vMR8MrAFSodxZp96wtuMQuI</HostId>
|
33
|
+
<ArgumentName>x-amz-acl</ArgumentName>
|
34
|
+
</Error>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
logging_enabled: >
|
2
|
+
<BucketLoggingStatus xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
3
|
+
<LoggingEnabled>
|
4
|
+
<TargetBucket>mylogs</TargetBucket>
|
5
|
+
<TargetPrefix>access_log-</TargetPrefix>
|
6
|
+
</LoggingEnabled>
|
7
|
+
</BucketLoggingStatus>
|
8
|
+
|
9
|
+
logging_disabled: >
|
10
|
+
<BucketLoggingStatus xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
11
|
+
<!--<LoggingEnabled>
|
12
|
+
<TargetBucket>myLogsBucket</TargetBucket>
|
13
|
+
<TargetPrefix>add/this/prefix/to/my/log/files/access_log-</TargetPrefix>
|
14
|
+
</LoggingEnabled>-->
|
15
|
+
</BucketLoggingStatus>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
policy_with_one_grant: >
|
2
|
+
<AccessControlPolicy>
|
3
|
+
<Owner>
|
4
|
+
<ID>bb2041a25975c3d4ce9775fe9e93e5b77a6a9fad97dc7e00686191f3790b13f1</ID>
|
5
|
+
<DisplayName>mmolina@onramp.net</DisplayName>
|
6
|
+
</Owner>
|
7
|
+
<AccessControlList>
|
8
|
+
<Grant>
|
9
|
+
<Grantee xsi:type="CanonicalUser" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
10
|
+
<ID>bb2041a25975c3d4ce9775fe9e93e5b77a6a9fad97dc7e00686191f3790b13f1</ID>
|
11
|
+
<DisplayName>mmolina@onramp.net</DisplayName>
|
12
|
+
</Grantee>
|
13
|
+
<Permission>FULL_CONTROL</Permission>
|
14
|
+
</Grant>
|
15
|
+
</AccessControlList>
|
16
|
+
</AccessControlPolicy>
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class LoggingStatusReadingTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@disabled = logging_status(:logging_disabled)
|
7
|
+
@enabled = logging_status(:logging_enabled)
|
8
|
+
@new_status = Logging::Status.new('target_bucket' => 'foo', 'target_prefix' => 'access-log-')
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_logging_enabled?
|
12
|
+
assert !@disabled.logging_enabled?
|
13
|
+
assert !@new_status.logging_enabled?
|
14
|
+
assert @enabled.logging_enabled?
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_passing_in_prefix_and_bucket
|
18
|
+
assert_equal 'foo', @new_status.target_bucket
|
19
|
+
assert_equal 'access-log-', @new_status.target_prefix
|
20
|
+
assert !@new_status.logging_enabled?
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def logging_status(fixture)
|
25
|
+
Logging::Status.new(Parsing::XmlParser.new(Fixtures::Logging[fixture.to_s]))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class LoggingStatusWritingTest < LoggingStatusReadingTest
|
30
|
+
|
31
|
+
def setup
|
32
|
+
super
|
33
|
+
@disabled = Logging::Status.new(Parsing::XmlParser.new(@disabled.to_xml))
|
34
|
+
@enabled = Logging::Status.new(Parsing::XmlParser.new(@enabled.to_xml))
|
35
|
+
end
|
36
|
+
end
|
data/test/mocks/base.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require_library_or_gem 'flexmock'
|
2
|
+
|
3
|
+
module AWS
|
4
|
+
module S3
|
5
|
+
class FakeResponse < String
|
6
|
+
attr_reader :code, :body, :headers
|
7
|
+
def initialize(options = {})
|
8
|
+
@code = options.delete(:code) || 200
|
9
|
+
@body = options.delete(:body) || ''
|
10
|
+
@headers = {'content-type' => 'application/xml'}.merge(options.delete(:headers) || {})
|
11
|
+
super(@body)
|
12
|
+
end
|
13
|
+
|
14
|
+
# For ErrorResponse
|
15
|
+
def response
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](header)
|
20
|
+
headers[header]
|
21
|
+
end
|
22
|
+
|
23
|
+
def each(&block)
|
24
|
+
headers.each(&block)
|
25
|
+
end
|
26
|
+
alias_method :each_header, :each
|
27
|
+
end
|
28
|
+
|
29
|
+
class Base
|
30
|
+
class << self
|
31
|
+
@@responses = []
|
32
|
+
@@in_test_mode = false
|
33
|
+
@@catch_all_response = nil
|
34
|
+
|
35
|
+
def in_test_mode=(boolean)
|
36
|
+
@@in_test_mode = boolean
|
37
|
+
end
|
38
|
+
|
39
|
+
def responses
|
40
|
+
@@responses
|
41
|
+
end
|
42
|
+
|
43
|
+
def catch_all_response
|
44
|
+
@@catch_all_response
|
45
|
+
end
|
46
|
+
|
47
|
+
def reset!
|
48
|
+
responses.clear
|
49
|
+
end
|
50
|
+
|
51
|
+
def request_returns(response_data)
|
52
|
+
responses.concat [response_data].flatten.map {|data| FakeResponse.new(data)}
|
53
|
+
end
|
54
|
+
|
55
|
+
def request_always_returns(response_data, &block)
|
56
|
+
in_test_mode do
|
57
|
+
@@catch_all_response = FakeResponse.new(response_data)
|
58
|
+
yield
|
59
|
+
@@catch_all_response = nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def in_test_mode(&block)
|
64
|
+
self.in_test_mode = true
|
65
|
+
yield
|
66
|
+
ensure
|
67
|
+
self.in_test_mode = false
|
68
|
+
end
|
69
|
+
|
70
|
+
alias_method :old_connection, :connection
|
71
|
+
def connection
|
72
|
+
if @@in_test_mode
|
73
|
+
@mock_connection ||=
|
74
|
+
begin
|
75
|
+
mock_connection = FlexMock.new
|
76
|
+
mock_connection.mock_handle(:request) do
|
77
|
+
raise 'No responses left' unless response = catch_all_response || responses.shift
|
78
|
+
response
|
79
|
+
end
|
80
|
+
mock_connection
|
81
|
+
end
|
82
|
+
else
|
83
|
+
old_connection
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/test/object_test.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class ObjectTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
bucket = Bucket.new(Parsing::XmlParser.new(Fixtures::Buckets.bucket_with_one_key))
|
6
|
+
@object = bucket.objects.first
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_header_settings_reader_and_writer
|
10
|
+
S3Object.in_test_mode do
|
11
|
+
headers = {'content-type' => 'text/plain'}
|
12
|
+
S3Object.request_returns :headers => headers
|
13
|
+
|
14
|
+
assert_nothing_raised do
|
15
|
+
@object.content_type
|
16
|
+
end
|
17
|
+
|
18
|
+
assert_equal 'text/plain', @object.content_type
|
19
|
+
|
20
|
+
assert_nothing_raised do
|
21
|
+
@object.content_type = 'image/jpg'
|
22
|
+
end
|
23
|
+
|
24
|
+
assert_equal 'image/jpg', @object.content_type
|
25
|
+
|
26
|
+
assert_raises(NoMethodError) do
|
27
|
+
@object.non_existant_header_setting
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_key_name_validation
|
33
|
+
assert_raises(InvalidKeyName) do
|
34
|
+
S3Object.create(nil, '', 'marcel')
|
35
|
+
end
|
36
|
+
|
37
|
+
assert_raises(InvalidKeyName) do
|
38
|
+
huge_name = 'a' * 1500
|
39
|
+
S3Object.create(huge_name, '', 'marcel')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_object_has_owner
|
44
|
+
assert_kind_of Owner, @object.owner
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_owner_attributes_are_accessible
|
48
|
+
owner = @object.owner
|
49
|
+
assert owner.id
|
50
|
+
assert owner.display_name
|
51
|
+
assert_equal 'bb2041a25975c3d4ce9775fe9e93e5b77a6a9fad97dc7e00686191f3790b13f1', owner.id
|
52
|
+
assert_equal 'mmolina@onramp.net', owner.display_name
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_only_valid_attributes_accessible
|
56
|
+
assert_raises(NoMethodError) do
|
57
|
+
@object.owner.foo
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_fetching_object_value_generates_value_object
|
62
|
+
S3Object.in_test_mode do
|
63
|
+
S3Object.request_returns :body => 'hello!'
|
64
|
+
value = S3Object.value('foo', 'bar')
|
65
|
+
assert_kind_of S3Object::Value, value
|
66
|
+
assert_equal 'hello!', value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_fetching_file_by_name_raises_when_heuristic_fails
|
71
|
+
S3Object.request_always_returns :body => Fixtures::Buckets.bucket_with_one_key do
|
72
|
+
assert_raises(NoSuchKey) do
|
73
|
+
S3Object.find('not_tongue_overload.jpg', 'marcel_molina')
|
74
|
+
end
|
75
|
+
|
76
|
+
object = nil # Block scoping
|
77
|
+
assert_nothing_raised do
|
78
|
+
object = S3Object.find('tongue_overload.jpg', 'marcel_molina')
|
79
|
+
end
|
80
|
+
assert_kind_of S3Object, object
|
81
|
+
assert_equal 'tongue_overload.jpg', object.key
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_about
|
86
|
+
S3Object.in_test_mode do
|
87
|
+
headers = {'content-size' => '12345', 'date' => Time.now.httpdate, 'content-type' => 'application/xml'}
|
88
|
+
S3Object.request_returns :headers => headers
|
89
|
+
about = S3Object.about('foo', 'bar')
|
90
|
+
assert_kind_of S3Object::About, about
|
91
|
+
assert_equal headers, about
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_s3object_equality
|
96
|
+
Bucket.in_test_mode do
|
97
|
+
Bucket.request_returns :body => Fixtures::Buckets.bucket_with_more_than_one_key
|
98
|
+
file1, file2 = Bucket.objects('does not matter')
|
99
|
+
assert file1 == file1
|
100
|
+
assert file2 == file2
|
101
|
+
assert !(file1 == file2) # /!\ Parens required /!\
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_inspect
|
106
|
+
S3Object.in_test_mode do
|
107
|
+
S3Object.request_returns :body => Fixtures::Buckets.bucket_with_one_key
|
108
|
+
object = S3Object.find('tongue_overload.jpg', 'bucket does not matter')
|
109
|
+
assert object.path
|
110
|
+
assert_nothing_raised { object.inspect }
|
111
|
+
assert object.inspect[object.path]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_etag
|
116
|
+
S3Object.in_test_mode do
|
117
|
+
S3Object.request_returns :body => Fixtures::Buckets.bucket_with_one_key
|
118
|
+
file = S3Object.find('tongue_overload.jpg', 'bucket does not matter')
|
119
|
+
assert file.etag
|
120
|
+
assert_equal 'f21f7c4e8ea6e34b268887b07d6da745', file.etag
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class MetadataTest < Test::Unit::TestCase
|
126
|
+
def setup
|
127
|
+
@metadata = S3Object::Metadata.new(Fixtures::Headers.headers_including_one_piece_of_metadata)
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_only_metadata_is_extracted
|
131
|
+
assert @metadata.to_headers.size == 1
|
132
|
+
assert @metadata.to_headers['x-amz-meta-test']
|
133
|
+
assert_equal 'foo', @metadata.to_headers['x-amz-meta-test']
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_setting_new_metadata_normalizes_name
|
137
|
+
@metadata[:bar] = 'baz'
|
138
|
+
assert @metadata.to_headers.include?('x-amz-meta-bar')
|
139
|
+
@metadata['baz'] = 'quux'
|
140
|
+
assert @metadata.to_headers.include?('x-amz-meta-baz')
|
141
|
+
@metadata['x-amz-meta-quux'] = 'whatever'
|
142
|
+
assert @metadata.to_headers.include?('x-amz-meta-quux')
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_clobbering_existing_header
|
146
|
+
@metadata[:bar] = 'baz'
|
147
|
+
assert_equal 'baz', @metadata.to_headers['x-amz-meta-bar']
|
148
|
+
@metadata[:bar] = 'quux'
|
149
|
+
assert_equal 'quux', @metadata.to_headers['x-amz-meta-bar']
|
150
|
+
@metadata['bar'] = 'foo'
|
151
|
+
assert_equal 'foo', @metadata.to_headers['x-amz-meta-bar']
|
152
|
+
@metadata['x-amz-meta-bar'] = 'bar'
|
153
|
+
assert_equal 'bar', @metadata.to_headers['x-amz-meta-bar']
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_invalid_metadata
|
157
|
+
@metadata[:invalid_header] = ' ' * (S3Object::Metadata::SIZE_LIMIT + 1)
|
158
|
+
assert_raises InvalidMetadataValue do
|
159
|
+
@metadata.to_headers
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class ValueTest < Test::Unit::TestCase
|
165
|
+
def setup
|
166
|
+
@response = FakeResponse.new(:body => 'hello there')
|
167
|
+
@value = S3Object::Value.new(@response)
|
168
|
+
end
|
169
|
+
|
170
|
+
def test_value_is_set_to_response_body
|
171
|
+
assert_equal @response.body, @value
|
172
|
+
end
|
173
|
+
|
174
|
+
def test_response_is_accessible_from_value_object
|
175
|
+
assert_equal @response, @value.response
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
class CoercibleStringTest < Test::Unit::TestCase
|
3
|
+
|
4
|
+
def test_coerce
|
5
|
+
coercions = [
|
6
|
+
['1', 1],
|
7
|
+
['false', false],
|
8
|
+
['true', true],
|
9
|
+
['2006-10-29T23:14:47.000Z', Time.parse('2006-10-29T23:14:47.000Z')],
|
10
|
+
['Hello!', 'Hello!'],
|
11
|
+
['false23', 'false23'],
|
12
|
+
['03 1-2-3-Apple-Tree.mp3', '03 1-2-3-Apple-Tree.mp3']
|
13
|
+
]
|
14
|
+
|
15
|
+
coercions.each do |before, after|
|
16
|
+
assert_nothing_raised do
|
17
|
+
assert_equal after, Parsing::CoercibleString.coerce(before)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class TypecastingTest < Test::Unit::TestCase
|
24
|
+
# Make it easier to call methods in tests
|
25
|
+
Parsing::Typecasting.public_instance_methods.each do |method|
|
26
|
+
Parsing::Typecasting.send(:module_function, method)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_array_with_one_element_that_is_a_hash
|
30
|
+
value = [{'Available' => 'true'}]
|
31
|
+
assert_equal [{'available' => true}], Parsing::Typecasting.typecast(value)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_hash_with_one_key_whose_value_is_an_array
|
35
|
+
value = {
|
36
|
+
'Bucket' =>
|
37
|
+
[
|
38
|
+
{'Available' => 'true'}
|
39
|
+
]
|
40
|
+
}
|
41
|
+
|
42
|
+
expected = {
|
43
|
+
'bucket' =>
|
44
|
+
[
|
45
|
+
{'available' => true}
|
46
|
+
]
|
47
|
+
}
|
48
|
+
assert_equal expected, Parsing::Typecasting.typecast(value)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class XmlParserTest < Test::Unit::TestCase
|
54
|
+
def test_bucket_is_always_forced_to_be_an_array_unless_empty
|
55
|
+
one_bucket = Parsing::XmlParser.new(Fixtures::Buckets.bucket_list_with_one_bucket)
|
56
|
+
more_than_one = Parsing::XmlParser.new(Fixtures::Buckets.bucket_list_with_more_than_one_bucket)
|
57
|
+
|
58
|
+
[one_bucket, more_than_one].each do |bucket_list|
|
59
|
+
assert_kind_of Array, bucket_list['buckets']['bucket']
|
60
|
+
end
|
61
|
+
|
62
|
+
no_buckets = Parsing::XmlParser.new(Fixtures::Buckets.empty_bucket_list)
|
63
|
+
assert no_buckets.has_key?('buckets')
|
64
|
+
assert_nil no_buckets['buckets']
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_bucket_contents_are_forced_to_be_an_array_unless_empty
|
68
|
+
one_key = Parsing::XmlParser.new(Fixtures::Buckets.bucket_with_one_key)
|
69
|
+
more_than_one = Parsing::XmlParser.new(Fixtures::Buckets.bucket_with_more_than_one_key)
|
70
|
+
[one_key, more_than_one].each do |bucket_with_contents|
|
71
|
+
assert_kind_of Array, bucket_with_contents['contents']
|
72
|
+
end
|
73
|
+
|
74
|
+
no_keys = Parsing::XmlParser.new(Fixtures::Buckets.empty_bucket)
|
75
|
+
assert !no_keys.has_key?('contents')
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_policy_grants_are_always_an_array
|
79
|
+
policy = Parsing::XmlParser.new(Fixtures::Policies.policy_with_one_grant)
|
80
|
+
assert_kind_of Array, policy['access_control_list']['grant']
|
81
|
+
end
|
82
|
+
end
|