seomoz-riak-client 1.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +27 -0
- data/Guardfile +14 -0
- data/Rakefile +76 -0
- data/erl_src/riak_kv_test_backend.beam +0 -0
- data/erl_src/riak_kv_test_backend.erl +174 -0
- data/erl_src/riak_search_test_backend.beam +0 -0
- data/erl_src/riak_search_test_backend.erl +175 -0
- data/lib/active_support/cache/riak_store.rb +2 -0
- data/lib/riak.rb +21 -0
- data/lib/riak/bucket.rb +215 -0
- data/lib/riak/cache_store.rb +84 -0
- data/lib/riak/client.rb +415 -0
- data/lib/riak/client/beefcake/messages.rb +147 -0
- data/lib/riak/client/beefcake/object_methods.rb +92 -0
- data/lib/riak/client/beefcake_protobuffs_backend.rb +176 -0
- data/lib/riak/client/excon_backend.rb +65 -0
- data/lib/riak/client/http_backend.rb +203 -0
- data/lib/riak/client/http_backend/configuration.rb +46 -0
- data/lib/riak/client/http_backend/key_streamer.rb +43 -0
- data/lib/riak/client/http_backend/object_methods.rb +94 -0
- data/lib/riak/client/http_backend/request_headers.rb +34 -0
- data/lib/riak/client/http_backend/transport_methods.rb +218 -0
- data/lib/riak/client/net_http_backend.rb +79 -0
- data/lib/riak/client/protobuffs_backend.rb +97 -0
- data/lib/riak/client/pump.rb +30 -0
- data/lib/riak/client/search.rb +94 -0
- data/lib/riak/core_ext.rb +6 -0
- data/lib/riak/core_ext/blank.rb +53 -0
- data/lib/riak/core_ext/extract_options.rb +7 -0
- data/lib/riak/core_ext/json.rb +15 -0
- data/lib/riak/core_ext/slice.rb +18 -0
- data/lib/riak/core_ext/stringify_keys.rb +10 -0
- data/lib/riak/core_ext/symbolize_keys.rb +10 -0
- data/lib/riak/core_ext/to_param.rb +31 -0
- data/lib/riak/encoding.rb +6 -0
- data/lib/riak/failed_request.rb +81 -0
- data/lib/riak/i18n.rb +3 -0
- data/lib/riak/json.rb +28 -0
- data/lib/riak/link.rb +85 -0
- data/lib/riak/locale/en.yml +48 -0
- data/lib/riak/map_reduce.rb +206 -0
- data/lib/riak/map_reduce/filter_builder.rb +94 -0
- data/lib/riak/map_reduce/phase.rb +98 -0
- data/lib/riak/map_reduce_error.rb +7 -0
- data/lib/riak/robject.rb +290 -0
- data/lib/riak/search.rb +3 -0
- data/lib/riak/serializers.rb +74 -0
- data/lib/riak/stamp.rb +77 -0
- data/lib/riak/test_server.rb +252 -0
- data/lib/riak/util/escape.rb +45 -0
- data/lib/riak/util/fiber1.8.rb +48 -0
- data/lib/riak/util/headers.rb +53 -0
- data/lib/riak/util/multipart.rb +52 -0
- data/lib/riak/util/multipart/stream_parser.rb +62 -0
- data/lib/riak/util/tcp_socket_extensions.rb +58 -0
- data/lib/riak/util/translation.rb +19 -0
- data/lib/riak/walk_spec.rb +105 -0
- data/riak-client.gemspec +55 -0
- data/seomoz-riak-client.gemspec +55 -0
- data/spec/fixtures/cat.jpg +0 -0
- data/spec/fixtures/multipart-blank.txt +7 -0
- data/spec/fixtures/multipart-mapreduce.txt +10 -0
- data/spec/fixtures/multipart-with-body.txt +16 -0
- data/spec/fixtures/server.cert.crt +15 -0
- data/spec/fixtures/server.cert.key +15 -0
- data/spec/fixtures/test.pem +1 -0
- data/spec/integration/riak/cache_store_spec.rb +154 -0
- data/spec/integration/riak/http_backends_spec.rb +58 -0
- data/spec/integration/riak/protobuffs_backends_spec.rb +32 -0
- data/spec/integration/riak/test_server_spec.rb +161 -0
- data/spec/riak/beefcake_protobuffs_backend_spec.rb +59 -0
- data/spec/riak/bucket_spec.rb +205 -0
- data/spec/riak/client_spec.rb +517 -0
- data/spec/riak/core_ext/to_param_spec.rb +15 -0
- data/spec/riak/escape_spec.rb +69 -0
- data/spec/riak/excon_backend_spec.rb +64 -0
- data/spec/riak/headers_spec.rb +38 -0
- data/spec/riak/http_backend/configuration_spec.rb +51 -0
- data/spec/riak/http_backend/object_methods_spec.rb +217 -0
- data/spec/riak/http_backend/transport_methods_spec.rb +117 -0
- data/spec/riak/http_backend_spec.rb +269 -0
- data/spec/riak/link_spec.rb +71 -0
- data/spec/riak/map_reduce/filter_builder_spec.rb +32 -0
- data/spec/riak/map_reduce/phase_spec.rb +136 -0
- data/spec/riak/map_reduce_spec.rb +310 -0
- data/spec/riak/multipart_spec.rb +23 -0
- data/spec/riak/net_http_backend_spec.rb +16 -0
- data/spec/riak/robject_spec.rb +427 -0
- data/spec/riak/search_spec.rb +178 -0
- data/spec/riak/serializers_spec.rb +93 -0
- data/spec/riak/stamp_spec.rb +54 -0
- data/spec/riak/stream_parser_spec.rb +53 -0
- data/spec/riak/walk_spec_spec.rb +195 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/drb_mock_server.rb +39 -0
- data/spec/support/http_backend_implementation_examples.rb +266 -0
- data/spec/support/integration_setup.rb +10 -0
- data/spec/support/mock_server.rb +81 -0
- data/spec/support/mocks.rb +4 -0
- data/spec/support/test_server.yml.example +2 -0
- data/spec/support/unified_backend_examples.rb +255 -0
- metadata +271 -0
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Riak::Client::HTTPBackend::TransportMethods do
|
4
|
+
before :each do
|
5
|
+
@client = Riak::Client.new
|
6
|
+
@backend = Riak::Client::HTTPBackend.new(@client)
|
7
|
+
@backend.instance_variable_set(:@server_config, {})
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should generate default headers for requests based on the client settings" do
|
11
|
+
@client.client_id = "testing"
|
12
|
+
@backend.default_headers.should == {"X-Riak-ClientId" => "testing", "Accept" => "multipart/mixed, application/json;q=0.7, */*;q=0.5"}
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should generate a root URI based on the client settings" do
|
16
|
+
@backend.root_uri.should be_kind_of(URI)
|
17
|
+
@backend.root_uri.to_s.should == "http://127.0.0.1:8098"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should compute a URI from a relative resource path" do
|
21
|
+
@backend.path("baz").should be_kind_of(URI)
|
22
|
+
@backend.path("foo").to_s.should == "http://127.0.0.1:8098/foo"
|
23
|
+
@backend.path("foo", "bar").to_s.should == "http://127.0.0.1:8098/foo/bar"
|
24
|
+
@backend.path("/foo/bar").to_s.should == "http://127.0.0.1:8098/foo/bar"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should compute a URI from a relative resource path with a hash of query parameters" do
|
28
|
+
@backend.path("baz", :r => 2).to_s.should == "http://127.0.0.1:8098/baz?r=2"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should raise an error if a resource path is too short" do
|
32
|
+
lambda { @backend.verify_path!(["/riak/"]) }.should raise_error(ArgumentError)
|
33
|
+
lambda { @backend.verify_path!(["/riak/", "foo"]) }.should_not raise_error
|
34
|
+
lambda { @backend.verify_path!(["/mapred"]) }.should_not raise_error
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "verify_path_and_body!" do
|
38
|
+
it "should separate the path and body from given arguments" do
|
39
|
+
uri, data = @backend.verify_path_and_body!(["/riak/", "foo", "This is the body."])
|
40
|
+
uri.should == ["/riak/", "foo"]
|
41
|
+
data.should == "This is the body."
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should raise an error if the body is not a string or IO or IO-like (responds to :read)" do
|
45
|
+
lambda { @backend.verify_path_and_body!(["/riak/", "foo", nil]) }.should raise_error(ArgumentError)
|
46
|
+
lambda { @backend.verify_path_and_body!(["/riak/", "foo", File.open("spec/fixtures/cat.jpg")]) }.should_not raise_error(ArgumentError)
|
47
|
+
lambda { @backend.verify_path_and_body!(["/riak/", "foo", Tempfile.new('riak-spec')]) }.should_not raise_error(ArgumentError)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should raise an error if a body is not given" do
|
51
|
+
lambda { @backend.verify_path_and_body!(["/riak/", "foo"])}.should raise_error(ArgumentError)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should raise an error if a path is not given" do
|
55
|
+
lambda { @backend.verify_path_and_body!(["/riak/"])}.should raise_error(ArgumentError)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "detecting valid response codes" do
|
60
|
+
it "should accept strings or integers for either argument" do
|
61
|
+
@backend.should be_valid_response("300", "300")
|
62
|
+
@backend.should be_valid_response(300, "300")
|
63
|
+
@backend.should be_valid_response("300", 300)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should accept an array of strings or integers for the expected code" do
|
67
|
+
@backend.should be_valid_response([200,304], "200")
|
68
|
+
@backend.should be_valid_response(["200",304], "200")
|
69
|
+
@backend.should be_valid_response([200,"304"], "200")
|
70
|
+
@backend.should be_valid_response(["200","304"], "200")
|
71
|
+
@backend.should be_valid_response([200,304], 200)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should be false when none of the response codes match" do
|
75
|
+
@backend.should_not be_valid_response(200, 404)
|
76
|
+
@backend.should_not be_valid_response(["200","304"], 404)
|
77
|
+
@backend.should_not be_valid_response([200,304], 404)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "detecting whether a body should be returned" do
|
82
|
+
it "should be false when the method is :head" do
|
83
|
+
@backend.should_not be_return_body(:head, 200, false)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should be false when the response code is 204, 205, or 304" do
|
87
|
+
@backend.should_not be_return_body(:get, 204, false)
|
88
|
+
@backend.should_not be_return_body(:get, 205, false)
|
89
|
+
@backend.should_not be_return_body(:get, 304, false)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should be false when a streaming block was passed" do
|
93
|
+
@backend.should_not be_return_body(:get, 200, true)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should be true when the method is not head, a code other than 204, 205, or 304 was given, and there was no streaming block" do
|
97
|
+
[:get, :put, :post, :delete].each do |method|
|
98
|
+
[100,101,200,201,202,203,206,300,301,302,303,305,307,400,401,
|
99
|
+
402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,
|
100
|
+
500,501,502,503,504,505].each do |code|
|
101
|
+
@backend.should be_return_body(method, code, false)
|
102
|
+
@backend.should be_return_body(method, code.to_s, false)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should force subclasses to implement the perform method" do
|
109
|
+
lambda { @backend.send(:perform, :get, "/foo", {}, 200) }.should raise_error(NotImplementedError)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should allow using the https protocol" do
|
113
|
+
@client = Riak::Client.new(:protocol => 'https')
|
114
|
+
@backend = Riak::Client::HTTPBackend.new(@client)
|
115
|
+
@backend.root_uri.to_s.should eq("https://127.0.0.1:8098")
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,269 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Riak::Client::HTTPBackend do
|
4
|
+
before :each do
|
5
|
+
@client = Riak::Client.new
|
6
|
+
@backend = Riak::Client::HTTPBackend.new(@client)
|
7
|
+
@backend.instance_variable_set(:@server_config, {})
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should take the Riak::Client when creating" do
|
11
|
+
lambda { Riak::Client::HTTPBackend.new(nil) }.should raise_error(ArgumentError)
|
12
|
+
lambda { Riak::Client::HTTPBackend.new(@client) }.should_not raise_error
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should make the client accessible" do
|
16
|
+
@backend.client.should == @client
|
17
|
+
end
|
18
|
+
|
19
|
+
context "pinging the server" do
|
20
|
+
it "should succeed on 200" do
|
21
|
+
@backend.should_receive(:get).with(200, "/ping", {}, {}).and_return({:code => 200, :body => "OK"})
|
22
|
+
@backend.ping.should be_true
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should fail on any other code or error" do
|
26
|
+
@backend.should_receive(:get).and_raise("socket closed")
|
27
|
+
@backend.ping.should be_false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "fetching an object" do
|
32
|
+
it "should perform a GET request and return an RObject" do
|
33
|
+
@backend.should_receive(:get).with([200,300], "/riak/","foo", "db", {}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
|
34
|
+
@backend.fetch_object("foo", "db").should be_kind_of(Riak::RObject)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should pass the R quorum as a query parameter" do
|
38
|
+
@backend.should_receive(:get).with([200,300], "/riak/","foo", "db", {:r => 2}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
|
39
|
+
@backend.fetch_object("foo", "db", 2)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should escape the bucket and key names" do
|
43
|
+
@backend.should_receive(:get).with([200,300], "/riak/","foo%20", "%20bar", {}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
|
44
|
+
@backend.fetch_object('foo ',' bar').should be_kind_of(Riak::RObject)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "reloading an object" do
|
49
|
+
before do
|
50
|
+
@object = Riak::RObject.new(@client.bucket("foo"), "bar")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should use conditional request headers" do
|
54
|
+
@object.etag = "etag"
|
55
|
+
@backend.should_receive(:get).with([200,300,304], "/riak/", "foo", "bar", {}, {'If-None-Match' => "etag"}).and_return({:code => 304})
|
56
|
+
@backend.reload_object(@object)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should return without modifying the object if the response is 304 Not Modified" do
|
60
|
+
@backend.should_receive(:get).and_return({:code => 304})
|
61
|
+
@backend.should_not_receive(:load_object)
|
62
|
+
@backend.reload_object(@object)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should raise an exception when the response code is not 200 or 304" do
|
66
|
+
@backend.should_receive(:get).and_raise(Riak::HTTPFailedRequest.new(:get, 200, 500, {}, ''))
|
67
|
+
lambda { @backend.reload_object(@object) }.should raise_error(Riak::FailedRequest)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should escape the bucket and key names" do
|
71
|
+
# @bucket.should_receive(:name).and_return("some/deep/path")
|
72
|
+
@object.bucket = @client.bucket("some/deep/path")
|
73
|
+
@object.key = "another/deep/path"
|
74
|
+
@backend.should_receive(:get).with([200,300,304], "/riak/", "some%2Fdeep%2Fpath", "another%2Fdeep%2Fpath", {}, {}).and_return({:code => 304})
|
75
|
+
@backend.reload_object(@object)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "storing an object" do
|
80
|
+
before do
|
81
|
+
@bucket = Riak::Bucket.new(@client, "foo")
|
82
|
+
@object = Riak::RObject.new(@bucket)
|
83
|
+
@object.content_type = "text/plain"
|
84
|
+
@object.data = "This is some text."
|
85
|
+
@headers = @backend.store_headers(@object)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should use the raw_data as the request body" do
|
89
|
+
@object.content_type = "application/json"
|
90
|
+
body = @object.raw_data = "{this is probably invalid json!}}"
|
91
|
+
@backend.stub(:post).and_return({})
|
92
|
+
@object.should_not_receive(:serialize)
|
93
|
+
@backend.store_object(@object, false)
|
94
|
+
end
|
95
|
+
|
96
|
+
context "when the object has no key" do
|
97
|
+
it "should issue a POST request to the bucket, and update the object properties (returning the body by default)" do
|
98
|
+
@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})
|
99
|
+
@backend.store_object(@object, true, nil, nil)
|
100
|
+
@object.key.should == "somereallylongstring"
|
101
|
+
@object.vclock.should == "areallylonghashvalue"
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should include persistence-tuning parameters in the query string" do
|
105
|
+
@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})
|
106
|
+
@backend.store_object(@object, true, nil, 2)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should escape the bucket name" do
|
110
|
+
@object.bucket = @client.bucket("foo ")
|
111
|
+
@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})
|
112
|
+
@backend.store_object(@object, true)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context "when the object has a key" do
|
117
|
+
before :each do
|
118
|
+
@object.key = "bar"
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should issue a PUT request to the bucket, and update the object properties (returning the body by default)" do
|
122
|
+
@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})
|
123
|
+
@backend.store_object(@object, true, nil, nil)
|
124
|
+
@object.key.should == "somereallylongstring"
|
125
|
+
@object.vclock.should == "areallylonghashvalue"
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should include persistence-tuning parameters in the query string" do
|
129
|
+
@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})
|
130
|
+
@backend.store_object(@object, true, 2, nil)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should escape the bucket and key names" do
|
134
|
+
@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})
|
135
|
+
@bucket.instance_variable_set(:@name, "foo ")
|
136
|
+
@object.key = "bar/baz"
|
137
|
+
@backend.store_object(@object, true, nil, nil)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context "deleting an object" do
|
143
|
+
it "should perform a DELETE request" do
|
144
|
+
@backend.should_receive(:delete).with([204,404], "/riak/", "foo", 'bar',{},{}).and_return(:code => 204)
|
145
|
+
@backend.delete_object("foo", "bar")
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should escape the bucket and key names" do
|
149
|
+
@backend.should_receive(:delete).with([204,404], "/riak/", "bucket%20spaces", "deep%2Fpath",{},{}).and_return({:code => 204, :headers => {}})
|
150
|
+
@backend.delete_object("bucket spaces", "deep/path")
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context "fetching bucket properties" do
|
155
|
+
it "should GET the bucket URL and parse the response as JSON" do
|
156
|
+
@backend.should_receive(:get).with(200, "/riak/", "foo", {:keys => false, :props => true}, {}).and_return({:body => '{"props":{"n_val":3}}'})
|
157
|
+
@backend.get_bucket_props("foo").should == {"n_val" => 3}
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should escape the bucket name" do
|
161
|
+
@backend.should_receive(:get).with(200, "/riak/", "foo%20bar", {:keys => false, :props => true}, {}).and_return({:body => '{"props":{"n_val":3}}'})
|
162
|
+
@backend.get_bucket_props("foo bar")
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context "setting bucket properties" do
|
167
|
+
it "should PUT the properties to the bucket URL as JSON" do
|
168
|
+
@backend.should_receive(:put).with(204, "/riak/","foo", '{"props":{"n_val":2}}', {"Content-Type" => "application/json"}).and_return({:body => "", :headers => {}})
|
169
|
+
@backend.set_bucket_props("foo", {:n_val => 2})
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should escape the bucket name" do
|
173
|
+
@backend.should_receive(:put).with(204, "/riak/","foo%20bar", '{"props":{"n_val":2}}', {"Content-Type" => "application/json"}).and_return({:body => "", :headers => {}})
|
174
|
+
@backend.set_bucket_props("foo bar", {:n_val => 2})
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context "listing keys" do
|
179
|
+
it "should unescape key names" do
|
180
|
+
@backend.should_receive(:get).with(200, "/riak/","foo", {:props => false, :keys => true}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar%20baz"]}'})
|
181
|
+
@backend.list_keys("foo").should == ["bar baz"]
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should escape the bucket name" do
|
185
|
+
@backend.should_receive(:get).with(200, "/riak/","unescaped%20", {:props => false, :keys => true}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar"]}'})
|
186
|
+
@backend.list_keys("unescaped ").should == ["bar"]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context "listing buckets" do
|
191
|
+
it "should GET the raw URL with ?buckets=true and parse the response as JSON" do
|
192
|
+
@backend.should_receive(:get).with(200, "/riak/", {:buckets => true}, {}).and_return({:body => '{"buckets":["foo", "bar", "baz"]}'})
|
193
|
+
@backend.list_buckets.should == ["foo", "bar", "baz"]
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context "performing a MapReduce query" do
|
198
|
+
before do
|
199
|
+
@mr = Riak::MapReduce.new(@client).map("Riak.mapValues", :keep => true)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should issue POST request to the mapred endpoint" do
|
203
|
+
@backend.should_receive(:post).with(200, "/mapred", @mr.to_json, hash_including("Content-Type" => "application/json")).and_return({:headers => {'content-type' => ["application/json"]}, :body => "[]"})
|
204
|
+
@backend.mapred(@mr)
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should vivify JSON responses" do
|
208
|
+
@backend.stub!(:post).and_return(:headers => {'content-type' => ["application/json"]}, :body => '[{"key":"value"}]')
|
209
|
+
@backend.mapred(@mr).should == [{"key" => "value"}]
|
210
|
+
end
|
211
|
+
|
212
|
+
it "should return the full response hash for non-JSON responses" do
|
213
|
+
response = {:code => 200, :headers => {'content-type' => ["text/plain"]}, :body => 'This is some text.'}
|
214
|
+
@backend.stub!(:post).and_return(response)
|
215
|
+
@backend.mapred(@mr).should == response
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should stream results through the block" do
|
219
|
+
data = File.read("spec/fixtures/multipart-mapreduce.txt")
|
220
|
+
@backend.should_receive(:post).with(200, "/mapred", {:chunked => true}, @mr.to_json, hash_including("Content-Type" => "application/json")).and_yield(data)
|
221
|
+
block = mock
|
222
|
+
block.should_receive(:ping).twice.and_return(true)
|
223
|
+
@backend.mapred(@mr) do |phase, data|
|
224
|
+
block.ping
|
225
|
+
phase.should == 0
|
226
|
+
data.should have(1).item
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context "getting statistics" do
|
232
|
+
it "should get the status URL and parse the response as JSON" do
|
233
|
+
@backend.should_receive(:get).with(200, "/stats", {}, {}).and_return({:body => '{"vnode_gets":20348}'})
|
234
|
+
@backend.stats.should == {"vnode_gets" => 20348}
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
context "performing a link-walking query" do
|
239
|
+
before do
|
240
|
+
@bucket = Riak::Bucket.new(@client, "foo")
|
241
|
+
@object = Riak::RObject.new(@bucket, "bar")
|
242
|
+
@body = File.read(File.expand_path("#{File.dirname(__FILE__)}/../fixtures/multipart-with-body.txt"))
|
243
|
+
@specs = [Riak::WalkSpec.new(:tag => "next", :keep => true)]
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should perform a GET request for the given object and walk specs" do
|
247
|
+
@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")
|
248
|
+
@backend.link_walk(@object, @specs)
|
249
|
+
end
|
250
|
+
|
251
|
+
it "should parse the results into arrays of objects" do
|
252
|
+
@backend.should_receive(:get).and_return(:headers => {"content-type" => ["multipart/mixed; boundary=5EiMOjuGavQ2IbXAqsJPLLfJNlA"]}, :body => @body)
|
253
|
+
results = @backend.link_walk(@object, @specs)
|
254
|
+
results.should be_kind_of(Array)
|
255
|
+
results.first.should be_kind_of(Array)
|
256
|
+
obj = results.first.first
|
257
|
+
obj.should be_kind_of(Riak::RObject)
|
258
|
+
obj.content_type.should == "text/plain"
|
259
|
+
obj.key.should == "baz"
|
260
|
+
obj.bucket.should == @bucket
|
261
|
+
end
|
262
|
+
|
263
|
+
it "should assign the bucket for newly parsed objects" do
|
264
|
+
@backend.stub!(:get).and_return(:headers => {"content-type" => ["multipart/mixed; boundary=5EiMOjuGavQ2IbXAqsJPLLfJNlA"]}, :body => @body)
|
265
|
+
@client.should_receive(:bucket).with("foo").and_return(@bucket)
|
266
|
+
@backend.link_walk(@object, @specs)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Riak::Link do
|
4
|
+
describe "parsing a link header" do
|
5
|
+
it "should create Link objects from the data" do
|
6
|
+
result = Riak::Link.parse('</riak/foo/bar>; rel="tag", </riak/foo>; rel="up"')
|
7
|
+
result.should be_kind_of(Array)
|
8
|
+
result.should be_all {|i| Riak::Link === i }
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should set the bucket, key, url and rel parameters properly" do
|
12
|
+
result = Riak::Link.parse('</riak/foo/bar>; riaktag="tag", </riak/foo>; rel="up"')
|
13
|
+
result[0].url.should == "/riak/foo/bar"
|
14
|
+
result[0].bucket.should == "foo"
|
15
|
+
result[0].key.should == "bar"
|
16
|
+
result[0].rel.should == "tag"
|
17
|
+
result[1].url.should == "/riak/foo"
|
18
|
+
result[1].bucket.should == "foo"
|
19
|
+
result[1].key.should == nil
|
20
|
+
result[1].rel.should == "up"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should keep the url intact if it does not point to a bucket or bucket/key" do
|
24
|
+
result = Riak::Link.parse('</mapred>; rel="riak_kv_wm_mapred"')
|
25
|
+
result[0].url.should == "/mapred"
|
26
|
+
result[0].bucket.should be_nil
|
27
|
+
result[0].key.should be_nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should convert to a string appropriate for use in the Link header" do
|
32
|
+
Riak::Link.new("/riak/foo", "up").to_s.should == '</riak/foo>; riaktag="up"'
|
33
|
+
Riak::Link.new("/riak/foo/bar", "next").to_s.should == '</riak/foo/bar>; riaktag="next"'
|
34
|
+
Riak::Link.new("/riak", "riak_kv_wm_raw").to_s.should == '</riak>; riaktag="riak_kv_wm_raw"'
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should convert to a walk spec when pointing to an object" do
|
38
|
+
Riak::Link.new("/riak/foo/bar", "next").to_walk_spec.to_s.should == "foo,next,_"
|
39
|
+
lambda { Riak::Link.new("/riak/foo", "up").to_walk_spec }.should raise_error
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should be equivalent to a link with the same url and rel" do
|
43
|
+
one = Riak::Link.new("/riak/foo/bar", "next")
|
44
|
+
two = Riak::Link.new("/riak/foo/bar", "next")
|
45
|
+
one.should == two
|
46
|
+
[one].should include(two)
|
47
|
+
[two].should include(one)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should unescape the bucket name" do
|
51
|
+
Riak::Link.new("/riak/bucket%20spaces/key", "foo").bucket.should == "bucket spaces"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should unescape the key name" do
|
55
|
+
Riak::Link.new("/riak/bucket/key%2Fname", "foo").key.should == "key/name"
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should not rely on the prefix to equal /riak/ when extracting the bucket and key" do
|
59
|
+
link = Riak::Link.new("/raw/bucket/key", "foo")
|
60
|
+
link.bucket.should == "bucket"
|
61
|
+
link.key.should == "key"
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should construct from bucket, key and tag" do
|
65
|
+
link = Riak::Link.new("bucket", "key", "tag")
|
66
|
+
link.bucket.should == "bucket"
|
67
|
+
link.key.should == "key"
|
68
|
+
link.tag.should == "tag"
|
69
|
+
link.url.should == "/riak/bucket/key"
|
70
|
+
end
|
71
|
+
end
|