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,178 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Search features" do
|
4
|
+
describe Riak::Client do
|
5
|
+
before :each do
|
6
|
+
@client = Riak::Client.new
|
7
|
+
@http = mock(Riak::Client::HTTPBackend)
|
8
|
+
@client.stub!(:http).and_return(@http)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "searching" do
|
12
|
+
it "should exclude the index from the URL when not specified" do
|
13
|
+
@http.should_receive(:get).with(200, "/solr", "select", hash_including("q" => "foo"), {}).and_return({:code => 200, :headers => {"content-type"=>["application/json"]}, :body => "{}"})
|
14
|
+
@client.search("foo")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should include extra options in the query string" do
|
18
|
+
@http.should_receive(:get).with(200, "/solr", "select", hash_including('rows' => 30), {}).and_return({:code => 200, :headers => {"content-type"=>["application/json"]}, :body => "{}"})
|
19
|
+
@client.search("foo", 'rows' => 30)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should include the index in the URL when specified" do
|
23
|
+
@http.should_receive(:get).with(200, "/solr", "search", "select", hash_including("q" => "foo"), {}).and_return({:code => 200, :headers => {"content-type"=>["application/json"]}, :body => "{}"})
|
24
|
+
@client.search("search", "foo")
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should vivify JSON responses" do
|
28
|
+
@http.should_receive(:get).and_return({:code => 200, :headers => {"content-type"=>["application/json"]}, :body => '{"response":{"docs":["foo"]}}'})
|
29
|
+
@client.search("foo").should == {"response" => {"docs" => ["foo"]}}
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return non-JSON responses raw" do
|
33
|
+
@http.should_receive(:get).and_return({:code => 200, :headers => {"content-type"=>["text/plain"]}, :body => '{"response":{"docs":["foo"]}}'})
|
34
|
+
@client.search("foo").should == '{"response":{"docs":["foo"]}}'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
describe "indexing documents" do
|
38
|
+
it "should exclude the index from the URL when not specified" do
|
39
|
+
@http.should_receive(:post).with(200, "/solr", "update", anything, anything).and_return({:code => 200, :headers => {'content-type' => ['text/html']}, :body => ""})
|
40
|
+
@client.index({:id => 1, :field => "value"})
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should include the index in the URL when specified" do
|
44
|
+
@http.should_receive(:post).with(200, "/solr", "foo", "update", anything, anything).and_return({:code => 200, :headers => {'content-type' => ['text/html']}, :body => ""})
|
45
|
+
@client.index("foo", {:id => 1, :field => "value"})
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should raise an error when documents do not contain an id" do
|
49
|
+
@http.stub!(:post).and_return(true)
|
50
|
+
lambda { @client.index({:field => "value"}) }.should raise_error(ArgumentError)
|
51
|
+
lambda { @client.index({:id => 1, :field => "value"}) }.should_not raise_error(ArgumentError)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should build a Solr <add> request" do
|
55
|
+
expect_update_body('<add><doc><field name="id">1</field><field name="field">value</field></doc></add>')
|
56
|
+
@client.index({'id' => 1, 'field' => "value"})
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should include multiple documents in the <add> request" do
|
60
|
+
expect_update_body('<add><doc><field name="id">1</field><field name="field">value</field></doc><doc><field name="id">2</field><field name="foo">bar</field></doc></add>')
|
61
|
+
@client.index({'id' => 1, 'field' => "value"}, {'id' => 2, 'foo' => "bar"})
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "removing documents" do
|
66
|
+
it "should exclude the index from the URL when not specified" do
|
67
|
+
@http.should_receive(:post).with(200, "/solr","update", anything, anything).and_return({:code => 200, :headers => {'content-type' => ['text/html']}, :body => ""})
|
68
|
+
@client.remove({:id => 1})
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should include the index in the URL when specified" do
|
72
|
+
@http.should_receive(:post).with(200, "/solr", "foo", "update", anything, anything).and_return({:code => 200, :headers => {'content-type' => ['text/html']}, :body => ""})
|
73
|
+
@client.remove("foo", {:id => 1})
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should raise an error when document specifications don't include an id or query" do
|
77
|
+
@http.stub!(:post).and_return({:code => 200})
|
78
|
+
lambda { @client.remove({:foo => "bar"}) }.should raise_error(ArgumentError)
|
79
|
+
lambda { @client.remove({:id => 1}) }.should_not raise_error
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should build a Solr <delete> request" do
|
83
|
+
expect_update_body('<delete><id>1</id></delete>')
|
84
|
+
@client.remove(:id => 1)
|
85
|
+
expect_update_body('<delete><query>title:old</query></delete>')
|
86
|
+
@client.remove(:query => "title:old")
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should include multiple specs in the <delete> request" do
|
90
|
+
expect_update_body('<delete><id>1</id><query>title:old</query></delete>')
|
91
|
+
@client.remove({:id => 1}, {:query => "title:old"})
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def expect_update_body(body, index=nil)
|
96
|
+
args = [200, "/solr", index, "update", body, {"Content-Type" => "text/xml"}].compact
|
97
|
+
@http.should_receive(:post).with(*args).and_return({:code => 200, :headers => {'content-type' => ['text/html']}, :body => ""})
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe Riak::Bucket do
|
102
|
+
before :each do
|
103
|
+
@client = Riak::Client.new
|
104
|
+
@bucket = Riak::Bucket.new(@client, "foo")
|
105
|
+
end
|
106
|
+
|
107
|
+
def load_without_index_hook
|
108
|
+
@bucket.instance_variable_set(:@props, {"precommit" => []})
|
109
|
+
end
|
110
|
+
|
111
|
+
def load_with_index_hook
|
112
|
+
@bucket.instance_variable_set(:@props, {"precommit" => [{"mod" => "riak_search_kv_hook", "fun" => "precommit"}]})
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should detect whether the indexing hook is installed" do
|
116
|
+
load_without_index_hook
|
117
|
+
@bucket.is_indexed?.should be_false
|
118
|
+
|
119
|
+
load_with_index_hook
|
120
|
+
@bucket.is_indexed?.should be_true
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "enabling indexing" do
|
124
|
+
it "should add the index hook when not present" do
|
125
|
+
load_without_index_hook
|
126
|
+
@bucket.should_receive(:props=).with({"precommit" => [Riak::Bucket::SEARCH_PRECOMMIT_HOOK]})
|
127
|
+
@bucket.enable_index!
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should not modify the precommit when the hook is present" do
|
131
|
+
load_with_index_hook
|
132
|
+
@bucket.should_not_receive(:props=)
|
133
|
+
@bucket.enable_index!
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe "disabling indexing" do
|
138
|
+
it "should remove the index hook when present" do
|
139
|
+
load_with_index_hook
|
140
|
+
@bucket.should_receive(:props=).with({"precommit" => []})
|
141
|
+
@bucket.disable_index!
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should not modify the precommit when the hook is missing" do
|
145
|
+
load_without_index_hook
|
146
|
+
@bucket.should_not_receive(:props=)
|
147
|
+
@bucket.disable_index!
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe Riak::MapReduce do
|
153
|
+
before :each do
|
154
|
+
@client = Riak::Client.new
|
155
|
+
@mr = Riak::MapReduce.new(@client)
|
156
|
+
end
|
157
|
+
|
158
|
+
describe "using a search query as inputs" do
|
159
|
+
it "should accept a bucket name and query" do
|
160
|
+
@mr.search("foo", "bar OR baz")
|
161
|
+
@mr.inputs.should == {:module => "riak_search", :function => "mapred_search", :arg => ["foo", "bar OR baz"]}
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should accept a Riak::Bucket and query" do
|
165
|
+
@mr.search(Riak::Bucket.new(@client, "foo"), "bar OR baz")
|
166
|
+
@mr.inputs.should == {:module => "riak_search", :function => "mapred_search", :arg => ["foo", "bar OR baz"]}
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should emit the Erlang function and arguments" do
|
170
|
+
@mr.search("foo", "bar OR baz")
|
171
|
+
@mr.to_json.should include('"inputs":{')
|
172
|
+
@mr.to_json.should include('"module":"riak_search"')
|
173
|
+
@mr.to_json.should include('"function":"mapred_search"')
|
174
|
+
@mr.to_json.should include('"arg":["foo","bar OR baz"]')
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Riak::Serializers do
|
4
|
+
shared_examples_for "a serializer" do |type, deserialized, serialized|
|
5
|
+
context "for #{type}" do
|
6
|
+
it "serializes #{deserialized} to #{serialized}" do
|
7
|
+
described_class.serialize(type, deserialized).should == serialized
|
8
|
+
end
|
9
|
+
|
10
|
+
it "deserializes #{serialized} to #{deserialized}" do
|
11
|
+
described_class.deserialize(type, serialized).should == deserialized
|
12
|
+
end
|
13
|
+
|
14
|
+
it "round trips properly" do
|
15
|
+
str = described_class.serialize(type, deserialized)
|
16
|
+
described_class.deserialize(type, str).should == deserialized
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it_behaves_like "a serializer", "text/plain", "a string", "a string"
|
22
|
+
it_behaves_like "a serializer", "application/json", { "a" => 7 }, %q|{"a":7}|
|
23
|
+
it_behaves_like "a serializer", "application/x-ruby-marshal", { :a => 3 }, Marshal.dump({ :a => 3 })
|
24
|
+
|
25
|
+
described_class::YAML_MIME_TYPES.each do |mime_type|
|
26
|
+
it_behaves_like "a serializer", mime_type, { "a" => 7 }, YAML.dump({ "a" => 7 })
|
27
|
+
end
|
28
|
+
|
29
|
+
%w[ serialize deserialize ].each do |meth|
|
30
|
+
describe ".#{meth}" do
|
31
|
+
it 'raises a NotImplementedError when given an unrecognized content type' do
|
32
|
+
expect {
|
33
|
+
described_class.send(meth, "application/unrecognized", "string")
|
34
|
+
}.to raise_error(NotImplementedError)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "plain text serializer" do
|
40
|
+
it 'calls #to_s to convert the object to a string' do
|
41
|
+
described_class.serialize("text/plain", :a_string).should == "a_string"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "JSON serializer" do
|
46
|
+
it "respects the max nesting option" do
|
47
|
+
# Sadly, this spec will not fail for me when using yajl-ruby
|
48
|
+
# on Ruby 1.9, even when passing the options to #to_json is
|
49
|
+
# not implemented.
|
50
|
+
Riak.json_options = {:max_nesting => 51}
|
51
|
+
h = {}
|
52
|
+
p = h
|
53
|
+
(1..50).each do |i|
|
54
|
+
p['a'] = {}
|
55
|
+
p = p['a']
|
56
|
+
end
|
57
|
+
s = h.to_json(Riak.json_options)
|
58
|
+
expect {
|
59
|
+
described_class.serialize('application/json', h)
|
60
|
+
}.should_not raise_error
|
61
|
+
|
62
|
+
expect {
|
63
|
+
described_class.deserialize('application/json', s)
|
64
|
+
}.should_not raise_error
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "a custom serializer" do
|
69
|
+
let(:custom_serializer) do
|
70
|
+
Object.new.tap do |o|
|
71
|
+
def o.dump(string)
|
72
|
+
"The string is: #{string}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def o.load(string)
|
76
|
+
string.sub!(/^The string is: /, '')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'can be registered' do
|
82
|
+
described_class['application/custom-type-1'] = custom_serializer
|
83
|
+
described_class['application/custom-type-1'].should be(custom_serializer)
|
84
|
+
end
|
85
|
+
|
86
|
+
it_behaves_like "a serializer", "application/custom-type-a", "foo", "The string is: foo" do
|
87
|
+
before(:each) do
|
88
|
+
described_class['application/custom-type-a'] = custom_serializer
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'riak/stamp'
|
3
|
+
|
4
|
+
describe Riak::Stamp do
|
5
|
+
subject { described_class.new(Riak::Client.new) }
|
6
|
+
it "should generate always increasing integer identifiers" do
|
7
|
+
1000.times do
|
8
|
+
one = subject.next
|
9
|
+
two = subject.next
|
10
|
+
[one, two].should be_all {|i| Integer === i }
|
11
|
+
two.should > one
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should delay until the next millisecond when the sequence overflows" do
|
16
|
+
old = subject.instance_variable_get(:@timestamp) + 0
|
17
|
+
subject.instance_variable_set(:@sequence, described_class::SEQUENCE_MASK)
|
18
|
+
count = 0
|
19
|
+
# Simulate the time_gen method returning the same thing multiple times
|
20
|
+
subject.stub(:time_gen) do
|
21
|
+
count += 1
|
22
|
+
if count < 10
|
23
|
+
old
|
24
|
+
else
|
25
|
+
old + 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
((subject.next >> described_class::TIMESTAMP_SHIFT) & described_class::TIMESTAMP_MASK).should == old + 1
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should raise an exception when the system clock moves backwards" do
|
32
|
+
old = subject.instance_variable_get(:@timestamp)
|
33
|
+
subject.should_receive(:time_gen).and_return(old - 10)
|
34
|
+
expect {
|
35
|
+
subject.next
|
36
|
+
}.to raise_error(Riak::BackwardsClockError)
|
37
|
+
end
|
38
|
+
|
39
|
+
# The client/worker ID should be used for disambiguation, not for
|
40
|
+
# primary ordering. This breaks from the Snowflake model where the
|
41
|
+
# worker ID is in more significant bits.
|
42
|
+
it "should use the client ID as the bottom component of the identifier" do
|
43
|
+
(subject.next & described_class::CLIENT_ID_MASK).should == subject.client.client_id & described_class::CLIENT_ID_MASK
|
44
|
+
end
|
45
|
+
|
46
|
+
context "using a non-integer client ID" do
|
47
|
+
subject { described_class.new(Riak::Client.new(:client_id => "ripple")) }
|
48
|
+
let(:hash) { "ripple".hash }
|
49
|
+
|
50
|
+
it "should use the hash of the client ID as the bottom component of the identifier" do
|
51
|
+
(subject.next & described_class::CLIENT_ID_MASK).should == subject.client.client_id.hash & described_class::CLIENT_ID_MASK
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Riak::Util::Multipart::StreamParser do
|
4
|
+
let(:klass) { Riak::Util::Multipart::StreamParser }
|
5
|
+
let(:block) { mock }
|
6
|
+
it "should detect the initial boundary" do
|
7
|
+
text = "--boundary1\r\nContent-Type: text/plain\r\n\r\nfoo\r\n--boundary1--\r\n"
|
8
|
+
parser = klass.new do |result|
|
9
|
+
result[:headers]['content-type'].should include("text/plain")
|
10
|
+
result[:body].should == "foo"
|
11
|
+
end
|
12
|
+
parser.accept text
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should detect inner multipart bodies" do
|
16
|
+
block.should_receive(:ping).once.and_return(true)
|
17
|
+
parser = klass.new do |result|
|
18
|
+
block.ping
|
19
|
+
result.should have(1).item
|
20
|
+
result.first[:headers]['content-type'].should include("text/plain")
|
21
|
+
result.first[:body].should == "SCP sloooow...."
|
22
|
+
end
|
23
|
+
File.open("spec/fixtures/multipart-with-body.txt", "r") do |f|
|
24
|
+
while chunk = f.read(16)
|
25
|
+
parser.accept chunk
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should yield successive complete chunks to the block" do
|
31
|
+
block.should_receive(:ping).twice.and_return(true)
|
32
|
+
parser = klass.new do |result|
|
33
|
+
block.ping
|
34
|
+
result[:headers]['content-type'].should include("application/json")
|
35
|
+
lambda { Riak::JSON.parse(result[:body]) }.should_not raise_error
|
36
|
+
end
|
37
|
+
File.open("spec/fixtures/multipart-mapreduce.txt", "r") do |f|
|
38
|
+
while chunk = f.read(16)
|
39
|
+
parser.accept chunk
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should yield successive complete bodies to the block, even when multiple bodies are accepted in a single chunk" do
|
45
|
+
block.should_receive(:ping).twice.and_return(true)
|
46
|
+
parser = klass.new do |result|
|
47
|
+
block.ping
|
48
|
+
result[:headers]['content-type'].should include("application/json")
|
49
|
+
lambda { Riak::JSON.parse(result[:body]) }.should_not raise_error
|
50
|
+
end
|
51
|
+
parser.accept File.read("spec/fixtures/multipart-mapreduce.txt")
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Riak::WalkSpec do
|
4
|
+
describe "initializing" do
|
5
|
+
describe "with a hash" do
|
6
|
+
it "should be empty by default" do
|
7
|
+
spec = Riak::WalkSpec.new({})
|
8
|
+
spec.bucket.should == "_"
|
9
|
+
spec.tag.should == "_"
|
10
|
+
spec.keep.should be_false
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should extract the bucket" do
|
14
|
+
spec = Riak::WalkSpec.new({:bucket => "foo"})
|
15
|
+
spec.bucket.should == "foo"
|
16
|
+
spec.tag.should == "_"
|
17
|
+
spec.keep.should be_false
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should extract the tag" do
|
21
|
+
spec = Riak::WalkSpec.new({:tag => "foo"})
|
22
|
+
spec.bucket.should == "_"
|
23
|
+
spec.tag.should == "foo"
|
24
|
+
spec.keep.should be_false
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should extract the keep" do
|
28
|
+
spec = Riak::WalkSpec.new({:keep => true})
|
29
|
+
spec.bucket.should == "_"
|
30
|
+
spec.tag.should == "_"
|
31
|
+
spec.keep.should be_true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "with three arguments for bucket, tag, and keep" do
|
36
|
+
it "should assign the bucket, tag, and keep" do
|
37
|
+
spec = Riak::WalkSpec.new("foo", "next", false)
|
38
|
+
spec.bucket.should == "foo"
|
39
|
+
spec.tag.should == "next"
|
40
|
+
spec.keep.should be_false
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should make the bucket '_' when false or nil" do
|
44
|
+
spec = Riak::WalkSpec.new(nil, "next", false)
|
45
|
+
spec.bucket.should == "_"
|
46
|
+
spec = Riak::WalkSpec.new(false, "next", false)
|
47
|
+
spec.bucket.should == "_"
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should make the tag '_' when false or nil" do
|
51
|
+
spec = Riak::WalkSpec.new("foo", nil, false)
|
52
|
+
spec.tag.should == "_"
|
53
|
+
spec = Riak::WalkSpec.new("foo", false, false)
|
54
|
+
spec.tag.should == "_"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should make the keep false when false or nil" do
|
58
|
+
spec = Riak::WalkSpec.new(nil, nil, nil)
|
59
|
+
spec.keep.should be_false
|
60
|
+
spec = Riak::WalkSpec.new(nil, nil, false)
|
61
|
+
spec.keep.should be_false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should raise an ArgumentError for invalid arguments" do
|
66
|
+
lambda { Riak::WalkSpec.new }.should raise_error(ArgumentError)
|
67
|
+
lambda { Riak::WalkSpec.new("foo") }.should raise_error(ArgumentError)
|
68
|
+
lambda { Riak::WalkSpec.new("foo","bar") }.should raise_error(ArgumentError)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "converting to a string" do
|
73
|
+
before :each do
|
74
|
+
@spec = Riak::WalkSpec.new({})
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should be the empty spec by default" do
|
78
|
+
@spec.to_s.should == "_,_,_"
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should include the bucket when set" do
|
82
|
+
@spec.bucket = "foo"
|
83
|
+
@spec.to_s.should == "foo,_,_"
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should include the tag when set" do
|
87
|
+
@spec.tag = "next"
|
88
|
+
@spec.to_s.should == "_,next,_"
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should include the keep when true" do
|
92
|
+
@spec.keep = true
|
93
|
+
@spec.to_s.should == "_,_,1"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "creating from a list of parameters" do
|
98
|
+
it "should detect hashes and WalkSpecs interleaved with other parameters" do
|
99
|
+
specs = Riak::WalkSpec.normalize(nil,"next",nil,{:bucket => "foo"},Riak::WalkSpec.new({:tag => "child", :keep => true}))
|
100
|
+
specs.should have(3).items
|
101
|
+
specs.should be_all {|s| s.kind_of?(Riak::WalkSpec) }
|
102
|
+
specs.join("/").should == "_,next,_/foo,_,_/_,child,1"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "matching other objects with ===" do
|
107
|
+
before :each do
|
108
|
+
@spec = Riak::WalkSpec.new({})
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should not match objects that aren't links or walk specs" do
|
112
|
+
@spec.should_not === "foo"
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "matching links" do
|
116
|
+
before :each do
|
117
|
+
@link = Riak::Link.new("/riak/foo/bar", "next")
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should match a link when the bucket and tag are not specified" do
|
121
|
+
@spec.should === @link
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should match a link when the bucket is the same" do
|
125
|
+
@spec.bucket = "foo"
|
126
|
+
@spec.should === @link
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should not match a link when the bucket is different" do
|
130
|
+
@spec.bucket = "bar"
|
131
|
+
@spec.should_not === @link
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should match a link when the tag is the same" do
|
135
|
+
@spec.tag = "next"
|
136
|
+
@spec.should === @link
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should not match a link when the tag is different" do
|
140
|
+
@spec.tag = "previous"
|
141
|
+
@spec.should_not === @link
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should match a link when the bucket and tag are the same" do
|
145
|
+
@spec.bucket = "foo"
|
146
|
+
@spec.should === @link
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "matching walk specs" do
|
151
|
+
before :each do
|
152
|
+
@other = Riak::WalkSpec.new({})
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should match a walk spec that is equivalent" do
|
156
|
+
@spec.should === @other
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should not match a walk spec that has a different keep value" do
|
160
|
+
@other.keep = true
|
161
|
+
@spec.should_not === @other
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should match a walk spec with a more specific bucket" do
|
165
|
+
@other.bucket = "foo"
|
166
|
+
@spec.should === @other
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should match a walk spec with the same bucket" do
|
170
|
+
@other.bucket = "foo"; @spec.bucket = "foo"
|
171
|
+
@spec.should === @other
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should not match a walk spec with a different bucket" do
|
175
|
+
@other.bucket = "foo"; @spec.bucket = "bar"
|
176
|
+
@spec.should_not === @other
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should match a walk spec with a more specific tag" do
|
180
|
+
@other.tag = "next"
|
181
|
+
@spec.should === @other
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should match a walk spec with the same tag" do
|
185
|
+
@other.tag = "next"; @spec.tag = "next"
|
186
|
+
@spec.should === @other
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should not match a walk spec with a different tag" do
|
190
|
+
@other.tag = "next"; @spec.tag = "previous"
|
191
|
+
@spec.should_not === @other
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|