fakes3as6 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/CONTRIBUTING.md +50 -0
- data/DEPLOY_README.md +18 -0
- data/Dockerfile +13 -0
- data/Gemfile +4 -0
- data/ISSUE_TEMPLATE.md +9 -0
- data/Makefile +7 -0
- data/PULL_REQUEST_TEMPLATE.md +9 -0
- data/README.md +42 -0
- data/Rakefile +18 -0
- data/bin/fakes3 +6 -0
- data/fakes3.gemspec +35 -0
- data/lib/fakes3.rb +3 -0
- data/lib/fakes3/bucket.rb +65 -0
- data/lib/fakes3/bucket_query.rb +11 -0
- data/lib/fakes3/cli.rb +71 -0
- data/lib/fakes3/errors.rb +46 -0
- data/lib/fakes3/file_store.rb +320 -0
- data/lib/fakes3/rate_limitable_file.rb +21 -0
- data/lib/fakes3/s3_object.rb +19 -0
- data/lib/fakes3/server.rb +581 -0
- data/lib/fakes3/sorted_object_list.rb +137 -0
- data/lib/fakes3/unsupported_operation.rb +4 -0
- data/lib/fakes3/util.rb +8 -0
- data/lib/fakes3/version.rb +3 -0
- data/lib/fakes3/xml_adapter.rb +222 -0
- data/lib/fakes3/xml_parser.rb +16 -0
- data/static/button.svg +4 -0
- data/static/logo.png +0 -0
- data/test/aws_sdk_commands_test.rb +98 -0
- data/test/aws_sdk_v2_commands_test.rb +65 -0
- data/test/boto_test.rb +25 -0
- data/test/botocmd.py +87 -0
- data/test/cli_test.rb +18 -0
- data/test/local_s3_cfg +34 -0
- data/test/minitest_helper.rb +46 -0
- data/test/post_test.rb +58 -0
- data/test/right_aws_commands_test.rb +219 -0
- data/test/s3_commands_test.rb +208 -0
- data/test/s3cmd_test.rb +52 -0
- data/test/test_helper.rb +6 -0
- metadata +270 -0
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'set'
|
2
|
+
module FakeS3
|
3
|
+
class S3MatchSet
|
4
|
+
attr_accessor :matches,:is_truncated,:common_prefixes
|
5
|
+
def initialize
|
6
|
+
@matches = []
|
7
|
+
@is_truncated = false
|
8
|
+
@common_prefixes = []
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# This class has some of the semantics necessary for how buckets can return
|
13
|
+
# their items
|
14
|
+
#
|
15
|
+
# It is currently implemented naively as a sorted set + hash If you are going
|
16
|
+
# to try to put massive lists inside buckets and ls them, you will be sorely
|
17
|
+
# disappointed about this performance.
|
18
|
+
class SortedObjectList
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@sorted_set = SortedSet.new
|
22
|
+
@object_map = {}
|
23
|
+
@mutex = Mutex.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def count
|
27
|
+
@sorted_set.count
|
28
|
+
end
|
29
|
+
|
30
|
+
def find(object_name)
|
31
|
+
@object_map[object_name]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Add an S3 object into the sorted list
|
35
|
+
def add(s3_object)
|
36
|
+
return if !s3_object
|
37
|
+
|
38
|
+
@object_map[s3_object.name] = s3_object
|
39
|
+
@sorted_set << s3_object
|
40
|
+
end
|
41
|
+
|
42
|
+
def remove(s3_object)
|
43
|
+
return if !s3_object
|
44
|
+
|
45
|
+
@object_map.delete(s3_object.name)
|
46
|
+
@sorted_set.delete(s3_object)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return back a set of matches based on the passed in options
|
50
|
+
#
|
51
|
+
# options:
|
52
|
+
#
|
53
|
+
# :marker : a string to start the lexographical search (it is not included
|
54
|
+
# in the result)
|
55
|
+
# :max_keys : a maximum number of results
|
56
|
+
# :prefix : a string to filter the results by
|
57
|
+
# :delimiter : not supported yet
|
58
|
+
def list(options)
|
59
|
+
marker = options[:marker]
|
60
|
+
prefix = options[:prefix]
|
61
|
+
max_keys = options[:max_keys] || 1000
|
62
|
+
delimiter = options[:delimiter]
|
63
|
+
|
64
|
+
ms = S3MatchSet.new
|
65
|
+
|
66
|
+
marker_found = true
|
67
|
+
pseudo = nil
|
68
|
+
if marker
|
69
|
+
marker_found = false
|
70
|
+
if !@object_map[marker]
|
71
|
+
pseudo = S3Object.new
|
72
|
+
pseudo.name = marker
|
73
|
+
@sorted_set << pseudo
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
if delimiter
|
78
|
+
if prefix
|
79
|
+
base_prefix = prefix
|
80
|
+
else
|
81
|
+
base_prefix = ""
|
82
|
+
end
|
83
|
+
prefix_offset = base_prefix.length
|
84
|
+
end
|
85
|
+
|
86
|
+
count = 0
|
87
|
+
last_chunk = nil
|
88
|
+
@sorted_set.each do |s3_object|
|
89
|
+
if marker_found && (!prefix or s3_object.name.index(prefix) == 0)
|
90
|
+
if delimiter
|
91
|
+
name = s3_object.name
|
92
|
+
remainder = name.slice(prefix_offset, name.length)
|
93
|
+
chunks = remainder.split(delimiter, 2)
|
94
|
+
if chunks.length > 1
|
95
|
+
if (last_chunk != chunks[0])
|
96
|
+
# "All of the keys rolled up in a common prefix count as
|
97
|
+
# a single return when calculating the number of
|
98
|
+
# returns. See MaxKeys."
|
99
|
+
# (http://awsdocs.s3.amazonaws.com/S3/latest/s3-api.pdf)
|
100
|
+
count += 1
|
101
|
+
if count <= max_keys
|
102
|
+
ms.common_prefixes << base_prefix + chunks[0] + delimiter
|
103
|
+
last_chunk = chunks[0]
|
104
|
+
else
|
105
|
+
is_truncated = true
|
106
|
+
break
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Continue to the next key, since this one has a
|
111
|
+
# delimiter.
|
112
|
+
next
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
count += 1
|
117
|
+
if count <= max_keys
|
118
|
+
ms.matches << s3_object
|
119
|
+
else
|
120
|
+
is_truncated = true
|
121
|
+
break
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
if marker and marker == s3_object.name
|
126
|
+
marker_found = true
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
if pseudo
|
131
|
+
@sorted_set.delete(pseudo)
|
132
|
+
end
|
133
|
+
|
134
|
+
return ms
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
data/lib/fakes3/util.rb
ADDED
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'builder'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module FakeS3
|
5
|
+
class XmlAdapter
|
6
|
+
def self.buckets(bucket_objects)
|
7
|
+
output = ""
|
8
|
+
xml = Builder::XmlMarkup.new(:target => output)
|
9
|
+
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
|
10
|
+
xml.ListAllMyBucketsResult(:xmlns => "http://s3.amazonaws.com/doc/2006-03-01/") { |lam|
|
11
|
+
lam.Owner { |owner|
|
12
|
+
owner.ID("123")
|
13
|
+
owner.DisplayName("FakeS3")
|
14
|
+
}
|
15
|
+
lam.Buckets { |buckets|
|
16
|
+
bucket_objects.each do |bucket|
|
17
|
+
buckets.Bucket do |b|
|
18
|
+
b.Name(bucket.name)
|
19
|
+
b.CreationDate(bucket.creation_date.strftime("%Y-%m-%dT%H:%M:%S.000Z"))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
}
|
23
|
+
}
|
24
|
+
output
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.error(error)
|
28
|
+
output = ""
|
29
|
+
xml = Builder::XmlMarkup.new(:target => output)
|
30
|
+
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
|
31
|
+
xml.Error { |err|
|
32
|
+
err.Code(error.code)
|
33
|
+
err.Message(error.message)
|
34
|
+
err.Resource(error.resource)
|
35
|
+
err.RequestId(1)
|
36
|
+
}
|
37
|
+
output
|
38
|
+
end
|
39
|
+
|
40
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
41
|
+
#<Error>
|
42
|
+
# <Code>NoSuchKey</Code>
|
43
|
+
# <Message>The resource you requested does not exist</Message>
|
44
|
+
# <Resource>/mybucket/myfoto.jpg</Resource>
|
45
|
+
# <RequestId>4442587FB7D0A2F9</RequestId>
|
46
|
+
#</Error>
|
47
|
+
#
|
48
|
+
def self.error_no_such_bucket(name)
|
49
|
+
output = ""
|
50
|
+
xml = Builder::XmlMarkup.new(:target => output)
|
51
|
+
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
|
52
|
+
xml.Error { |err|
|
53
|
+
err.Code("NoSuchBucket")
|
54
|
+
err.Message("The resource you requested does not exist")
|
55
|
+
err.Resource(name)
|
56
|
+
err.RequestId(1)
|
57
|
+
}
|
58
|
+
output
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.error_bucket_not_empty(name)
|
62
|
+
output = ""
|
63
|
+
xml = Builder::XmlMarkup.new(:target => output)
|
64
|
+
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
|
65
|
+
xml.Error { |err|
|
66
|
+
err.Code("BucketNotEmpty")
|
67
|
+
err.Message("The bucket you tried to delete is not empty.")
|
68
|
+
err.Resource(name)
|
69
|
+
err.RequestId(1)
|
70
|
+
}
|
71
|
+
output
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.error_no_such_key(name)
|
75
|
+
output = ""
|
76
|
+
xml = Builder::XmlMarkup.new(:target => output)
|
77
|
+
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
|
78
|
+
xml.Error { |err|
|
79
|
+
err.Code("NoSuchKey")
|
80
|
+
err.Message("The specified key does not exist")
|
81
|
+
err.Key(name)
|
82
|
+
err.RequestId(1)
|
83
|
+
err.HostId(2)
|
84
|
+
}
|
85
|
+
output
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.bucket(bucket)
|
89
|
+
output = ""
|
90
|
+
xml = Builder::XmlMarkup.new(:target => output)
|
91
|
+
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
|
92
|
+
xml.ListBucketResult(:xmlns => "http://s3.amazonaws.com/doc/2006-03-01/") { |lbr|
|
93
|
+
lbr.Name(bucket.name)
|
94
|
+
lbr.Prefix
|
95
|
+
lbr.Marker
|
96
|
+
lbr.MaxKeys("1000")
|
97
|
+
lbr.IsTruncated("false")
|
98
|
+
}
|
99
|
+
output
|
100
|
+
end
|
101
|
+
|
102
|
+
# A bucket query gives back the bucket along with contents
|
103
|
+
# <Contents>
|
104
|
+
#<Key>Nelson</Key>
|
105
|
+
# <LastModified>2006-01-01T12:00:00.000Z</LastModified>
|
106
|
+
# <ETag>"828ef3fdfa96f00ad9f27c383fc9ac7f"</ETag>
|
107
|
+
# <Size>5</Size>
|
108
|
+
# <StorageClass>STANDARD</StorageClass>
|
109
|
+
# <Owner>
|
110
|
+
# <ID>bcaf161ca5fb16fd081034f</ID>
|
111
|
+
# <DisplayName>webfile</DisplayName>
|
112
|
+
# </Owner>
|
113
|
+
# </Contents>
|
114
|
+
|
115
|
+
def self.append_objects_to_list_bucket_result(lbr,objects)
|
116
|
+
return if objects.nil? or objects.size == 0
|
117
|
+
|
118
|
+
if objects.index(nil)
|
119
|
+
require 'ruby-debug'
|
120
|
+
Debugger.start
|
121
|
+
debugger
|
122
|
+
end
|
123
|
+
|
124
|
+
objects.each do |s3_object|
|
125
|
+
lbr.Contents { |contents|
|
126
|
+
contents.Key(s3_object.name)
|
127
|
+
contents.LastModified(s3_object.modified_date)
|
128
|
+
contents.ETag("\"#{s3_object.md5}\"")
|
129
|
+
contents.Size(s3_object.size)
|
130
|
+
contents.StorageClass("STANDARD")
|
131
|
+
|
132
|
+
contents.Owner { |owner|
|
133
|
+
owner.ID("abc")
|
134
|
+
owner.DisplayName("You")
|
135
|
+
}
|
136
|
+
}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.append_common_prefixes_to_list_bucket_result(lbr, prefixes)
|
141
|
+
return if prefixes.nil? or prefixes.size == 0
|
142
|
+
|
143
|
+
prefixes.each do |common_prefix|
|
144
|
+
lbr.CommonPrefixes { |contents| contents.Prefix(common_prefix) }
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.bucket_query(bucket_query)
|
149
|
+
output = ""
|
150
|
+
bucket = bucket_query.bucket
|
151
|
+
xml = Builder::XmlMarkup.new(:target => output)
|
152
|
+
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
|
153
|
+
xml.ListBucketResult(:xmlns => "http://s3.amazonaws.com/doc/2006-03-01/") { |lbr|
|
154
|
+
lbr.Name(bucket.name)
|
155
|
+
lbr.Prefix(bucket_query.prefix)
|
156
|
+
lbr.Marker(bucket_query.marker)
|
157
|
+
lbr.MaxKeys(bucket_query.max_keys)
|
158
|
+
lbr.IsTruncated(bucket_query.is_truncated?)
|
159
|
+
append_objects_to_list_bucket_result(lbr,bucket_query.matches)
|
160
|
+
append_common_prefixes_to_list_bucket_result(lbr, bucket_query.common_prefixes)
|
161
|
+
}
|
162
|
+
output
|
163
|
+
end
|
164
|
+
|
165
|
+
# ACL xml
|
166
|
+
def self.acl(object = nil)
|
167
|
+
output = ""
|
168
|
+
xml = Builder::XmlMarkup.new(:target => output)
|
169
|
+
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
|
170
|
+
xml.AccessControlPolicy(:xmlns => "http://s3.amazonaws.com/doc/2006-03-01/") { |acp|
|
171
|
+
acp.Owner do |owner|
|
172
|
+
owner.ID("abc")
|
173
|
+
owner.DisplayName("You")
|
174
|
+
end
|
175
|
+
acp.AccessControlList do |acl|
|
176
|
+
acl.Grant do |grant|
|
177
|
+
grant.Grantee("xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance", "xsi:type" => "CanonicalUser") do |grantee|
|
178
|
+
grantee.ID("abc")
|
179
|
+
grantee.DisplayName("You")
|
180
|
+
end
|
181
|
+
grant.Permission("FULL_CONTROL")
|
182
|
+
end
|
183
|
+
end
|
184
|
+
}
|
185
|
+
output
|
186
|
+
end
|
187
|
+
|
188
|
+
# <CopyObjectResult>
|
189
|
+
# <LastModified>2009-10-28T22:32:00</LastModified>
|
190
|
+
# <ETag>"9b2cf535f27731c974343645a3985328"</ETag>
|
191
|
+
# </CopyObjectResult>
|
192
|
+
def self.copy_object_result(object)
|
193
|
+
output = ""
|
194
|
+
xml = Builder::XmlMarkup.new(:target => output)
|
195
|
+
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
|
196
|
+
xml.CopyObjectResult { |result|
|
197
|
+
result.LastModified(object.modified_date)
|
198
|
+
result.ETag("\"#{object.md5}\"")
|
199
|
+
}
|
200
|
+
output
|
201
|
+
end
|
202
|
+
|
203
|
+
# <CompleteMultipartUploadResult>
|
204
|
+
# <Location>http://Example-Bucket.s3.amazonaws.com/Example-Object</Location>
|
205
|
+
# <Bucket>Example-Bucket</Bucket>
|
206
|
+
# <Key>Example-Object</Key>
|
207
|
+
# <ETag>"3858f62230ac3c915f300c664312c11f-9"</ETag>
|
208
|
+
# </CompleteMultipartUploadResult>
|
209
|
+
def self.complete_multipart_result(object)
|
210
|
+
output = ""
|
211
|
+
xml = Builder::XmlMarkup.new(:target => output)
|
212
|
+
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
|
213
|
+
xml.CompleteMultipartUploadResult { |result|
|
214
|
+
result.Location("TODO: implement")
|
215
|
+
result.Bucket("TODO: implement")
|
216
|
+
result.Key(object.name)
|
217
|
+
result.ETag("\"#{object.md5}\"")
|
218
|
+
}
|
219
|
+
output
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'xmlsimple'
|
2
|
+
|
3
|
+
module FakeS3
|
4
|
+
class XmlParser
|
5
|
+
def self.delete_objects(request)
|
6
|
+
keys = []
|
7
|
+
|
8
|
+
objects = XmlSimple.xml_in(request.body, {'NoAttr' => true})['Object']
|
9
|
+
objects.each do |key|
|
10
|
+
keys << key['Key'][0]
|
11
|
+
end
|
12
|
+
|
13
|
+
keys
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/static/button.svg
ADDED
@@ -0,0 +1,4 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
3
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="110px" height="20px" viewBox="0 0 110 20" enable-background="new 0 0 110 20" xml:space="preserve"> <image id="image0" width="110" height="20" x="0" y="0" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAG4AAAAUCAMAAABbPPhuAAAABGdBTUEAALGPC/xhBQAAACBjSFJN AAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAABL1BMVEUAUJX///8AUJUAUJUA UJUAUJUAUJUAUJUAUJUAUJUAUJUAUJUAUJUAUJUAUJUAUJUAUJUAUJUAUJUAUJUAUJUAUJUAUJUA UJUAUJUAUJUAUJUAUJUAUJUAUJU5e7Rmns59r9uAs917sNx6r9yDtN5lnc0vdK9EhLtzq9o/i8wp fsYhecQdd8Mke8U9iss6fLU4e7RyqtougccfeMNQjcx0otRjmNAhesQzhMliodVKib8+i8zT4PDI 2OyTtdyvx+ShvuD///8qf8dOlNCAstwlfMU7fbZ/st0gecQ5g8e7z+iErNibw+QyhMnq8Pj19/vf 6PTG3fAwg8jm8PhNlND5+/12rNs0hcluqNh3rdtNisDn8fl5rtvH3fBnns4xdbCcw+VhoNU5fLVo oM99sNsds+t8AAAAHXRSTlMAAAZux/NbG8+ZCNAobZoK+dNcn/VgLF+dB2/L9Kp1EagAAAABYktH RAH/Ai3eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AsECgU1BHGi1AAAAmZJREFUSMe9 lgtT1DAQxytwgoLC+X4BeiegGEUkCXqXtgkomlabqvh+P77/Z3A3SXst5Wa4cY6duUmbbPaX/HeT XhAEE5NTrcXx2ckTFQuC6Zml5Zu3Ot3x2O2V1VOnK7iJmbU7d9fvkTHZ/Qedjdm5Ae7Mw81HW0ef Thkfjbf9+EmvVQoaTC33HU2ghdGQWZGIbSuVGHF/Wzu7K6tn5z1u4em661bO4sMnFRgmo6NRKvas 39lrO0GDxecvChzDqMpFjCTjUiJH0gZOy1BIDS+JbcBXigTfQxFTHBZJ6oK+tEWx/QoEPedwHVLB pSpzkYWSVClKuDK13WGrc5RBkAgbo8HXwENOKL5nfli6oK/flIKen0dct8TlAibyEkdC+MUqaeAS lVMdc21UCj4J+GYMZOESMsEYumiOS7VB3+57QbsoaBXnc1fiUlhxrngDlykrFQwzwGTWF8VhsGWQ OleSsVxFPui7965CP3zsXbhYxYGYFEKVOIjLIFoDZz1tnq2VOCJB1IQY1y0L3KcC97mBw6XhgoEk MWDmc1DD2fCY1gzKQ5MBjpLUKI45wG4X9MuuF/PrXvtSrVQgdxmkIIWn3K4Oc08LnFsy4sAhiQ3m TEgByS5w0sSwP8qUiXHYBv3mS2X/+w9XKoODgHUmIC2h5eG2hApJE0ci0MtERAvoCXWJSzOnoR12 J+GnPwi/eq3L9WNeMc7cGYt9wskwD830gW4nBq/ddL4q65dY02CXIflvs2fuSnGJTS4Nv6L5iNfx YXbgir46u7bZH+cHqLvRnqt9Xn//+Xtsn1f483Dt+o3j+vPwD8d6fpTeP3ACAAAAJXRFWHRkYXRl OmNyZWF0ZQAyMDE2LTExLTA0VDEwOjA1OjUzLTA3OjAwJ5wXuQAAACV0RVh0ZGF0ZTptb2RpZnkA MjAxNi0xMS0wNFQxMDowNTo1My0wNzowMFbBrwUAAAAASUVORK5CYII="/>
|
4
|
+
</svg>
|
data/static/logo.png
ADDED
Binary file
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
require 'aws-sdk-v1'
|
3
|
+
require 'rest-client'
|
4
|
+
|
5
|
+
class AwsSdkCommandsTest < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@s3 = AWS::S3.new(:access_key_id => '123',
|
8
|
+
:secret_access_key => 'abc',
|
9
|
+
:s3_endpoint => 'localhost',
|
10
|
+
:s3_port => 10453,
|
11
|
+
:use_ssl => false)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_copy_to
|
15
|
+
bucket = @s3.buckets["test_copy_to"]
|
16
|
+
object = bucket.objects["key1"]
|
17
|
+
object.write("asdf")
|
18
|
+
|
19
|
+
assert object.exists?
|
20
|
+
object.copy_to("key2")
|
21
|
+
|
22
|
+
assert_equal 2, bucket.objects.count
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_multipart_upload
|
26
|
+
bucket = @s3.buckets["test_multipart_upload"]
|
27
|
+
object = bucket.objects["key1"]
|
28
|
+
object.write("thisisaverybigfile", :multipart_threshold => 5)
|
29
|
+
assert object.exists?
|
30
|
+
assert_equal "thisisaverybigfile", object.read
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_metadata
|
34
|
+
file_path = './test_root/test_metadata/metaobject'
|
35
|
+
FileUtils.rm_rf file_path
|
36
|
+
|
37
|
+
bucket = @s3.buckets["test_metadata"]
|
38
|
+
object = bucket.objects["metaobject"]
|
39
|
+
object.write(
|
40
|
+
'data',
|
41
|
+
# this is sent as header x-amz-storage-class
|
42
|
+
:storage_class => 'REDUCED_REDUNDANCY',
|
43
|
+
# this is sent as header x-amz-meta-custom1
|
44
|
+
:metadata => {
|
45
|
+
"custom1" => "foobar"
|
46
|
+
}
|
47
|
+
)
|
48
|
+
assert object.exists?
|
49
|
+
metadata_file = YAML.load(IO.read("#{file_path}/.fakes3_metadataFFF/metadata"))
|
50
|
+
|
51
|
+
assert metadata_file.has_key?(:custom_metadata), 'Metadata file does not contain a :custom_metadata key'
|
52
|
+
assert metadata_file[:custom_metadata].has_key?('custom1'), ':custom_metadata does not contain field "custom1"'
|
53
|
+
assert_equal 'foobar', metadata_file[:custom_metadata]['custom1'], '"custom1" does not equal expected value "foobar"'
|
54
|
+
|
55
|
+
assert metadata_file.has_key?(:amazon_metadata), 'Metadata file does not contain an :amazon_metadata key'
|
56
|
+
assert metadata_file[:amazon_metadata].has_key?('storage-class'), ':amazon_metadata does not contain field "storage-class"'
|
57
|
+
assert_equal 'REDUCED_REDUNDANCY', metadata_file[:amazon_metadata]['storage-class'], '"storage-class" does not equal expected value "REDUCED_REDUNDANCY"'
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_content_disposition
|
61
|
+
bucket = @s3.buckets["test_bucket"]
|
62
|
+
bucket.objects.create("test_object", "asdf", :content_disposition => "application/test")
|
63
|
+
assert_equal "application/test", content_disposition("test_bucket", "test_object")
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_content_disposition_copy
|
67
|
+
bucket = @s3.buckets["test_bucket"]
|
68
|
+
object = bucket.objects.create("test_object", "asdf", :content_disposition => "application/test")
|
69
|
+
object.copy_to("test_copy_object")
|
70
|
+
assert_equal "application/test", content_disposition("test_bucket", "test_copy_object")
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_content_disposition_request_parameter
|
74
|
+
bucket = @s3.buckets["test_bucket"]
|
75
|
+
object = bucket.objects.create("test_object", "asdf")
|
76
|
+
url = object.url_for(:read, :response_content_disposition => "application/test", :signature_version => :v4)
|
77
|
+
assert_equal "application/test", response_header(url, :content_disposition)
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_content_type_request_parameter
|
81
|
+
bucket = @s3.buckets["test_bucket"]
|
82
|
+
object = bucket.objects.create("test_object", "asdf")
|
83
|
+
url = object.url_for(:read, :response_content_type => "application/test", :signature_version => :v4)
|
84
|
+
assert_equal "application/test", response_header(url, :content_type)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Unfortunately v1 of the AWS SDK doesn't support reading the content_disposition of an object
|
88
|
+
def content_disposition(bucket_name, key)
|
89
|
+
url = "http://localhost:#{@s3.client.port}/#{bucket_name}/#{key}"
|
90
|
+
response_header(url, :content_disposition)
|
91
|
+
end
|
92
|
+
|
93
|
+
def response_header(url, header_name)
|
94
|
+
RestClient.head(url.to_s) do |response|
|
95
|
+
response.headers[header_name]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|