fakes3test6 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,4 @@
1
+ module FakeS3
2
+ class UnsupportedOperation < RuntimeError
3
+ end
4
+ end
@@ -0,0 +1,8 @@
1
+ module FakeS3
2
+ module Util
3
+ def Util.strip_before_and_after(string, strip_this)
4
+ regex_friendly_strip_this = Regexp.escape(strip_this)
5
+ string.gsub(/\A[#{regex_friendly_strip_this}]+|[#{regex_friendly_strip_this}]+\z/, '')
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module FakeS3
2
+ VERSION = "1.2.0"
3
+ end
@@ -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>&quot;828ef3fdfa96f00ad9f27c383fc9ac7f&quot;</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
@@ -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>
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