Merge branch 'master' into stable
This commit is contained in:
commit
2434c6e0b2
@ -1,7 +1,6 @@
|
|||||||
language: ruby
|
language: ruby
|
||||||
cache: bundler
|
cache: bundler
|
||||||
rvm:
|
rvm:
|
||||||
- 2.4
|
|
||||||
- 2.5
|
- 2.5
|
||||||
- 2.6
|
- 2.6
|
||||||
services:
|
services:
|
||||||
|
50
Gemfile.lock
50
Gemfile.lock
@ -1,46 +1,47 @@
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
activesupport (5.2.3)
|
activesupport (6.0.1)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
addressable (2.6.0)
|
zeitwerk (~> 2.2)
|
||||||
public_suffix (>= 2.0.2, < 4.0)
|
addressable (2.7.0)
|
||||||
|
public_suffix (>= 2.0.2, < 5.0)
|
||||||
backports (3.15.0)
|
backports (3.15.0)
|
||||||
concurrent-ruby (1.1.5)
|
concurrent-ruby (1.1.5)
|
||||||
crack (0.4.3)
|
crack (0.4.3)
|
||||||
safe_yaml (~> 1.0.0)
|
safe_yaml (~> 1.0.0)
|
||||||
domain_name (0.5.20190701)
|
domain_name (0.5.20190701)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
faraday (0.15.4)
|
faraday (0.17.0)
|
||||||
multipart-post (>= 1.2, < 3)
|
multipart-post (>= 1.2, < 3)
|
||||||
hashdiff (1.0.0)
|
hashdiff (1.0.0)
|
||||||
http-accept (1.7.0)
|
http-accept (1.7.0)
|
||||||
http-cookie (1.0.3)
|
http-cookie (1.0.3)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
i18n (1.6.0)
|
i18n (1.7.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
kgio (2.11.2)
|
kgio (2.11.2)
|
||||||
m (1.5.1)
|
m (1.5.1)
|
||||||
method_source (>= 0.6.7)
|
method_source (>= 0.6.7)
|
||||||
rake (>= 0.9.2.2)
|
rake (>= 0.9.2.2)
|
||||||
method_source (0.9.2)
|
method_source (0.9.2)
|
||||||
mime-types (3.2.2)
|
mime-types (3.3)
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2019.0331)
|
mime-types-data (3.2019.1009)
|
||||||
minitest (5.11.3)
|
minitest (5.13.0)
|
||||||
minitest-stub_any_instance (1.0.2)
|
minitest-stub_any_instance (1.0.2)
|
||||||
multi_json (1.13.1)
|
multi_json (1.14.1)
|
||||||
multipart-post (2.1.1)
|
multipart-post (2.1.1)
|
||||||
mustermann (1.0.3)
|
mustermann (1.0.3)
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
public_suffix (3.1.1)
|
public_suffix (4.0.1)
|
||||||
purdytest (2.0.0)
|
purdytest (2.0.0)
|
||||||
minitest (~> 5.5)
|
minitest (~> 5.5)
|
||||||
rack (2.0.7)
|
rack (2.0.8)
|
||||||
rack-protection (2.0.5)
|
rack-protection (2.0.7)
|
||||||
rack
|
rack
|
||||||
rack-test (1.1.0)
|
rack-test (1.1.0)
|
||||||
rack (>= 1.0, < 3)
|
rack (>= 1.0, < 3)
|
||||||
@ -49,30 +50,30 @@ GEM
|
|||||||
rack (>= 1.1, < 3.0)
|
rack (>= 1.1, < 3.0)
|
||||||
unicorn (~> 5.1)
|
unicorn (~> 5.1)
|
||||||
raindrops (0.19.0)
|
raindrops (0.19.0)
|
||||||
rake (12.3.3)
|
rake (13.0.1)
|
||||||
redis (4.1.2)
|
redis (4.1.3)
|
||||||
rest-client (2.1.0.rc1)
|
rest-client (2.1.0)
|
||||||
http-accept (>= 1.7.0, < 2.0)
|
http-accept (>= 1.7.0, < 2.0)
|
||||||
http-cookie (>= 1.0.2, < 2.0)
|
http-cookie (>= 1.0.2, < 2.0)
|
||||||
mime-types (>= 1.16, < 4.0)
|
mime-types (>= 1.16, < 4.0)
|
||||||
netrc (~> 0.8)
|
netrc (~> 0.8)
|
||||||
safe_yaml (1.0.5)
|
safe_yaml (1.0.5)
|
||||||
sentry-raven (2.11.0)
|
sentry-raven (2.12.3)
|
||||||
faraday (>= 0.7.6, < 1.0)
|
faraday (>= 0.7.6, < 1.0)
|
||||||
sinatra (2.0.5)
|
sinatra (2.0.7)
|
||||||
mustermann (~> 1.0)
|
mustermann (~> 1.0)
|
||||||
rack (~> 2.0)
|
rack (~> 2.0)
|
||||||
rack-protection (= 2.0.5)
|
rack-protection (= 2.0.7)
|
||||||
tilt (~> 2.0)
|
tilt (~> 2.0)
|
||||||
sinatra-contrib (2.0.5)
|
sinatra-contrib (2.0.7)
|
||||||
backports (>= 2.8.2)
|
backports (>= 2.8.2)
|
||||||
multi_json
|
multi_json
|
||||||
mustermann (~> 1.0)
|
mustermann (~> 1.0)
|
||||||
rack-protection (= 2.0.5)
|
rack-protection (= 2.0.7)
|
||||||
sinatra (= 2.0.5)
|
sinatra (= 2.0.7)
|
||||||
tilt (>= 1.3, < 3)
|
tilt (~> 2.0)
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
tilt (2.0.9)
|
tilt (2.0.10)
|
||||||
tzinfo (1.2.5)
|
tzinfo (1.2.5)
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
@ -81,10 +82,11 @@ GEM
|
|||||||
unicorn (5.5.1)
|
unicorn (5.5.1)
|
||||||
kgio (~> 2.6)
|
kgio (~> 2.6)
|
||||||
raindrops (~> 0.7)
|
raindrops (~> 0.7)
|
||||||
webmock (3.6.0)
|
webmock (3.7.6)
|
||||||
addressable (>= 2.3.6)
|
addressable (>= 2.3.6)
|
||||||
crack (>= 0.3.2)
|
crack (>= 0.3.2)
|
||||||
hashdiff (>= 0.4.0, < 2.0.0)
|
hashdiff (>= 0.4.0, < 2.0.0)
|
||||||
|
zeitwerk (2.2.1)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
@ -69,6 +69,11 @@ module RemoteStorage
|
|||||||
|
|
||||||
res = do_get_request(url)
|
res = do_get_request(url)
|
||||||
|
|
||||||
|
if res.headers[:content_range]
|
||||||
|
# Partial content
|
||||||
|
server.headers["Content-Range"] = res.headers[:content_range]
|
||||||
|
server.status 206
|
||||||
|
end
|
||||||
set_response_headers(metadata)
|
set_response_headers(metadata)
|
||||||
|
|
||||||
return res.body
|
return res.body
|
||||||
@ -113,6 +118,9 @@ module RemoteStorage
|
|||||||
end
|
end
|
||||||
|
|
||||||
def put_data(user, directory, key, data, content_type)
|
def put_data(user, directory, key, data, content_type)
|
||||||
|
# Do not try to perform the PUT request when the Content-Type does not
|
||||||
|
# look like a MIME type
|
||||||
|
server.halt 415 unless content_type.match(/^.+\/.+/i)
|
||||||
server.halt 400 if server.env["HTTP_CONTENT_RANGE"]
|
server.halt 400 if server.env["HTTP_CONTENT_RANGE"]
|
||||||
server.halt 409, "Conflict" if has_name_collision?(user, directory, key)
|
server.halt 409, "Conflict" if has_name_collision?(user, directory, key)
|
||||||
|
|
||||||
@ -405,7 +413,9 @@ module RemoteStorage
|
|||||||
|
|
||||||
def do_get_request(url, &block)
|
def do_get_request(url, &block)
|
||||||
deal_with_unauthorized_requests do
|
deal_with_unauthorized_requests do
|
||||||
RestClient.get(url, default_headers, &block)
|
headers = { }
|
||||||
|
headers["Range"] = server.env["HTTP_RANGE"] if server.env["HTTP_RANGE"]
|
||||||
|
RestClient.get(url, default_headers.merge(headers), &block)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -32,8 +32,10 @@ module RemoteStorage
|
|||||||
|
|
||||||
def do_get_request(url, &block)
|
def do_get_request(url, &block)
|
||||||
deal_with_unauthorized_requests do
|
deal_with_unauthorized_requests do
|
||||||
|
headers = { }
|
||||||
|
headers["Range"] = server.env["HTTP_RANGE"] if server.env["HTTP_RANGE"]
|
||||||
authorization_headers = authorization_headers_for("GET", url)
|
authorization_headers = authorization_headers_for("GET", url)
|
||||||
RestClient.get(url, authorization_headers, &block)
|
RestClient.get(url, authorization_headers.merge(headers), &block)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -68,8 +68,9 @@ class LiquorCabinet < Sinatra::Base
|
|||||||
before path do
|
before path do
|
||||||
headers 'Access-Control-Allow-Origin' => '*',
|
headers 'Access-Control-Allow-Origin' => '*',
|
||||||
'Access-Control-Allow-Methods' => 'GET, PUT, DELETE',
|
'Access-Control-Allow-Methods' => 'GET, PUT, DELETE',
|
||||||
'Access-Control-Allow-Headers' => 'Authorization, Content-Type, Origin, If-Match, If-None-Match',
|
'Access-Control-Allow-Headers' => 'Authorization, Content-Type, Origin, If-Match, If-None-Match, Range',
|
||||||
'Access-Control-Expose-Headers' => 'ETag, Content-Length'
|
'Access-Control-Expose-Headers' => 'ETag, Content-Length, Content-Range',
|
||||||
|
'Accept-Ranges' => 'bytes'
|
||||||
headers['Access-Control-Allow-Origin'] = env["HTTP_ORIGIN"] if env["HTTP_ORIGIN"]
|
headers['Access-Control-Allow-Origin'] = env["HTTP_ORIGIN"] if env["HTTP_ORIGIN"]
|
||||||
headers['Cache-Control'] = 'no-cache'
|
headers['Cache-Control'] = 'no-cache'
|
||||||
|
|
||||||
|
@ -16,12 +16,20 @@ describe "S3 provider" do
|
|||||||
stub_request(:put, "#{container_url_for("phil")}/food/aguacate").
|
stub_request(:put, "#{container_url_for("phil")}/food/aguacate").
|
||||||
with(body: "aye").
|
with(body: "aye").
|
||||||
to_return(status: 200, headers: { etag: '"0915etag"', date: "Fri, 04 Mar 2016 12:20:18 GMT" })
|
to_return(status: 200, headers: { etag: '"0915etag"', date: "Fri, 04 Mar 2016 12:20:18 GMT" })
|
||||||
|
stub_request(:put, "#{container_url_for("phil")}/public/shares/example.jpg").
|
||||||
|
to_return(status: 200, headers: { etag: '"0817etag"', content_type: "image/jpeg", date: "Fri, 04 Mar 2016 12:20:18 GMT" })
|
||||||
|
stub_request(:put, "#{container_url_for("phil")}/public/shares/example_partial.jpg").
|
||||||
|
to_return(status: 200, headers: { etag: '"0817etag"', content_type: "image/jpeg", date: "Fri, 04 Mar 2016 12:20:18 GMT" })
|
||||||
stub_request(:head, "#{container_url_for("phil")}/food/aguacate").
|
stub_request(:head, "#{container_url_for("phil")}/food/aguacate").
|
||||||
to_return(status: 200, headers: { last_modified: "Fri, 04 Mar 2016 12:20:18 GMT" })
|
to_return(status: 200, headers: { last_modified: "Fri, 04 Mar 2016 12:20:18 GMT" })
|
||||||
stub_request(:get, "#{container_url_for("phil")}/food/aguacate").
|
stub_request(:get, "#{container_url_for("phil")}/food/aguacate").
|
||||||
to_return(status: 200, body: "rootbody", headers: { etag: '"0817etag"', content_type: "text/plain; charset=utf-8" })
|
to_return(status: 200, body: "rootbody", headers: { etag: '"0817etag"', content_type: "text/plain; charset=utf-8" })
|
||||||
stub_request(:delete, "#{container_url_for("phil")}/food/aguacate").
|
stub_request(:delete, "#{container_url_for("phil")}/food/aguacate").
|
||||||
to_return(status: 200, headers: { etag: '"0815etag"' })
|
to_return(status: 200, headers: { etag: '"0815etag"' })
|
||||||
|
stub_request(:get, "#{container_url_for("phil")}/public/shares/example.jpg").
|
||||||
|
to_return(status: 200, body: "", headers: { etag: '"0817etag"', content_type: "image/jpeg" })
|
||||||
|
stub_request(:get, "#{container_url_for("phil")}/public/shares/example_partial.jpg").
|
||||||
|
to_return(status: 206, body: "", headers: { etag: '"0817etag"', content_type: "image/jpeg", content_range: "bytes 0-16/128" })
|
||||||
|
|
||||||
# Write new content to check the metadata in Redis
|
# Write new content to check the metadata in Redis
|
||||||
stub_request(:put, "#{container_url_for("phil")}/food/banano").
|
stub_request(:put, "#{container_url_for("phil")}/food/banano").
|
||||||
|
@ -13,7 +13,7 @@ shared_examples_for 'a REST adapter' do
|
|||||||
|
|
||||||
it "returns 404 on non-existing routes" do
|
it "returns 404 on non-existing routes" do
|
||||||
get "/virginmargarita"
|
get "/virginmargarita"
|
||||||
last_response.status.must_equal 404
|
_(last_response.status).must_equal 404
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "PUT requests" do
|
describe "PUT requests" do
|
||||||
@ -32,10 +32,10 @@ shared_examples_for 'a REST adapter' do
|
|||||||
put "/phil/food/aguacate", "si"
|
put "/phil/food/aguacate", "si"
|
||||||
|
|
||||||
metadata = redis.hgetall "rs:m:phil:food/aguacate"
|
metadata = redis.hgetall "rs:m:phil:food/aguacate"
|
||||||
metadata["s"].must_equal "2"
|
_(metadata["s"]).must_equal "2"
|
||||||
metadata["t"].must_equal "text/plain; charset=utf-8"
|
_(metadata["t"]).must_equal "text/plain; charset=utf-8"
|
||||||
metadata["e"].must_equal "0815etag"
|
_(metadata["e"]).must_equal "0815etag"
|
||||||
metadata["m"].length.must_equal 13
|
_(metadata["m"].length).must_equal 13
|
||||||
end
|
end
|
||||||
|
|
||||||
it "updates the metadata object in redis when it changes" do
|
it "updates the metadata object in redis when it changes" do
|
||||||
@ -43,10 +43,10 @@ shared_examples_for 'a REST adapter' do
|
|||||||
put "/phil/food/banano", "oh, no"
|
put "/phil/food/banano", "oh, no"
|
||||||
|
|
||||||
metadata = redis.hgetall "rs:m:phil:food/banano"
|
metadata = redis.hgetall "rs:m:phil:food/banano"
|
||||||
metadata["s"].must_equal "6"
|
_(metadata["s"]).must_equal "6"
|
||||||
metadata["t"].must_equal "text/plain; charset=utf-8"
|
_(metadata["t"]).must_equal "text/plain; charset=utf-8"
|
||||||
metadata["e"].must_equal "0817etag"
|
_(metadata["e"]).must_equal "0817etag"
|
||||||
metadata["m"].must_equal "1457094020000"
|
_(metadata["m"]).must_equal "1457094020000"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "creates the directory objects metadata in redis" do
|
it "creates the directory objects metadata in redis" do
|
||||||
@ -54,34 +54,34 @@ shared_examples_for 'a REST adapter' do
|
|||||||
put "/phil/food/camaron", "yummi"
|
put "/phil/food/camaron", "yummi"
|
||||||
|
|
||||||
metadata = redis.hgetall "rs:m:phil:/"
|
metadata = redis.hgetall "rs:m:phil:/"
|
||||||
metadata["e"].must_equal "fe2976909daaf074660981ab563fe65d"
|
_(metadata["e"]).must_equal "fe2976909daaf074660981ab563fe65d"
|
||||||
metadata["m"].length.must_equal 13
|
_(metadata["m"].length).must_equal 13
|
||||||
|
|
||||||
metadata = redis.hgetall "rs:m:phil:food/"
|
metadata = redis.hgetall "rs:m:phil:food/"
|
||||||
metadata["e"].must_equal "926f98ff820f2f9764fd3c60a22865ad"
|
_(metadata["e"]).must_equal "926f98ff820f2f9764fd3c60a22865ad"
|
||||||
metadata["m"].length.must_equal 13
|
_(metadata["m"].length).must_equal 13
|
||||||
|
|
||||||
food_items = redis.smembers "rs:m:phil:food/:items"
|
food_items = redis.smembers "rs:m:phil:food/:items"
|
||||||
food_items.each do |food_item|
|
food_items.each do |food_item|
|
||||||
["camaron", "aguacate"].must_include food_item
|
_(["camaron", "aguacate"]).must_include food_item
|
||||||
end
|
end
|
||||||
|
|
||||||
root_items = redis.smembers "rs:m:phil:/:items"
|
root_items = redis.smembers "rs:m:phil:/:items"
|
||||||
root_items.must_equal ["food/"]
|
_(root_items).must_equal ["food/"]
|
||||||
end
|
end
|
||||||
|
|
||||||
context "response code" do
|
context "response code" do
|
||||||
it "is 201 for newly created objects" do
|
it "is 201 for newly created objects" do
|
||||||
put "/phil/food/aguacate", "ci"
|
put "/phil/food/aguacate", "ci"
|
||||||
|
|
||||||
last_response.status.must_equal 201
|
_(last_response.status).must_equal 201
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is 200 for updated objects" do
|
it "is 200 for updated objects" do
|
||||||
put "/phil/food/aguacate", "deliciosa"
|
put "/phil/food/aguacate", "deliciosa"
|
||||||
put "/phil/food/aguacate", "muy deliciosa"
|
put "/phil/food/aguacate", "muy deliciosa"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ shared_examples_for 'a REST adapter' do
|
|||||||
put "/phil/food/aguacate", "1234567890"
|
put "/phil/food/aguacate", "1234567890"
|
||||||
|
|
||||||
size_log = redis.get "rs:s:phil"
|
size_log = redis.get "rs:s:phil"
|
||||||
size_log.must_equal "10"
|
_(size_log).must_equal "10"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "logs the size difference when updating existing objects" do
|
it "logs the size difference when updating existing objects" do
|
||||||
@ -99,7 +99,7 @@ shared_examples_for 'a REST adapter' do
|
|||||||
put "/phil/food/aguacate", "123"
|
put "/phil/food/aguacate", "123"
|
||||||
|
|
||||||
size_log = redis.get "rs:s:phil"
|
size_log = redis.get "rs:s:phil"
|
||||||
size_log.must_equal "13"
|
_(size_log).must_equal "13"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -111,15 +111,15 @@ shared_examples_for 'a REST adapter' do
|
|||||||
it "are listed in the directory listing with all metadata" do
|
it "are listed in the directory listing with all metadata" do
|
||||||
get "phil/"
|
get "phil/"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
last_response.content_type.must_equal "application/ld+json"
|
_(last_response.content_type).must_equal "application/ld+json"
|
||||||
|
|
||||||
content = JSON.parse(last_response.body)
|
content = JSON.parse(last_response.body)
|
||||||
content["items"]["bamboo.txt"].wont_be_nil
|
_(content["items"]["bamboo.txt"]).wont_be_nil
|
||||||
content["items"]["bamboo.txt"]["ETag"].must_equal "0818etag"
|
_(content["items"]["bamboo.txt"]["ETag"]).must_equal "0818etag"
|
||||||
content["items"]["bamboo.txt"]["Content-Type"].must_equal "text/plain; charset=utf-8"
|
_(content["items"]["bamboo.txt"]["Content-Type"]).must_equal "text/plain; charset=utf-8"
|
||||||
content["items"]["bamboo.txt"]["Content-Length"].must_equal 8
|
_(content["items"]["bamboo.txt"]["Content-Length"]).must_equal 8
|
||||||
content["items"]["bamboo.txt"]["Last-Modified"].must_equal "Fri, 04 Mar 2016 12:20:18 GMT"
|
_(content["items"]["bamboo.txt"]["Last-Modified"]).must_equal "Fri, 04 Mar 2016 12:20:18 GMT"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -127,31 +127,31 @@ shared_examples_for 'a REST adapter' do
|
|||||||
it "is successful when there is no name collision" do
|
it "is successful when there is no name collision" do
|
||||||
put "/phil/food/aguacate", "si"
|
put "/phil/food/aguacate", "si"
|
||||||
|
|
||||||
last_response.status.must_equal 201
|
_(last_response.status).must_equal 201
|
||||||
|
|
||||||
metadata = redis.hgetall "rs:m:phil:food/aguacate"
|
metadata = redis.hgetall "rs:m:phil:food/aguacate"
|
||||||
metadata["s"].must_equal "2"
|
_(metadata["s"]).must_equal "2"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "conflicts when there is a directory with same name as document" do
|
it "conflicts when there is a directory with same name as document" do
|
||||||
put "/phil/food/aguacate", "si"
|
put "/phil/food/aguacate", "si"
|
||||||
put "/phil/food", "wontwork"
|
put "/phil/food", "wontwork"
|
||||||
|
|
||||||
last_response.status.must_equal 409
|
_(last_response.status).must_equal 409
|
||||||
last_response.body.must_equal "Conflict"
|
_(last_response.body).must_equal "Conflict"
|
||||||
|
|
||||||
metadata = redis.hgetall "rs:m:phil:food"
|
metadata = redis.hgetall "rs:m:phil:food"
|
||||||
metadata.must_be_empty
|
_(metadata).must_be_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
it "conflicts when there is a document with same name as directory" do
|
it "conflicts when there is a document with same name as directory" do
|
||||||
put "/phil/food/aguacate", "si"
|
put "/phil/food/aguacate", "si"
|
||||||
put "/phil/food/aguacate/empanado", "wontwork"
|
put "/phil/food/aguacate/empanado", "wontwork"
|
||||||
|
|
||||||
last_response.status.must_equal 409
|
_(last_response.status).must_equal 409
|
||||||
|
|
||||||
metadata = redis.hgetall "rs:m:phil:food/aguacate/empanado"
|
metadata = redis.hgetall "rs:m:phil:food/aguacate/empanado"
|
||||||
metadata.must_be_empty
|
_(metadata).must_be_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns 400 when a Content-Range header is sent" do
|
it "returns 400 when a Content-Range header is sent" do
|
||||||
@ -159,7 +159,7 @@ shared_examples_for 'a REST adapter' do
|
|||||||
|
|
||||||
put "/phil/food/aguacate", "si"
|
put "/phil/food/aguacate", "si"
|
||||||
|
|
||||||
last_response.status.must_equal 400
|
_(last_response.status).must_equal 400
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -173,8 +173,8 @@ shared_examples_for 'a REST adapter' do
|
|||||||
|
|
||||||
put "/phil/food/aguacate", "aye"
|
put "/phil/food/aguacate", "aye"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
last_response.headers["Etag"].must_equal "\"0915etag\""
|
_(last_response.headers["Etag"]).must_equal "\"0915etag\""
|
||||||
end
|
end
|
||||||
|
|
||||||
it "allows the request if the header contains a weak ETAG matching the current ETag" do
|
it "allows the request if the header contains a weak ETAG matching the current ETag" do
|
||||||
@ -182,8 +182,8 @@ shared_examples_for 'a REST adapter' do
|
|||||||
|
|
||||||
put "/phil/food/aguacate", "aye"
|
put "/phil/food/aguacate", "aye"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
last_response.headers["Etag"].must_equal "\"0915etag\""
|
_(last_response.headers["Etag"]).must_equal "\"0915etag\""
|
||||||
end
|
end
|
||||||
|
|
||||||
it "allows the request if the header contains a weak ETAG with leading quote matching the current ETag" do
|
it "allows the request if the header contains a weak ETAG with leading quote matching the current ETag" do
|
||||||
@ -191,8 +191,8 @@ shared_examples_for 'a REST adapter' do
|
|||||||
|
|
||||||
put "/phil/food/aguacate", "aye"
|
put "/phil/food/aguacate", "aye"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
last_response.headers["Etag"].must_equal "\"0915etag\""
|
_(last_response.headers["Etag"]).must_equal "\"0915etag\""
|
||||||
end
|
end
|
||||||
|
|
||||||
it "fails the request if the header does not match the current ETag" do
|
it "fails the request if the header does not match the current ETag" do
|
||||||
@ -200,8 +200,8 @@ shared_examples_for 'a REST adapter' do
|
|||||||
|
|
||||||
put "/phil/food/aguacate", "aye"
|
put "/phil/food/aguacate", "aye"
|
||||||
|
|
||||||
last_response.status.must_equal 412
|
_(last_response.status).must_equal 412
|
||||||
last_response.body.must_equal "Precondition Failed"
|
_(last_response.body).must_equal "Precondition Failed"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "allows the request if redis metadata became out of sync" do
|
it "allows the request if redis metadata became out of sync" do
|
||||||
@ -209,7 +209,7 @@ shared_examples_for 'a REST adapter' do
|
|||||||
|
|
||||||
put "/phil/food/aguacate", "aye"
|
put "/phil/food/aguacate", "aye"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -219,7 +219,7 @@ shared_examples_for 'a REST adapter' do
|
|||||||
|
|
||||||
put "/phil/food/aguacate", "si"
|
put "/phil/food/aguacate", "si"
|
||||||
|
|
||||||
last_response.status.must_equal 201
|
_(last_response.status).must_equal 201
|
||||||
end
|
end
|
||||||
|
|
||||||
it "fails the request if the document already exists" do
|
it "fails the request if the document already exists" do
|
||||||
@ -228,8 +228,18 @@ shared_examples_for 'a REST adapter' do
|
|||||||
header "If-None-Match", "*"
|
header "If-None-Match", "*"
|
||||||
put "/phil/food/aguacate", "si"
|
put "/phil/food/aguacate", "si"
|
||||||
|
|
||||||
last_response.status.must_equal 412
|
_(last_response.status).must_equal 412
|
||||||
last_response.body.must_equal "Precondition Failed"
|
_(last_response.body).must_equal "Precondition Failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Content-Type" do
|
||||||
|
it "must be in the type/subtype format" do
|
||||||
|
header "Content-Type", "text"
|
||||||
|
|
||||||
|
put "/phil/food/invalid_content_type", "invalid"
|
||||||
|
|
||||||
|
_(last_response.status).must_equal 415
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -247,8 +257,8 @@ shared_examples_for 'a REST adapter' do
|
|||||||
it "says it's not authorized" do
|
it "says it's not authorized" do
|
||||||
delete "/phil/food/aguacate"
|
delete "/phil/food/aguacate"
|
||||||
|
|
||||||
last_response.status.must_equal 401
|
_(last_response.status).must_equal 401
|
||||||
last_response.body.must_equal "Unauthorized"
|
_(last_response.body).must_equal "Unauthorized"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -257,8 +267,8 @@ shared_examples_for 'a REST adapter' do
|
|||||||
header "Authorization", "Bearer "
|
header "Authorization", "Bearer "
|
||||||
delete "/phil/food/aguacate"
|
delete "/phil/food/aguacate"
|
||||||
|
|
||||||
last_response.status.must_equal 401
|
_(last_response.status).must_equal 401
|
||||||
last_response.body.must_equal "Unauthorized"
|
_(last_response.body).must_equal "Unauthorized"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -267,8 +277,8 @@ shared_examples_for 'a REST adapter' do
|
|||||||
header "Authorization", "Bearer wrongtoken"
|
header "Authorization", "Bearer wrongtoken"
|
||||||
delete "/phil/food/aguacate"
|
delete "/phil/food/aguacate"
|
||||||
|
|
||||||
last_response.status.must_equal 401
|
_(last_response.status).must_equal 401
|
||||||
last_response.body.must_equal "Unauthorized"
|
_(last_response.body).must_equal "Unauthorized"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -288,14 +298,14 @@ shared_examples_for 'a REST adapter' do
|
|||||||
delete "/phil/food/aguacate"
|
delete "/phil/food/aguacate"
|
||||||
|
|
||||||
size_log = redis.get "rs:s:phil"
|
size_log = redis.get "rs:s:phil"
|
||||||
size_log.must_equal "8"
|
_(size_log).must_equal "8"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "deletes the metadata object in redis" do
|
it "deletes the metadata object in redis" do
|
||||||
delete "/phil/food/aguacate"
|
delete "/phil/food/aguacate"
|
||||||
|
|
||||||
metadata = redis.hgetall "rs:m:phil:food/aguacate"
|
metadata = redis.hgetall "rs:m:phil:food/aguacate"
|
||||||
metadata.must_be_empty
|
_(metadata).must_be_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
it "deletes the directory objects metadata in redis" do
|
it "deletes the directory objects metadata in redis" do
|
||||||
@ -306,15 +316,15 @@ shared_examples_for 'a REST adapter' do
|
|||||||
end
|
end
|
||||||
|
|
||||||
metadata = redis.hgetall "rs:m:phil:food/"
|
metadata = redis.hgetall "rs:m:phil:food/"
|
||||||
metadata["e"].must_equal "newetag"
|
_(metadata["e"]).must_equal "newetag"
|
||||||
metadata["m"].length.must_equal 13
|
_(metadata["m"].length).must_equal 13
|
||||||
metadata["m"].wont_equal old_metadata["m"]
|
_(metadata["m"]).wont_equal old_metadata["m"]
|
||||||
|
|
||||||
food_items = redis.smembers "rs:m:phil:food/:items"
|
food_items = redis.smembers "rs:m:phil:food/:items"
|
||||||
food_items.sort.must_equal ["camaron", "desayunos/"]
|
_(food_items.sort).must_equal ["camaron", "desayunos/"]
|
||||||
|
|
||||||
root_items = redis.smembers "rs:m:phil:/:items"
|
root_items = redis.smembers "rs:m:phil:/:items"
|
||||||
root_items.must_equal ["food/"]
|
_(root_items).must_equal ["food/"]
|
||||||
end
|
end
|
||||||
|
|
||||||
it "deletes the parent directory objects metadata when deleting all items" do
|
it "deletes the parent directory objects metadata when deleting all items" do
|
||||||
@ -322,19 +332,19 @@ shared_examples_for 'a REST adapter' do
|
|||||||
delete "/phil/food/camaron"
|
delete "/phil/food/camaron"
|
||||||
delete "/phil/food/desayunos/bolon"
|
delete "/phil/food/desayunos/bolon"
|
||||||
|
|
||||||
redis.smembers("rs:m:phil:food/desayunos:items").must_be_empty
|
_(redis.smembers("rs:m:phil:food/desayunos:items")).must_be_empty
|
||||||
redis.hgetall("rs:m:phil:food/desayunos/").must_be_empty
|
_(redis.hgetall("rs:m:phil:food/desayunos/")).must_be_empty
|
||||||
|
|
||||||
redis.smembers("rs:m:phil:food/:items").must_be_empty
|
_(redis.smembers("rs:m:phil:food/:items")).must_be_empty
|
||||||
redis.hgetall("rs:m:phil:food/").must_be_empty
|
_(redis.hgetall("rs:m:phil:food/")).must_be_empty
|
||||||
|
|
||||||
redis.smembers("rs:m:phil:/:items").must_be_empty
|
_(redis.smembers("rs:m:phil:/:items")).must_be_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
it "responds with the ETag of the deleted item in the header" do
|
it "responds with the ETag of the deleted item in the header" do
|
||||||
delete "/phil/food/aguacate"
|
delete "/phil/food/aguacate"
|
||||||
|
|
||||||
last_response.headers["ETag"].must_equal "\"0815etag\""
|
_(last_response.headers["ETag"]).must_equal "\"0815etag\""
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when item doesn't exist" do
|
context "when item doesn't exist" do
|
||||||
@ -345,20 +355,20 @@ shared_examples_for 'a REST adapter' do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "returns a 404" do
|
it "returns a 404" do
|
||||||
last_response.status.must_equal 404
|
_(last_response.status).must_equal 404
|
||||||
last_response.body.must_equal "Not Found"
|
_(last_response.body).must_equal "Not Found"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "deletes any metadata that might still exist" do
|
it "deletes any metadata that might still exist" do
|
||||||
delete "/phil/food/steak"
|
delete "/phil/food/steak"
|
||||||
|
|
||||||
metadata = redis.hgetall "rs:m:phil:food/steak"
|
metadata = redis.hgetall "rs:m:phil:food/steak"
|
||||||
metadata.must_be_empty
|
_(metadata).must_be_empty
|
||||||
|
|
||||||
redis.smembers("rs:m:phil:food/:items").must_be_empty
|
_(redis.smembers("rs:m:phil:food/:items")).must_be_empty
|
||||||
redis.hgetall("rs:m:phil:food/").must_be_empty
|
_(redis.hgetall("rs:m:phil:food/")).must_be_empty
|
||||||
|
|
||||||
redis.smembers("rs:m:phil:/:items").must_be_empty
|
_(redis.smembers("rs:m:phil:/:items")).must_be_empty
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -368,7 +378,7 @@ shared_examples_for 'a REST adapter' do
|
|||||||
|
|
||||||
delete "/phil/food/aguacate"
|
delete "/phil/food/aguacate"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
end
|
end
|
||||||
|
|
||||||
it "succeeds when the header contains a weak ETAG matching the current ETag" do
|
it "succeeds when the header contains a weak ETAG matching the current ETag" do
|
||||||
@ -376,7 +386,7 @@ shared_examples_for 'a REST adapter' do
|
|||||||
|
|
||||||
delete "/phil/food/aguacate"
|
delete "/phil/food/aguacate"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
end
|
end
|
||||||
|
|
||||||
it "fails the request if it does not match the current ETag" do
|
it "fails the request if it does not match the current ETag" do
|
||||||
@ -384,8 +394,8 @@ shared_examples_for 'a REST adapter' do
|
|||||||
|
|
||||||
delete "/phil/food/aguacate"
|
delete "/phil/food/aguacate"
|
||||||
|
|
||||||
last_response.status.must_equal 412
|
_(last_response.status).must_equal 412
|
||||||
last_response.body.must_equal "Precondition Failed"
|
_(last_response.body).must_equal "Precondition Failed"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -397,14 +407,55 @@ shared_examples_for 'a REST adapter' do
|
|||||||
purge_redis
|
purge_redis
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "requests to public resources" do
|
||||||
|
before do
|
||||||
|
redis.sadd "authorizations:phil:amarillo", [":rw"]
|
||||||
|
header "Authorization", "Bearer amarillo"
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "normal request" do
|
||||||
|
before do
|
||||||
|
header "Content-Type", "image/jpeg"
|
||||||
|
|
||||||
|
put "/phil/public/shares/example.jpg", ""
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the required response headers" do
|
||||||
|
get "/phil/public/shares/example.jpg"
|
||||||
|
|
||||||
|
last_response.status.must_equal 200
|
||||||
|
last_response.headers["Content-Type"].must_equal "image/jpeg"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "partial request" do
|
||||||
|
before do
|
||||||
|
header "Content-Type", "image/jpeg"
|
||||||
|
|
||||||
|
put "/phil/public/shares/example_partial.jpg", <<-EOF
|
||||||
|
JFIFddDuckyA␍⎺␉␊␍
|
||||||
|
#%'%#//33//@@@@@@@@@@@@@@@&&0##0+.'''.+550055@@?@@@@@@@@@@@>"!1AQaq"2B
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the required response headers" do
|
||||||
|
header 'Range', 'bytes=0-16'
|
||||||
|
get "/phil/public/shares/example_partial.jpg"
|
||||||
|
|
||||||
|
last_response.status.must_equal 206
|
||||||
|
last_response.headers["Content-Type"].must_equal "image/jpeg"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "not authorized" do
|
context "not authorized" do
|
||||||
|
|
||||||
describe "without token" do
|
describe "without token" do
|
||||||
it "says it's not authorized" do
|
it "says it's not authorized" do
|
||||||
get "/phil/food/"
|
get "/phil/food/"
|
||||||
|
|
||||||
last_response.status.must_equal 401
|
_(last_response.status).must_equal 401
|
||||||
last_response.body.must_equal "Unauthorized"
|
_(last_response.body).must_equal "Unauthorized"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -413,8 +464,8 @@ shared_examples_for 'a REST adapter' do
|
|||||||
header "Authorization", "Bearer wrongtoken"
|
header "Authorization", "Bearer wrongtoken"
|
||||||
get "/phil/food/"
|
get "/phil/food/"
|
||||||
|
|
||||||
last_response.status.must_equal 401
|
_(last_response.status).must_equal 401
|
||||||
last_response.body.must_equal "Unauthorized"
|
_(last_response.body).must_equal "Unauthorized"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -436,18 +487,18 @@ shared_examples_for 'a REST adapter' do
|
|||||||
it "returns the required response headers" do
|
it "returns the required response headers" do
|
||||||
get "/phil/food/aguacate"
|
get "/phil/food/aguacate"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
# ETag is coming from the Redis metadata, not the storage server (which has "0817etag")
|
# ETag is coming from the Redis metadata, not the storage server (which has "0817etag")
|
||||||
last_response.headers["ETag"].must_equal "\"0815etag\""
|
_(last_response.headers["ETag"]).must_equal "\"0815etag\""
|
||||||
last_response.headers["Cache-Control"].must_equal "no-cache"
|
_(last_response.headers["Cache-Control"]).must_equal "no-cache"
|
||||||
last_response.headers["Content-Type"].must_equal "text/plain; charset=utf-8"
|
_(last_response.headers["Content-Type"]).must_equal "text/plain; charset=utf-8"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns a 404 when data doesn't exist" do
|
it "returns a 404 when data doesn't exist" do
|
||||||
get "/phil/food/steak"
|
get "/phil/food/steak"
|
||||||
|
|
||||||
last_response.status.must_equal 404
|
_(last_response.status).must_equal 404
|
||||||
last_response.body.must_equal "Not Found"
|
_(last_response.body).must_equal "Not Found"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "responds with 304 when IF_NONE_MATCH header contains the ETag" do
|
it "responds with 304 when IF_NONE_MATCH header contains the ETag" do
|
||||||
@ -455,9 +506,9 @@ shared_examples_for 'a REST adapter' do
|
|||||||
|
|
||||||
get "/phil/food/aguacate"
|
get "/phil/food/aguacate"
|
||||||
|
|
||||||
last_response.status.must_equal 304
|
_(last_response.status).must_equal 304
|
||||||
last_response.headers["ETag"].must_equal "\"0815etag\""
|
_(last_response.headers["ETag"]).must_equal "\"0815etag\""
|
||||||
last_response.headers["Last-Modified"].must_equal "Fri, 04 Mar 2016 12:20:18 GMT"
|
_(last_response.headers["Last-Modified"]).must_equal "Fri, 04 Mar 2016 12:20:18 GMT"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "responds with 304 when IF_NONE_MATCH header contains weak ETAG matching the current ETag" do
|
it "responds with 304 when IF_NONE_MATCH header contains weak ETAG matching the current ETag" do
|
||||||
@ -465,9 +516,9 @@ shared_examples_for 'a REST adapter' do
|
|||||||
|
|
||||||
get "/phil/food/aguacate"
|
get "/phil/food/aguacate"
|
||||||
|
|
||||||
last_response.status.must_equal 304
|
_(last_response.status).must_equal 304
|
||||||
last_response.headers["ETag"].must_equal "\"0815etag\""
|
_(last_response.headers["ETag"]).must_equal "\"0815etag\""
|
||||||
last_response.headers["Last-Modified"].must_equal "Fri, 04 Mar 2016 12:20:18 GMT"
|
_(last_response.headers["Last-Modified"]).must_equal "Fri, 04 Mar 2016 12:20:18 GMT"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -477,70 +528,70 @@ shared_examples_for 'a REST adapter' do
|
|||||||
it "returns the correct ETag header" do
|
it "returns the correct ETag header" do
|
||||||
get "/phil/food/"
|
get "/phil/food/"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
last_response.headers["ETag"].must_equal "\"f9f85fbf5aa1fa378fd79ac8aa0a457d\""
|
_(last_response.headers["ETag"]).must_equal "\"f9f85fbf5aa1fa378fd79ac8aa0a457d\""
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns a Cache-Control header with value 'no-cache'" do
|
it "returns a Cache-Control header with value 'no-cache'" do
|
||||||
get "/phil/food/"
|
get "/phil/food/"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
last_response.headers["Cache-Control"].must_equal "no-cache"
|
_(last_response.headers["Cache-Control"]).must_equal "no-cache"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "responds with 304 when IF_NONE_MATCH header contains the ETag" do
|
it "responds with 304 when IF_NONE_MATCH header contains the ETag" do
|
||||||
header "If-None-Match", "\"f9f85fbf5aa1fa378fd79ac8aa0a457d\""
|
header "If-None-Match", "\"f9f85fbf5aa1fa378fd79ac8aa0a457d\""
|
||||||
get "/phil/food/"
|
get "/phil/food/"
|
||||||
|
|
||||||
last_response.status.must_equal 304
|
_(last_response.status).must_equal 304
|
||||||
end
|
end
|
||||||
|
|
||||||
it "responds with 304 when IF_NONE_MATCH header contains weak ETAG matching the ETag" do
|
it "responds with 304 when IF_NONE_MATCH header contains weak ETAG matching the ETag" do
|
||||||
header "If-None-Match", "W/\"f9f85fbf5aa1fa378fd79ac8aa0a457d\""
|
header "If-None-Match", "W/\"f9f85fbf5aa1fa378fd79ac8aa0a457d\""
|
||||||
get "/phil/food/"
|
get "/phil/food/"
|
||||||
|
|
||||||
last_response.status.must_equal 304
|
_(last_response.status).must_equal 304
|
||||||
end
|
end
|
||||||
|
|
||||||
it "contains all items in the directory" do
|
it "contains all items in the directory" do
|
||||||
get "/phil/food/"
|
get "/phil/food/"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
last_response.content_type.must_equal "application/ld+json"
|
_(last_response.content_type).must_equal "application/ld+json"
|
||||||
|
|
||||||
content = JSON.parse(last_response.body)
|
content = JSON.parse(last_response.body)
|
||||||
content["@context"].must_equal "http://remotestorage.io/spec/folder-description"
|
_(content["@context"]).must_equal "http://remotestorage.io/spec/folder-description"
|
||||||
content["items"]["aguacate"].wont_be_nil
|
_(content["items"]["aguacate"]).wont_be_nil
|
||||||
content["items"]["aguacate"]["Content-Type"].must_equal "text/plain; charset=utf-8"
|
_(content["items"]["aguacate"]["Content-Type"]).must_equal "text/plain; charset=utf-8"
|
||||||
content["items"]["aguacate"]["Content-Length"].must_equal 2
|
_(content["items"]["aguacate"]["Content-Length"]).must_equal 2
|
||||||
content["items"]["aguacate"]["ETag"].must_equal "0815etag"
|
_(content["items"]["aguacate"]["ETag"]).must_equal "0815etag"
|
||||||
content["items"]["camaron"].wont_be_nil
|
_(content["items"]["camaron"]).wont_be_nil
|
||||||
content["items"]["camaron"]["Content-Type"].must_equal "text/plain; charset=utf-8"
|
_(content["items"]["camaron"]["Content-Type"]).must_equal "text/plain; charset=utf-8"
|
||||||
content["items"]["camaron"]["Content-Length"].must_equal 5
|
_(content["items"]["camaron"]["Content-Length"]).must_equal 5
|
||||||
content["items"]["camaron"]["ETag"].must_equal "0816etag"
|
_(content["items"]["camaron"]["ETag"]).must_equal "0816etag"
|
||||||
content["items"]["desayunos/"].wont_be_nil
|
_(content["items"]["desayunos/"]).wont_be_nil
|
||||||
content["items"]["desayunos/"]["ETag"].must_equal "dd36e3cfe52b5f33421150b289a7d48d"
|
_(content["items"]["desayunos/"]["ETag"]).must_equal "dd36e3cfe52b5f33421150b289a7d48d"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "contains all items in the root directory" do
|
it "contains all items in the root directory" do
|
||||||
get "phil/"
|
get "phil/"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
last_response.content_type.must_equal "application/ld+json"
|
_(last_response.content_type).must_equal "application/ld+json"
|
||||||
|
|
||||||
content = JSON.parse(last_response.body)
|
content = JSON.parse(last_response.body)
|
||||||
content["items"]["food/"].wont_be_nil
|
_(content["items"]["food/"]).wont_be_nil
|
||||||
content["items"]["food/"]["ETag"].must_equal "f9f85fbf5aa1fa378fd79ac8aa0a457d"
|
_(content["items"]["food/"]["ETag"]).must_equal "f9f85fbf5aa1fa378fd79ac8aa0a457d"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "responds with an empty directory liting when directory doesn't exist" do
|
it "responds with an empty directory liting when directory doesn't exist" do
|
||||||
get "phil/some-non-existing-dir/"
|
get "phil/some-non-existing-dir/"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
last_response.content_type.must_equal "application/ld+json"
|
_(last_response.content_type).must_equal "application/ld+json"
|
||||||
|
|
||||||
content = JSON.parse(last_response.body)
|
content = JSON.parse(last_response.body)
|
||||||
content["items"].must_equal({})
|
_(content["items"]).must_equal({})
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -560,8 +611,8 @@ shared_examples_for 'a REST adapter' do
|
|||||||
it "says it's not authorized" do
|
it "says it's not authorized" do
|
||||||
head "/phil/food/camarones"
|
head "/phil/food/camarones"
|
||||||
|
|
||||||
last_response.status.must_equal 401
|
_(last_response.status).must_equal 401
|
||||||
last_response.body.must_be_empty
|
_(last_response.body).must_be_empty
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -570,8 +621,8 @@ shared_examples_for 'a REST adapter' do
|
|||||||
header "Authorization", "Bearer wrongtoken"
|
header "Authorization", "Bearer wrongtoken"
|
||||||
head "/phil/food/camarones"
|
head "/phil/food/camarones"
|
||||||
|
|
||||||
last_response.status.must_equal 401
|
_(last_response.status).must_equal 401
|
||||||
last_response.body.must_be_empty
|
_(last_response.body).must_be_empty
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -592,9 +643,9 @@ shared_examples_for 'a REST adapter' do
|
|||||||
it "returns the correct header information" do
|
it "returns the correct header information" do
|
||||||
get "/phil/food/"
|
get "/phil/food/"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
last_response.content_type.must_equal "application/ld+json"
|
_(last_response.content_type).must_equal "application/ld+json"
|
||||||
last_response.headers["ETag"].must_equal "\"f9f85fbf5aa1fa378fd79ac8aa0a457d\""
|
_(last_response.headers["ETag"]).must_equal "\"f9f85fbf5aa1fa378fd79ac8aa0a457d\""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -603,8 +654,8 @@ shared_examples_for 'a REST adapter' do
|
|||||||
it "returns a 404" do
|
it "returns a 404" do
|
||||||
head "/phil/food/steak"
|
head "/phil/food/steak"
|
||||||
|
|
||||||
last_response.status.must_equal 404
|
_(last_response.status).must_equal 404
|
||||||
last_response.body.must_be_empty
|
_(last_response.body).must_be_empty
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -612,12 +663,12 @@ shared_examples_for 'a REST adapter' do
|
|||||||
it "returns the required response headers" do
|
it "returns the required response headers" do
|
||||||
head "/phil/food/aguacate"
|
head "/phil/food/aguacate"
|
||||||
|
|
||||||
last_response.status.must_equal 200
|
_(last_response.status).must_equal 200
|
||||||
last_response.headers["ETag"].must_equal "\"0815etag\""
|
_(last_response.headers["ETag"]).must_equal "\"0815etag\""
|
||||||
last_response.headers["Cache-Control"].must_equal "no-cache"
|
_(last_response.headers["Cache-Control"]).must_equal "no-cache"
|
||||||
last_response.headers["Last-Modified"].must_equal "Fri, 04 Mar 2016 12:20:18 GMT"
|
_(last_response.headers["Last-Modified"]).must_equal "Fri, 04 Mar 2016 12:20:18 GMT"
|
||||||
last_response.headers["Content-Type"].must_equal "text/plain; charset=utf-8"
|
_(last_response.headers["Content-Type"]).must_equal "text/plain; charset=utf-8"
|
||||||
last_response.headers["Content-Length"].must_equal "2"
|
_(last_response.headers["Content-Length"]).must_equal "2"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "responds with 304 when IF_NONE_MATCH header contains the ETag" do
|
it "responds with 304 when IF_NONE_MATCH header contains the ETag" do
|
||||||
@ -625,9 +676,9 @@ shared_examples_for 'a REST adapter' do
|
|||||||
|
|
||||||
head "/phil/food/aguacate"
|
head "/phil/food/aguacate"
|
||||||
|
|
||||||
last_response.status.must_equal 304
|
_(last_response.status).must_equal 304
|
||||||
last_response.headers["ETag"].must_equal "\"0815etag\""
|
_(last_response.headers["ETag"]).must_equal "\"0815etag\""
|
||||||
last_response.headers["Last-Modified"].must_equal "Fri, 04 Mar 2016 12:20:18 GMT"
|
_(last_response.headers["Last-Modified"]).must_equal "Fri, 04 Mar 2016 12:20:18 GMT"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "responds with 304 when IF_NONE_MATCH header contains weak ETAG matching the current ETag" do
|
it "responds with 304 when IF_NONE_MATCH header contains weak ETAG matching the current ETag" do
|
||||||
@ -635,9 +686,9 @@ shared_examples_for 'a REST adapter' do
|
|||||||
|
|
||||||
head "/phil/food/aguacate"
|
head "/phil/food/aguacate"
|
||||||
|
|
||||||
last_response.status.must_equal 304
|
_(last_response.status).must_equal 304
|
||||||
last_response.headers["ETag"].must_equal "\"0815etag\""
|
_(last_response.headers["ETag"]).must_equal "\"0815etag\""
|
||||||
last_response.headers["Last-Modified"].must_equal "Fri, 04 Mar 2016 12:20:18 GMT"
|
_(last_response.headers["Last-Modified"]).must_equal "Fri, 04 Mar 2016 12:20:18 GMT"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -16,12 +16,20 @@ describe "Swift provider" do
|
|||||||
stub_request(:put, "#{container_url_for("phil")}/food/aguacate").
|
stub_request(:put, "#{container_url_for("phil")}/food/aguacate").
|
||||||
with(body: "aye").
|
with(body: "aye").
|
||||||
to_return(status: 200, headers: { etag: "0915etag", last_modified: "Fri, 04 Mar 2016 12:20:18 GMT" })
|
to_return(status: 200, headers: { etag: "0915etag", last_modified: "Fri, 04 Mar 2016 12:20:18 GMT" })
|
||||||
|
stub_request(:put, "#{container_url_for("phil")}/public/shares/example.jpg").
|
||||||
|
to_return(status: 200, headers: { etag: '"0817etag"', content_type: "image/jpeg", last_modified: "Fri, 04 Mar 2016 12:20:18 GMT" })
|
||||||
|
stub_request(:put, "#{container_url_for("phil")}/public/shares/example_partial.jpg").
|
||||||
|
to_return(status: 200, headers: { etag: '"0817etag"', content_type: "image/jpeg", last_modified: "Fri, 04 Mar 2016 12:20:18 GMT" })
|
||||||
stub_request(:head, "#{container_url_for("phil")}/food/aguacate").
|
stub_request(:head, "#{container_url_for("phil")}/food/aguacate").
|
||||||
to_return(status: 200, headers: { last_modified: "Fri, 04 Mar 2016 12:20:18 GMT" })
|
to_return(status: 200, headers: { last_modified: "Fri, 04 Mar 2016 12:20:18 GMT" })
|
||||||
stub_request(:get, "#{container_url_for("phil")}/food/aguacate").
|
stub_request(:get, "#{container_url_for("phil")}/food/aguacate").
|
||||||
to_return(status: 200, body: "rootbody", headers: { etag: "0817etag", content_type: "text/plain; charset=utf-8" })
|
to_return(status: 200, body: "rootbody", headers: { etag: "0817etag", content_type: "text/plain; charset=utf-8" })
|
||||||
stub_request(:delete, "#{container_url_for("phil")}/food/aguacate").
|
stub_request(:delete, "#{container_url_for("phil")}/food/aguacate").
|
||||||
to_return(status: 200, headers: { etag: "0815etag" })
|
to_return(status: 200, headers: { etag: "0815etag" })
|
||||||
|
stub_request(:get, "#{container_url_for("phil")}/public/shares/example.jpg").
|
||||||
|
to_return(status: 200, body: "", headers: { etag: '"0817etag"', content_type: "image/jpeg" })
|
||||||
|
stub_request(:get, "#{container_url_for("phil")}/public/shares/example_partial.jpg").
|
||||||
|
to_return(status: 206, body: "", headers: { etag: '"0817etag"', content_type: "image/jpeg", content_range: "bytes 0-16/128" })
|
||||||
|
|
||||||
# Write new content to check the metadata in Redis
|
# Write new content to check the metadata in Redis
|
||||||
stub_request(:put, "#{container_url_for("phil")}/food/banano").
|
stub_request(:put, "#{container_url_for("phil")}/food/banano").
|
||||||
|
Loading…
x
Reference in New Issue
Block a user