spec/riak/http_backend_spec.rb in riak-client-0.9.8 vs spec/riak/http_backend_spec.rb in riak-client-1.0.0.beta
- old
+ new
@@ -1,19 +1,6 @@
-# Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License
-require File.expand_path("../spec_helper", File.dirname(__FILE__))
+require 'spec_helper'
describe Riak::Client::HTTPBackend do
before :each do
@client = Riak::Client.new
@backend = Riak::Client::HTTPBackend.new(@client)
@@ -29,11 +16,11 @@
@backend.client.should == @client
end
context "pinging the server" do
it "should succeed on 200" do
- @backend.should_receive(:get).with(200, "/ping", {}, {}).and_return({:code => 200, :body => "OK"})
+ @backend.should_receive(:get).with(200, @backend.ping_path).and_return({:code => 200, :body => "OK"})
@backend.ping.should be_true
end
it "should fail on any other code or error" do
@backend.should_receive(:get).and_raise("socket closed")
@@ -41,21 +28,21 @@
end
end
context "fetching an object" do
it "should perform a GET request and return an RObject" do
- @backend.should_receive(:get).with([200,300], "/riak/","foo", "db", {}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
+ @backend.should_receive(:get).with([200,300], @backend.object_path('foo', 'db')).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
@backend.fetch_object("foo", "db").should be_kind_of(Riak::RObject)
end
it "should pass the R quorum as a query parameter" do
- @backend.should_receive(:get).with([200,300], "/riak/","foo", "db", {:r => 2}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
- @backend.fetch_object("foo", "db", 2)
+ @backend.should_receive(:get).with([200,300], @backend.object_path("foo", "db", {:r => 2})).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
+ @backend.fetch_object("foo", "db", :r => 2)
end
it "should escape the bucket and key names" do
- @backend.should_receive(:get).with([200,300], "/riak/","foo%20", "%20bar", {}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
+ @backend.should_receive(:get).with([200,300], @backend.object_path('foo ', ' bar')).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
@backend.fetch_object('foo ',' bar').should be_kind_of(Riak::RObject)
end
end
context "reloading an object" do
@@ -63,11 +50,11 @@
@object = Riak::RObject.new(@client.bucket("foo"), "bar")
end
it "should use conditional request headers" do
@object.etag = "etag"
- @backend.should_receive(:get).with([200,300,304], "/riak/", "foo", "bar", {}, {'If-None-Match' => "etag"}).and_return({:code => 304})
+ @backend.should_receive(:get).with([200,300,304], @backend.object_path('foo', 'bar'), {'If-None-Match' => "etag"}).and_return({:code => 304})
@backend.reload_object(@object)
end
it "should return without modifying the object if the response is 304 Not Modified" do
@backend.should_receive(:get).and_return({:code => 304})
@@ -82,11 +69,11 @@
it "should escape the bucket and key names" do
# @bucket.should_receive(:name).and_return("some/deep/path")
@object.bucket = @client.bucket("some/deep/path")
@object.key = "another/deep/path"
- @backend.should_receive(:get).with([200,300,304], "/riak/", "some%2Fdeep%2Fpath", "another%2Fdeep%2Fpath", {}, {}).and_return({:code => 304})
+ @backend.should_receive(:get).with([200,300,304], @backend.object_path(@object.bucket.name, @object.key), {}).and_return({:code => 304})
@backend.reload_object(@object)
end
end
context "storing an object" do
@@ -101,121 +88,98 @@
it "should use the raw_data as the request body" do
@object.content_type = "application/json"
body = @object.raw_data = "{this is probably invalid json!}}"
@backend.stub(:post).and_return({})
@object.should_not_receive(:serialize)
- @backend.store_object(@object, false)
+ @backend.store_object(@object, :returnbody => false)
end
-
+
context "when the object has no key" do
it "should issue a POST request to the bucket, and update the object properties (returning the body by default)" do
- @backend.should_receive(:post).with(201, "/riak/", "foo", {:returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 201})
- @backend.store_object(@object, true, nil, nil)
+ @backend.should_receive(:post).with(201, @backend.object_path("foo", nil, {:returnbody => true}), "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 201})
+ @backend.store_object(@object, :returnbody => true)
@object.key.should == "somereallylongstring"
@object.vclock.should == "areallylonghashvalue"
end
it "should include persistence-tuning parameters in the query string" do
- @backend.should_receive(:post).with(201, "/riak/", "foo", {:dw => 2, :returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 201})
- @backend.store_object(@object, true, nil, 2)
+ @backend.should_receive(:post).with(201, @backend.object_path("foo", nil, {:dw => 2, :returnbody => true}), "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 201})
+ @backend.store_object(@object, :returnbody => true, :dw => 2)
end
-
- it "should escape the bucket name" do
- @object.bucket = @client.bucket("foo ")
- @backend.should_receive(:post).with(201, "/riak/", "foo%20", {:returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 201})
- @backend.store_object(@object, true)
- end
end
context "when the object has a key" do
before :each do
@object.key = "bar"
end
it "should issue a PUT request to the bucket, and update the object properties (returning the body by default)" do
- @backend.should_receive(:put).with([200,204,300], "/riak/", "foo/bar", {:returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
- @backend.store_object(@object, true, nil, nil)
+ @backend.should_receive(:put).with([200,204,300], @backend.object_path("foo", "bar", {:returnbody => true}), "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
+ @backend.store_object(@object, :returnbody => true)
@object.key.should == "somereallylongstring"
@object.vclock.should == "areallylonghashvalue"
end
-
+
it "should include persistence-tuning parameters in the query string" do
- @backend.should_receive(:put).with([200,204,300], "/riak/", "foo/bar", {:w => 2, :returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
- @backend.store_object(@object, true, 2, nil)
+ @backend.should_receive(:put).with([200,204,300], @backend.object_path("foo", "bar", {:w => 2, :returnbody => true}), "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
+ @backend.store_object(@object, :returnbody => true, :w => 2)
end
-
- it "should escape the bucket and key names" do
- @backend.should_receive(:put).with([200,204,300], "/riak/", "foo%20/bar%2Fbaz", {:returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
- @bucket.instance_variable_set(:@name, "foo ")
- @object.key = "bar/baz"
- @backend.store_object(@object, true, nil, nil)
- end
end
end
context "deleting an object" do
it "should perform a DELETE request" do
- @backend.should_receive(:delete).with([204,404], "/riak/", "foo", 'bar',{},{}).and_return(:code => 204)
+ @backend.should_receive(:delete).with([204,404], @backend.object_path("foo", 'bar'), {}).and_return(:code => 204)
@backend.delete_object("foo", "bar")
end
- it "should escape the bucket and key names" do
- @backend.should_receive(:delete).with([204,404], "/riak/", "bucket%20spaces", "deep%2Fpath",{},{}).and_return({:code => 204, :headers => {}})
- @backend.delete_object("bucket spaces", "deep/path")
+ it "should perform a DELETE request with the provided vclock" do
+ @backend.should_receive(:delete).with([204,404], @backend.object_path("foo", 'bar'), {'X-Riak-VClock' => "myvclock"}).and_return(:code => 204)
+ @backend.delete_object('foo', 'bar', :vclock => "myvclock")
end
+
+ it "should perform a DELETE request with the requested quorum value" do
+ @backend.should_receive(:delete).with([204,404], @backend.object_path("foo", 'bar', {:rw => 2}), {'X-Riak-VClock' => "myvclock"}).and_return(:code => 204)
+ @backend.delete_object('foo', 'bar', :vclock => "myvclock", :rw => 2)
+ end
end
context "fetching bucket properties" do
it "should GET the bucket URL and parse the response as JSON" do
- @backend.should_receive(:get).with(200, "/riak/", "foo", {:keys => false, :props => true}, {}).and_return({:body => '{"props":{"n_val":3}}'})
+ @backend.should_receive(:get).with(200, @backend.bucket_properties_path('foo')).and_return({:body => '{"props":{"n_val":3}}'})
@backend.get_bucket_props("foo").should == {"n_val" => 3}
end
-
- it "should escape the bucket name" do
- @backend.should_receive(:get).with(200, "/riak/", "foo%20bar", {:keys => false, :props => true}, {}).and_return({:body => '{"props":{"n_val":3}}'})
- @backend.get_bucket_props("foo bar")
- end
end
-
+
context "setting bucket properties" do
it "should PUT the properties to the bucket URL as JSON" do
- @backend.should_receive(:put).with(204, "/riak/","foo", '{"props":{"n_val":2}}', {"Content-Type" => "application/json"}).and_return({:body => "", :headers => {}})
- @backend.set_bucket_props("foo", {:n_val => 2})
+ @backend.should_receive(:put).with(204, @backend.bucket_properties_path('foo'), '{"props":{"n_val":2}}', {"Content-Type" => "application/json"}).and_return({:body => "", :headers => {}})
+ @backend.set_bucket_props("foo", {:n_val => 2})
end
-
- it "should escape the bucket name" do
- @backend.should_receive(:put).with(204, "/riak/","foo%20bar", '{"props":{"n_val":2}}', {"Content-Type" => "application/json"}).and_return({:body => "", :headers => {}})
- @backend.set_bucket_props("foo bar", {:n_val => 2})
- end
end
-
+
context "listing keys" do
it "should unescape key names" do
- @backend.should_receive(:get).with(200, "/riak/","foo", {:props => false, :keys => true}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar%20baz"]}'})
+ @backend.should_receive(:get).with(200, @backend.key_list_path('foo')).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar%20baz"]}'})
@backend.list_keys("foo").should == ["bar baz"]
end
-
- it "should escape the bucket name" do
- @backend.should_receive(:get).with(200, "/riak/","unescaped%20", {:props => false, :keys => true}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar"]}'})
- @backend.list_keys("unescaped ").should == ["bar"]
- end
end
context "listing buckets" do
- it "should GET the raw URL with ?buckets=true and parse the response as JSON" do
- @backend.should_receive(:get).with(200, "/riak/", {:buckets => true}, {}).and_return({:body => '{"buckets":["foo", "bar", "baz"]}'})
+ it "should GET the bucket list URL and parse the response as JSON" do
+ @backend.should_receive(:get).with(200, @backend.bucket_list_path).and_return({:body => '{"buckets":["foo", "bar", "baz"]}'})
@backend.list_buckets.should == ["foo", "bar", "baz"]
end
end
-
+
context "performing a MapReduce query" do
before do
@mr = Riak::MapReduce.new(@client).map("Riak.mapValues", :keep => true)
end
it "should issue POST request to the mapred endpoint" do
- @backend.should_receive(:post).with(200, "/mapred", @mr.to_json, hash_including("Content-Type" => "application/json")).and_return({:headers => {'content-type' => ["application/json"]}, :body => "[]"})
+ @backend.should_receive(:post).with(200, @backend.mapred_path, @mr.to_json, hash_including("Content-Type" => "application/json")).and_return({:headers => {'content-type' => ["application/json"]}, :body => "[]"})
@backend.mapred(@mr)
end
it "should vivify JSON responses" do
@backend.stub!(:post).and_return(:headers => {'content-type' => ["application/json"]}, :body => '[{"key":"value"}]')
@@ -228,11 +192,11 @@
@backend.mapred(@mr).should == response
end
it "should stream results through the block" do
data = File.read("spec/fixtures/multipart-mapreduce.txt")
- @backend.should_receive(:post).with(200, "/mapred", {:chunked => true}, @mr.to_json, hash_including("Content-Type" => "application/json")).and_yield(data)
+ @backend.should_receive(:post).with(200, @backend.mapred_path(:chunked => true), @mr.to_json, hash_including("Content-Type" => "application/json")).and_yield(data)
block = mock
block.should_receive(:ping).twice.and_return(true)
@backend.mapred(@mr) do |phase, data|
block.ping
phase.should == 0
@@ -241,25 +205,25 @@
end
end
context "getting statistics" do
it "should get the status URL and parse the response as JSON" do
- @backend.should_receive(:get).with(200, "/stats", {}, {}).and_return({:body => '{"vnode_gets":20348}'})
+ @backend.should_receive(:get).with(200, @backend.stats_path).and_return({:body => '{"vnode_gets":20348}'})
@backend.stats.should == {"vnode_gets" => 20348}
end
end
-
+
context "performing a link-walking query" do
before do
@bucket = Riak::Bucket.new(@client, "foo")
@object = Riak::RObject.new(@bucket, "bar")
@body = File.read(File.expand_path("#{File.dirname(__FILE__)}/../fixtures/multipart-with-body.txt"))
@specs = [Riak::WalkSpec.new(:tag => "next", :keep => true)]
end
it "should perform a GET request for the given object and walk specs" do
- @backend.should_receive(:get).with(200, "/riak/", "foo", "bar", "_,next,1").and_return(:headers => {"content-type" => ["multipart/mixed; boundary=12345"]}, :body => "\n--12345\nContent-Type: multipart/mixed; boundary=09876\n\n--09876--\n\n--12345--\n")
+ @backend.should_receive(:get).with(200, @backend.link_walk_path(@bucket.name, @object.key, @specs)).and_return(:headers => {"content-type" => ["multipart/mixed; boundary=12345"]}, :body => "\n--12345\nContent-Type: multipart/mixed; boundary=09876\n\n--09876--\n\n--12345--\n")
@backend.link_walk(@object, @specs)
end
it "should parse the results into arrays of objects" do
@backend.should_receive(:get).and_return(:headers => {"content-type" => ["multipart/mixed; boundary=5EiMOjuGavQ2IbXAqsJPLLfJNlA"]}, :body => @body)
@@ -275,8 +239,50 @@
it "should assign the bucket for newly parsed objects" do
@backend.stub!(:get).and_return(:headers => {"content-type" => ["multipart/mixed; boundary=5EiMOjuGavQ2IbXAqsJPLLfJNlA"]}, :body => @body)
@client.should_receive(:bucket).with("foo").and_return(@bucket)
@backend.link_walk(@object, @specs)
+ end
+
+ it "should discard unmarked tombstones" do
+ @backend.should_receive(:get).and_return(:headers => {"content-type" => ["multipart/mixed; boundary=CvfrSTCWwIiwezy0Zt1B2zwKgS7"]}, :body => File.read(File.expand_path("../../fixtures/multipart-with-unmarked-tombstone.txt", __FILE__)))
+ results = @backend.link_walk(@object, @specs)
+ results.first.should be_empty
+ end
+
+ it "should discard marked tombstones" do
+ @backend.should_receive(:get).and_return(:headers => {"content-type" => ["multipart/mixed; boundary=ADqgQtdmA5iQgyR5UGzX6V3HZtI"]}, :body => File.read(File.expand_path("../../fixtures/multipart-with-marked-tombstones.txt", __FILE__)))
+ results = @backend.link_walk(@object, @specs)
+ results.first.should be_empty
+ end
+ end
+
+ context "performing a search" do
+ before { @backend.send(:server_config)[:riak_solr_searcher_wm] = '/solr' }
+
+ it "should request the searcher resource" do
+ @backend.should_receive(:get).
+ with(200, @backend.solr_select_path(nil, 'foo', {'wt' => 'json'})).
+ and_return(:code => 200, :headers => {"content-type" => ['application/json']}, :body => '{}')
+ @backend.search(nil, 'foo')
+ end
+
+ it "should vivify JSON responses" do
+ @backend.should_receive(:get).and_return({:code => 200, :headers => {"content-type"=>["application/json"]}, :body => '{"response":{"docs":["foo"]}}'})
+ @backend.search(nil, "foo").should == {"response" => {"docs" => ["foo"]}}
+ end
+
+ it "should return non-JSON responses raw" do
+ @backend.should_receive(:get).and_return({:code => 200, :headers => {"content-type"=>["text/plain"]}, :body => '{"response":{"docs":["foo"]}}'})
+ @backend.search(nil, "foo").should == '{"response":{"docs":["foo"]}}'
+ end
+ end
+
+ context "updating a search index" do
+ before { @backend.send(:server_config)[:riak_solr_indexer_wm] = '/solr' }
+
+ it "should request the indexer resource" do
+ @backend.should_receive(:post).with(200, @backend.solr_update_path(nil), 'postbody', {"Content-Type" => "text/xml"})
+ @backend.update_search_index(nil, 'postbody')
end
end
end