fake_aws 0.0.2 → 0.0.3
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +2 -0
- data/README.md +24 -16
- data/fake_aws.gemspec +4 -1
- data/lib/fake_aws.rb +10 -2
- data/lib/fake_aws/error.rb +16 -0
- data/lib/fake_aws/s3/bucket_on_disk.rb +26 -0
- data/lib/fake_aws/s3/error_index.rb +37 -0
- data/lib/fake_aws/s3/object_on_disk.rb +50 -0
- data/lib/fake_aws/s3/operations/get_object.rb +30 -23
- data/lib/fake_aws/s3/operations/put_bucket.rb +38 -0
- data/lib/fake_aws/s3/operations/put_object.rb +22 -36
- data/lib/fake_aws/s3/rack_app.rb +19 -6
- data/lib/fake_aws/s3/request.rb +100 -0
- data/lib/fake_aws/s3/responses/common.rb +37 -0
- data/lib/fake_aws/s3/responses/empty.rb +25 -0
- data/lib/fake_aws/s3/responses/error.rb +50 -0
- data/lib/fake_aws/s3/responses/success.rb +31 -0
- data/lib/fake_aws/version.rb +1 -1
- data/spec/integration/s3/get_object_spec.rb +35 -12
- data/spec/integration/s3/put_bucket_spec.rb +40 -0
- data/spec/integration/s3/put_object_spec.rb +12 -4
- data/spec/integration/s3/request_styles_spec.rb +54 -0
- data/spec/spec_helper.rb +1 -16
- data/spec/support/common_header_examples.rb +16 -0
- data/spec/support/s3_integration_helpers.rb +18 -0
- data/spec/support/xml_parsing_helper.rb +7 -0
- data/spec/unit/s3/bucket_on_disk_spec.rb +39 -0
- data/spec/unit/s3/error_index_spec.rb +17 -0
- data/spec/unit/s3/object_on_disk_spec.rb +82 -0
- data/spec/unit/s3/request_spec.rb +92 -0
- data/spec/unit/s3/responses/empty_spec.rb +14 -0
- data/spec/unit/s3/responses/error_spec.rb +49 -0
- data/spec/unit/s3/responses/success_spec.rb +25 -0
- metadata +82 -26
- data/lib/fake_aws/s3/object_store.rb +0 -66
- data/lib/fake_aws/s3/xml_error_response.rb +0 -29
- data/spec/unit/s3/object_store_spec.rb +0 -75
- data/spec/unit/s3/xml_error_response_spec.rb +0 -9
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Request styles:" do
|
4
|
+
include S3IntegrationHelpers
|
5
|
+
|
6
|
+
let(:bucket) { "mah-bucket" }
|
7
|
+
let(:file_name) { "mah-file.txt"}
|
8
|
+
let(:file_contents) { "Hello, world!" }
|
9
|
+
|
10
|
+
before do
|
11
|
+
FileUtils.mkdir(File.join(s3_path, bucket))
|
12
|
+
File.write(File.join(s3_path, bucket, file_name), file_contents)
|
13
|
+
end
|
14
|
+
|
15
|
+
shared_examples "GET Object" do
|
16
|
+
it "returns a 200" do
|
17
|
+
response = get_example_file(file_name)
|
18
|
+
expect(response.status).to eq(200)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "returns the contents of the file" do
|
22
|
+
response = get_example_file(file_name)
|
23
|
+
expect(response.body).to eq(file_contents)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "path-style GET Object" do
|
28
|
+
def get_example_file(key)
|
29
|
+
connection.get("http://s3.amazonaws.com/#{bucket}/#{key}")
|
30
|
+
end
|
31
|
+
|
32
|
+
include_examples "GET Object"
|
33
|
+
end
|
34
|
+
|
35
|
+
context "virtual hosted-style GET Object" do
|
36
|
+
def get_example_file(key)
|
37
|
+
connection.get("http://#{bucket}.s3.amazonaws.com/#{key}")
|
38
|
+
end
|
39
|
+
|
40
|
+
include_examples "GET Object"
|
41
|
+
end
|
42
|
+
|
43
|
+
context "CNAME-style GET Object" do
|
44
|
+
let(:bucket) { "mah-bucket.mah-domain.com" }
|
45
|
+
|
46
|
+
def get_example_file(key)
|
47
|
+
connection.get("http://#{bucket}/#{key}")
|
48
|
+
end
|
49
|
+
|
50
|
+
include_examples "GET Object"
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
data/spec/spec_helper.rb
CHANGED
@@ -1,20 +1,5 @@
|
|
1
1
|
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
2
|
require 'fake_aws'
|
3
|
-
require 'faraday'
|
4
3
|
|
5
|
-
|
6
|
-
def self.included(base)
|
7
|
-
base.let(:s3_path) { "tmp" }
|
4
|
+
Dir["./spec/support/**/*.rb"].sort.each {|f| require f}
|
8
5
|
|
9
|
-
base.let(:connection) do
|
10
|
-
Faraday.new do |connection|
|
11
|
-
connection.adapter :rack, FakeAWS::S3::RackApp.new(s3_path)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
base.before do
|
16
|
-
FileUtils.rm_r(s3_path) rescue Errno::ENOENT
|
17
|
-
FileUtils.mkdir(s3_path)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Headers that should exist in every S3 response.
|
2
|
+
shared_examples "common response headers" do
|
3
|
+
it "has a Date header" do
|
4
|
+
time = Time.parse("2013-11-18 17:45")
|
5
|
+
allow(Time).to receive(:now).and_return(time)
|
6
|
+
expect(subject.headers["Date"]).to eq(time.httpdate)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "has a Server header" do
|
10
|
+
expect(subject.headers["Server"]).to eq("AmazonS3")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "has a request ID" do
|
14
|
+
expect(subject.headers["x-amz-request-id"]).to_not be_blank
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
module S3IntegrationHelpers
|
4
|
+
def self.included(base)
|
5
|
+
base.let(:s3_path) { "tmp" }
|
6
|
+
|
7
|
+
base.let(:connection) do
|
8
|
+
Faraday.new("http://s3.amazonaws.com") do |connection|
|
9
|
+
connection.adapter :rack, FakeAWS::S3::RackApp.new(s3_path)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
base.before do
|
14
|
+
FileUtils.rm_r(s3_path) rescue Errno::ENOENT
|
15
|
+
FileUtils.mkdir(s3_path)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FakeAWS::S3::BucketOnDisk do
|
4
|
+
let(:root_directory) { "tmp" }
|
5
|
+
let(:bucket) { "mah-bucket" }
|
6
|
+
|
7
|
+
subject { described_class.new(root_directory, bucket) }
|
8
|
+
|
9
|
+
let(:bucket_path) { "tmp/mah-bucket" }
|
10
|
+
|
11
|
+
before do
|
12
|
+
FileUtils.rm_r(root_directory) rescue Errno::ENOENT
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#exists?" do
|
16
|
+
it "returns true if the bucket directory exists" do
|
17
|
+
FileUtils.mkdir_p(bucket_path)
|
18
|
+
expect(subject.exists?).to be_truthy
|
19
|
+
end
|
20
|
+
|
21
|
+
it "returns false if the bucket directory doesn't exist" do
|
22
|
+
expect(subject.exists?).to be_falsy
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#create" do
|
27
|
+
it "creates the bucket directory" do
|
28
|
+
subject.create
|
29
|
+
expect(Dir.exists?(bucket_path)).to be_truthy
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#path" do
|
34
|
+
it "returns the path to the bucket" do
|
35
|
+
expect(subject.path).to eq(bucket_path)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FakeAWS::S3::ErrorIndex do
|
4
|
+
|
5
|
+
context ".error_for_code" do
|
6
|
+
it "returns something containing a description and status code for a known error" do
|
7
|
+
error = subject.error_for_code("NoSuchKey")
|
8
|
+
expect(error.description).to eq("The specified key does not exist.")
|
9
|
+
expect(error.status_code).to eq(404)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "blows up if given an unknown error code" do
|
13
|
+
expect { subject.error_for_code("NotARealCode") }.to raise_error(FakeAWS::UnknownResponseErrorCode)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FakeAWS::S3::ObjectOnDisk do
|
4
|
+
let(:key) { "/mah-file.txt" }
|
5
|
+
|
6
|
+
let(:bucket_path) { "tmp/mah-bucket" }
|
7
|
+
let(:object_path) { "#{bucket_path}#{key}" }
|
8
|
+
let(:metadata_path) { "#{object_path}.metadata.json" }
|
9
|
+
|
10
|
+
let(:bucket_on_disk) { double(:path => bucket_path) }
|
11
|
+
|
12
|
+
subject { described_class.new(bucket_on_disk, key) }
|
13
|
+
|
14
|
+
before do
|
15
|
+
FileUtils.rm_r(bucket_path) rescue Errno::ENOENT
|
16
|
+
FileUtils.mkdir_p(bucket_path)
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#exists?" do
|
20
|
+
it "returns true if the object file exists" do
|
21
|
+
File.write(object_path, "Hello, world!")
|
22
|
+
expect(subject.exists?).to be_truthy
|
23
|
+
end
|
24
|
+
|
25
|
+
it "returns false if the object file doesn't exist" do
|
26
|
+
expect(subject.exists?).to be_falsy
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#write" do
|
31
|
+
it "writes the content to the object file" do
|
32
|
+
subject.write("Hello, world!", { "bunnies" => "scary" })
|
33
|
+
|
34
|
+
expect(File.read(object_path)).to eq("Hello, world!")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "writes the metadata to the metadata file as JSON" do
|
38
|
+
subject.write("Hello, world!", { "bunnies" => "scary" })
|
39
|
+
|
40
|
+
expect(File.read(metadata_path)).to eq('{"bunnies":"scary"}')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#read_content" do
|
45
|
+
it "reads the contents of the object file" do
|
46
|
+
File.write(object_path, "Hello, world!")
|
47
|
+
|
48
|
+
expect(subject.read_content.read).to eq("Hello, world!")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#read_metadata" do
|
53
|
+
it "returns an empty hash if there's no metadata file" do
|
54
|
+
expect(subject.read_metadata).to eq({})
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns the JSON from the metadata file converted to a hash" do
|
58
|
+
File.write(metadata_path, '{"bunnies":"scary"}')
|
59
|
+
|
60
|
+
expect(subject.read_metadata).to eq("bunnies" => "scary")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#object_path" do
|
65
|
+
it "returns the path to the object" do
|
66
|
+
expect(subject.object_path).to eq(object_path)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#metadata_path" do
|
71
|
+
it "returns the path to the metadata" do
|
72
|
+
expect(subject.metadata_path).to eq(metadata_path)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#directory_path" do
|
77
|
+
it "returns the path to the directory containing the object" do
|
78
|
+
expect(subject.directory_path).to eq(bucket_path)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FakeAWS::S3::Request do
|
4
|
+
subject { described_class.new(env) }
|
5
|
+
|
6
|
+
context "#method" do
|
7
|
+
it "returns the request method" do
|
8
|
+
request = described_class.new("REQUEST_METHOD" => "GET")
|
9
|
+
expect(request.method).to eq("GET")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "#content_type" do
|
14
|
+
it "returns the content type" do
|
15
|
+
request = described_class.new("CONTENT_TYPE" => "text/plain")
|
16
|
+
expect(request.content_type).to eq("text/plain")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "#content" do
|
21
|
+
it "reads and returns the Rack input" do
|
22
|
+
rack_input = double("rack.input")
|
23
|
+
expect(rack_input).to receive(:read) { "foo" }
|
24
|
+
request = described_class.new("rack.input" => rack_input)
|
25
|
+
expect(request.content).to eq("foo")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "#http_headers" do
|
30
|
+
it "returns the HTTP headers" do
|
31
|
+
request = described_class.new("HTTP_X_FOO" => "foo", "HTTP_X_BAR" => "bar")
|
32
|
+
expect(request.http_headers).to eq("x-foo" => "foo", "x-bar" => "bar")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "ignores non-HTTP headers" do
|
36
|
+
request = described_class.new("FOO" => "foo")
|
37
|
+
expect(request.http_headers).to eq({})
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
shared_examples "request parsing" do
|
42
|
+
context "bucket request" do
|
43
|
+
let(:key) { "/" }
|
44
|
+
|
45
|
+
it "extracts the bucket" do
|
46
|
+
expect(request.bucket).to eq(bucket)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "has no key" do
|
50
|
+
expect(request.has_key?).to be_falsy
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "object request" do
|
55
|
+
let(:key) { "/mah-object.txt" }
|
56
|
+
|
57
|
+
it "extracts the bucket" do
|
58
|
+
expect(request.bucket).to eq(bucket)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "has a key" do
|
62
|
+
expect(request.has_key?).to be_truthy
|
63
|
+
end
|
64
|
+
|
65
|
+
it "extracts the key" do
|
66
|
+
expect(request.key).to eq(key)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "path-style" do
|
72
|
+
let(:bucket) { "mah-bucket" }
|
73
|
+
subject(:request) { described_class.new("SERVER_NAME" => "s3.amazonaws.com", "PATH_INFO" => "/#{bucket}#{key}") }
|
74
|
+
|
75
|
+
include_examples "request parsing"
|
76
|
+
end
|
77
|
+
|
78
|
+
context "virtual hosted-style" do
|
79
|
+
let(:bucket) { "mah-bucket" }
|
80
|
+
subject(:request) { described_class.new("SERVER_NAME" => "#{bucket}.s3.amazonaws.com", "PATH_INFO" => key) }
|
81
|
+
|
82
|
+
include_examples "request parsing"
|
83
|
+
end
|
84
|
+
|
85
|
+
context "CNAME-style" do
|
86
|
+
let(:bucket) { "mah-bucket.mah-domain.com" }
|
87
|
+
subject(:request) { described_class.new("SERVER_NAME" => bucket, "PATH_INFO" => key) }
|
88
|
+
|
89
|
+
include_examples "request parsing"
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FakeAWS::S3::Responses::Empty do
|
4
|
+
include_examples "common response headers"
|
5
|
+
|
6
|
+
it "has a status code of 200" do
|
7
|
+
expect(subject.status_code).to eq(200)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "has an empty body" do
|
11
|
+
expect(subject.body).to be_empty
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FakeAWS::S3::Responses::Error do
|
4
|
+
|
5
|
+
# Stub out looking up the error information:
|
6
|
+
let(:error_code) { "NoSuchKey" }
|
7
|
+
let(:error) { double(:description => "The specified key does not exist.", :status_code => 404) }
|
8
|
+
let(:error_index) { double(:error_for_code => error) }
|
9
|
+
before { stub_const("FakeAWS::S3::ErrorIndex", error_index) }
|
10
|
+
|
11
|
+
let(:resource) { "/mah-bucket/mah-object.txt" }
|
12
|
+
|
13
|
+
subject { described_class.new(error_code, "Resource" => resource) }
|
14
|
+
|
15
|
+
include_examples "common response headers"
|
16
|
+
|
17
|
+
it "has the right status code" do
|
18
|
+
expect(subject.status_code).to eq(error.status_code)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "has a content type of XML" do
|
22
|
+
expect(subject.headers["Content-Type"]).to eq("application/xml")
|
23
|
+
end
|
24
|
+
|
25
|
+
context "body" do
|
26
|
+
include XMLParsingHelper
|
27
|
+
|
28
|
+
let(:parsed_body) { parse_xml(subject.body) }
|
29
|
+
|
30
|
+
it "contains the right code" do
|
31
|
+
expect(parsed_body["Error"]["Code"]).to eq(error_code)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "contains the right message" do
|
35
|
+
expect(parsed_body["Error"]["Message"]).to eq(error.description)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "contains any additional fields passed in" do
|
39
|
+
expect(parsed_body["Error"]["Resource"]).to eq(resource)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "contains the right request ID" do
|
43
|
+
expect(parsed_body["Error"]["RequestId"]).to eq(subject.headers["x-amz-request-id"])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FakeAWS::S3::Responses::Success do
|
4
|
+
|
5
|
+
let(:headers) { { "Content-Type" => "text/plain" } }
|
6
|
+
let(:body) { "Hello, world!" }
|
7
|
+
|
8
|
+
subject { described_class.new(headers, body) }
|
9
|
+
|
10
|
+
include_examples "common response headers"
|
11
|
+
|
12
|
+
it "has a status code of 200" do
|
13
|
+
expect(subject.status_code).to eq(200)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has the right body" do
|
17
|
+
expect(subject.body).to eq(body)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "has the right content type" do
|
21
|
+
expect(subject.headers["Content-Type"]).to eq("text/plain")
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|