riakpb 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +35 -0
- data/Rakefile +44 -76
- data/lib/benchmark/bench_stocks.rb +26 -0
- data/lib/riakpb/client/rpc.rb +7 -7
- data/lib/riakpb/version.rb +3 -0
- data/lib/riakpb.rb +0 -2
- data/load_stocks.rb +10 -2
- data/riak-pbclient.tmproj +157 -0
- data/riakpb.gemspec +28 -0
- data/spec/riak/bucket_spec.rb +105 -0
- data/spec/riak/client_spec.rb +199 -0
- data/spec/riak/content_spec.rb +74 -0
- data/spec/riak/key_spec.rb +54 -0
- data/spec/riak/map_reduce_spec.rb +327 -0
- data/spec/riak/rpc_spec.rb +4 -0
- metadata +82 -33
- data/History.txt +0 -4
- data/Manifest.txt +0 -30
- data/script/console +0 -10
- data/script/destroy +0 -14
- data/script/generate +0 -14
@@ -0,0 +1,199 @@
|
|
1
|
+
require File.expand_path("../spec_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Riakpb::Client do
|
4
|
+
describe "when initializing" do
|
5
|
+
it "should default to the local interface on port 8087" do
|
6
|
+
client = Riakpb::Client.new
|
7
|
+
client.host.should == "127.0.0.1"
|
8
|
+
client.port.should == 8087
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should accept a host" do
|
12
|
+
client = Riakpb::Client.new :host => "riak.basho.com"
|
13
|
+
client.host.should == "riak.basho.com"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should accept a port" do
|
17
|
+
client = Riakpb::Client.new :port => 9000
|
18
|
+
client.port.should == 9000
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "reconfiguring" do
|
23
|
+
before :each do
|
24
|
+
@client = Riakpb::Client.new
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "setting the host" do
|
28
|
+
it "should allow setting the host" do
|
29
|
+
@client.should respond_to(:host=)
|
30
|
+
@client.host = "riak.basho.com"
|
31
|
+
@client.host.should == "riak.basho.com"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should require the host to be an IP or hostname" do
|
35
|
+
[238472384972, "", "riak.basho-.com"].each do |invalid|
|
36
|
+
lambda { @client.host = invalid }.should raise_error(ArgumentError)
|
37
|
+
end
|
38
|
+
["127.0.0.1", "10.0.100.5", "localhost", "otherhost.local", "riak.basho.com"].each do |valid|
|
39
|
+
lambda { @client.host = valid }.should_not raise_error
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end # describe "setting the host"
|
43
|
+
|
44
|
+
describe "setting the port" do
|
45
|
+
it "should allow setting the port" do
|
46
|
+
@client.should respond_to(:port=)
|
47
|
+
@client.port = 9000
|
48
|
+
@client.port.should == 9000
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should require the port to be a valid number" do
|
52
|
+
[-1,65536,"foo"].each do |invalid|
|
53
|
+
lambda { @client.port = invalid }.should raise_error(ArgumentError)
|
54
|
+
end
|
55
|
+
[0,1,65535,8098].each do |valid|
|
56
|
+
lambda { @client.port = valid }.should_not raise_error
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end # describe "setting the port"
|
60
|
+
|
61
|
+
describe "setting the client id" do
|
62
|
+
it "should accept a string unmodified" do
|
63
|
+
@client.client_id = "foo"
|
64
|
+
@client.client_id.should == "foo"
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should base64-encode an integer" do
|
68
|
+
@client.client_id = 1
|
69
|
+
@client.client_id.should == "AAAAAQ=="
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should reject an integer equal to the maximum client id" do
|
73
|
+
lambda { @client.client_id = Riakpb::Client::MAX_CLIENT_ID }.should raise_error(ArgumentError)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should reject an integer larger than the maximum client id" do
|
77
|
+
lambda { @client.client_id = Riakpb::Client::MAX_CLIENT_ID + 1 }.should raise_error(ArgumentError)
|
78
|
+
end
|
79
|
+
end # describe "setting the client id"
|
80
|
+
end # describe "reconfiguring"
|
81
|
+
|
82
|
+
describe "sending and receiving protocol buffers" do
|
83
|
+
before :each do
|
84
|
+
@client = Riakpb::Client.new
|
85
|
+
@client.rpc.stub!(:status).and_return(true)
|
86
|
+
@client.rpc.stub!(:request).and_return(nil)
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "basic communication with riak node" do
|
90
|
+
it "should send a ping request and return true" do
|
91
|
+
@client.rpc.stub!(:request).with(
|
92
|
+
Riakpb::Util::MessageCode::PING_REQUEST
|
93
|
+
).and_return('')
|
94
|
+
|
95
|
+
@client.ping?.should == true
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should request the connected riak node's server info and return a Hash" do
|
99
|
+
# test length or content? Need to look at what are considered acceptable values
|
100
|
+
@client.rpc.stub!(:request).with(
|
101
|
+
Riakpb::Util::MessageCode::GET_SERVER_INFO_REQUEST
|
102
|
+
).and_return(Riakpb::RpbGetServerInfoResp.new(
|
103
|
+
{ :node => "riak@127.0.0.1",
|
104
|
+
:server_version => "0.10.1"
|
105
|
+
}
|
106
|
+
))
|
107
|
+
|
108
|
+
@client.info[:node].should be_kind_of(String)
|
109
|
+
@client.info[:server_version].should be_kind_of(String)
|
110
|
+
end
|
111
|
+
end # describe "basic communication with riak node"
|
112
|
+
|
113
|
+
describe "bucket operations: retrieval (get) and send (set)" do
|
114
|
+
|
115
|
+
describe "bucket retrieval (get)" do
|
116
|
+
it "should send a request to list available bucket names and return a Protobuf::Field::FieldArray" do
|
117
|
+
@client.rpc.stub!(:request).with(
|
118
|
+
Riakpb::Util::MessageCode::LIST_BUCKETS_REQUEST
|
119
|
+
).and_return(
|
120
|
+
Riakpb::RpbListBucketsResp.new(
|
121
|
+
{ :buckets => ["goog"] }
|
122
|
+
))
|
123
|
+
|
124
|
+
@client.buckets.should be_kind_of(Protobuf::Field::FieldArray)
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should send a request with the bucket name and return a Riakpb::Bucket" do
|
128
|
+
@client.rpc.stub!(:request).with(
|
129
|
+
Riakpb::Util::MessageCode::GET_BUCKET_REQUEST,
|
130
|
+
Riakpb::RpbGetBucketReq.new(:bucket => "goog")
|
131
|
+
).and_return(
|
132
|
+
Riakpb::RpbGetBucketResp.new(
|
133
|
+
{ :props => {
|
134
|
+
:allow_mult => false,
|
135
|
+
:n_val => 3
|
136
|
+
}
|
137
|
+
}
|
138
|
+
))
|
139
|
+
|
140
|
+
@client.bucket("goog").should be_kind_of(Riakpb::Bucket)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should send a request to list keys within a bucket and return a Protobuf::Field::FieldArray" do
|
144
|
+
@client.rpc.stub!(:request).with(
|
145
|
+
Riakpb::Util::MessageCode::LIST_KEYS_REQUEST,
|
146
|
+
Riakpb::RpbListKeysReq.new(:bucket => "goog")
|
147
|
+
).and_return(
|
148
|
+
Riakpb::RpbListKeysResp.new(
|
149
|
+
{ :keys => ["2010-04-12", "2008-01-10", "2006-06-06"],
|
150
|
+
:done => true
|
151
|
+
}
|
152
|
+
))
|
153
|
+
@client.keys_in("goog").should be_kind_of(Protobuf::Field::FieldArray)
|
154
|
+
end
|
155
|
+
end # describe "bucket retrieval (get)"
|
156
|
+
|
157
|
+
describe "bucket sending (set)" do
|
158
|
+
end # describe "bucket sending (set)"
|
159
|
+
end # describe "bucket operations and retrieval"
|
160
|
+
|
161
|
+
describe "key operations: retrieval (get), send (put) and delete (del)" do
|
162
|
+
before :each do
|
163
|
+
@client.rpc.stub!(:request).with(
|
164
|
+
Riakpb::Util::MessageCode::GET_REQUEST,
|
165
|
+
Riakpb::RpbGetReq.new(:bucket => "goog", :key => "2010-04-12", :r => nil)
|
166
|
+
).and_return(
|
167
|
+
Riakpb::RpbGetResp.new(
|
168
|
+
{ :content => [],
|
169
|
+
:vclock => ""
|
170
|
+
}
|
171
|
+
))
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should send a request for a bucket/key pair and return a Riakpb::RpbGetResp" do
|
175
|
+
@client.get_request("goog", "2010-04-12").should be_kind_of(Riakpb::RpbGetResp)
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should have a vclock attribute within Riakpb::RpbGetResp of that is a String" do
|
179
|
+
@client.get_request("goog", "2010-04-12").vclock.should be_kind_of(String)
|
180
|
+
end
|
181
|
+
end # describe "key operations and retrieval"
|
182
|
+
|
183
|
+
describe "key operations and retrieval" do
|
184
|
+
before :each do
|
185
|
+
@client.rpc.stub!(:request).with(
|
186
|
+
Riakpb::Util::MessageCode::GET_REQUEST,
|
187
|
+
Riakpb::RpbGetReq.new(:bucket => "goog", :key => "2010-04-12", :r => nil)
|
188
|
+
).and_return(
|
189
|
+
Riakpb::RpbGetResp.new(
|
190
|
+
{ :content => [],
|
191
|
+
:vclock => ""
|
192
|
+
}
|
193
|
+
))
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
end # describe "key operations and retrieval"
|
198
|
+
end # describe "basic communication with riak node"
|
199
|
+
end # Riakpb::Client
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require File.expand_path("../spec_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Riakpb::Content do
|
4
|
+
describe "when directly initializing" do
|
5
|
+
before :each do
|
6
|
+
@client = Riakpb::Client.new
|
7
|
+
@client.rpc.stub!(:request).with(
|
8
|
+
Riakpb::Util::MessageCode::GET_BUCKET_REQUEST,
|
9
|
+
Riakpb::RpbGetBucketReq.new(:bucket => "goog")
|
10
|
+
).and_return(
|
11
|
+
Riakpb::RpbGetBucketResp.new(
|
12
|
+
{ :props => {
|
13
|
+
:allow_mult => false,
|
14
|
+
:n_val => 3
|
15
|
+
}
|
16
|
+
}
|
17
|
+
))
|
18
|
+
@bucket = @client["goog"]
|
19
|
+
@key = Riakpb::Key.new(@bucket, "test")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should default with nil attributes and links/usermeta as instances of Set/Hash" do
|
23
|
+
rcontent = Riakpb::Content.new(@key)
|
24
|
+
rcontent.key.should == @key
|
25
|
+
rcontent.value.should == nil
|
26
|
+
rcontent.content_type.should == nil
|
27
|
+
rcontent.charset.should == nil
|
28
|
+
rcontent.content_encoding.should == nil
|
29
|
+
rcontent.vtag.should == nil
|
30
|
+
rcontent.links.should be_kind_of(Hash)
|
31
|
+
rcontent.last_mod.should be_kind_of(Time)
|
32
|
+
rcontent.last_mod_usecs.should == nil
|
33
|
+
rcontent.usermeta.should be_kind_of(Hash)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should allow you to set the Key, after initialization" do
|
37
|
+
rcontent = Riakpb::Content.new(@key)
|
38
|
+
rcontent.key = @key
|
39
|
+
rcontent.key.should == @key
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should accept a Key as an argument to new, tying it back to an owner" do
|
43
|
+
rcontent = Riakpb::Content.new(@key)
|
44
|
+
rcontent.key.should == @key
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should serialize into a corresponding Protocol Buffer (RpbContent)" do
|
48
|
+
rcontent = Riakpb::Content.new(@key, :value => "Test")
|
49
|
+
rcontent.to_pb.should be_kind_of(Riakpb::RpbContent)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should load a Riakpb::RpbContent instance, returning a matching self, Content" do
|
53
|
+
rcontent = Riakpb::Content.new(@key)
|
54
|
+
rpb_content = Riakpb::RpbContent.new
|
55
|
+
rpb_content.value = "{\"Date\":\"2010-04-12\",\"Open\":567.35,\"High\":574.00,\"Low\":566.22,\"Close\":572.73,\"Volume\":2352400,\"Adj. Close\":572.73}"
|
56
|
+
rpb_content.content_type = "application/json"
|
57
|
+
rpb_content.vtag = "4DNB6Vt0zLl5VJ6P2xx9dc"
|
58
|
+
rpb_content.last_mod = 1274645855
|
59
|
+
rpb_content.last_mod_usecs = 968694
|
60
|
+
|
61
|
+
rcontent.load(rpb_content)
|
62
|
+
rcontent.key.should == @key
|
63
|
+
rcontent.value.should == {"Date" => "2010-04-12".to_date,"Open" => 567.35,"High" => 574.00,"Low" => 566.22,"Close" => 572.73,"Volume" => 2352400,"Adj. Close" => 572.73}
|
64
|
+
rcontent.content_type.should == "application/json"
|
65
|
+
rcontent.charset.should == nil
|
66
|
+
rcontent.content_encoding.should == nil
|
67
|
+
rcontent.vtag.should == "4DNB6Vt0zLl5VJ6P2xx9dc"
|
68
|
+
rcontent.links.should be_kind_of(Hash)
|
69
|
+
rcontent.last_mod.should == Time.at(1274645855.968694)
|
70
|
+
rcontent.usermeta.should be_kind_of(Hash)
|
71
|
+
end
|
72
|
+
|
73
|
+
end # describe "when directly initializing"
|
74
|
+
end # Riakpb::Content
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.expand_path("../spec_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Riakpb::Key do
|
4
|
+
describe "when directly initializing" do
|
5
|
+
before :each do
|
6
|
+
@client = Riakpb::Client.new
|
7
|
+
@client.rpc.stub!(:request).with(
|
8
|
+
Riakpb::Util::MessageCode::GET_BUCKET_REQUEST,
|
9
|
+
Riakpb::RpbGetBucketReq.new(:bucket => "goog")
|
10
|
+
).and_return(
|
11
|
+
Riakpb::RpbGetBucketResp.new(
|
12
|
+
{ :props => {
|
13
|
+
:allow_mult => false,
|
14
|
+
:n_val => 3
|
15
|
+
}
|
16
|
+
}
|
17
|
+
))
|
18
|
+
@bucket = @client["goog"]
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should default with the bucket and name, and an empty vclock" do
|
22
|
+
key = Riakpb::Key.new(@bucket, "test")
|
23
|
+
key.bucket.should == @bucket
|
24
|
+
key.name.should == "test"
|
25
|
+
key.vclock.should == nil
|
26
|
+
key.content.should be_kind_of(Riakpb::Content)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should serialize into a Key Protocol Buffer (RpbPutReq)" do
|
30
|
+
@client.rpc.stub!(:request).with(
|
31
|
+
Riakpb::Util::MessageCode::GET_REQUEST,
|
32
|
+
Riakpb::RpbGetReq.new(:bucket => "goog", :key => "2010-04-12", :r => nil)
|
33
|
+
).and_return(
|
34
|
+
Riakpb::RpbGetResp.new(
|
35
|
+
{ :content => [Riakpb::RpbContent.new(:value => "Test")],
|
36
|
+
:vclock => "k\xCEa```\xCC`\xCA\x05R,\xACL\xF7^e0%2\xE6\xB12\xC4s\xE6\x1D\xE5\xCB\x02\x00"
|
37
|
+
}
|
38
|
+
))
|
39
|
+
key = @bucket["2010-04-12"] # Riakpb::Key.new(@bucket, "test")
|
40
|
+
pb_put = key.to_pb_put
|
41
|
+
pb_put.should be_kind_of(Riakpb::RpbPutReq)
|
42
|
+
pb_put.vclock.should == "k\xCEa```\xCC`\xCA\x05R,\xACL\xF7^e0%2\xE6\xB12\xC4s\xE6\x1D\xE5\xCB\x02\x00"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should serialize into a Link Protocol Buffer (RpbLink)" do
|
46
|
+
key = Riakpb::Key.new(@bucket, "test")
|
47
|
+
pb_link = key.to_pb_link
|
48
|
+
pb_link.should be_kind_of(Riakpb::RpbLink)
|
49
|
+
pb_link.bucket.should == "goog"
|
50
|
+
pb_link.key.should == "test"
|
51
|
+
end
|
52
|
+
end # describe "when directly initializing"
|
53
|
+
end # Riakpb::Key
|
54
|
+
|
@@ -0,0 +1,327 @@
|
|
1
|
+
require File.expand_path("../spec_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Riakpb::MapReduce do
|
4
|
+
before :each do
|
5
|
+
@client = Riakpb::Client.new
|
6
|
+
@client.rpc.stub!(:request).and_return(nil)
|
7
|
+
@mr = Riakpb::MapReduce.new(@client)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should require a client" do
|
11
|
+
lambda { Riakpb::MapReduce.new }.should raise_error
|
12
|
+
lambda { Riakpb::MapReduce.new(@client) }.should_not raise_error
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should initialize the inputs and query to empty arrays" do
|
16
|
+
@mr.inputs.should == []
|
17
|
+
@mr.query.should == []
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should yield itself when given a block on initializing" do
|
21
|
+
@mr2 = nil
|
22
|
+
@mr = Riakpb::MapReduce.new(@client) do |mr|
|
23
|
+
@mr2 = mr
|
24
|
+
end
|
25
|
+
@mr2.should == @mr
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "adding inputs" do
|
29
|
+
it "should return self for chaining" do
|
30
|
+
@mr.add("foo", "bar").should == @mr
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should add bucket/key pairs to the inputs" do
|
34
|
+
@mr.add("foo","bar")
|
35
|
+
@mr.inputs.should == [["foo","bar"]]
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should add an array containing a bucket/key pair to the inputs" do
|
39
|
+
@mr.add(["foo","bar"])
|
40
|
+
@mr.inputs.should == [["foo","bar"]]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should add an object to the inputs by its bucket and key" do
|
44
|
+
bucket = Riakpb::Bucket.new(@client, "foo")
|
45
|
+
key = Riakpb::Key.new(bucket, "bar")
|
46
|
+
@mr.add(key)
|
47
|
+
@mr.inputs.should == [["foo", "bar"]]
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should add an array containing a bucket/key/key-data triple to the inputs" do
|
51
|
+
@mr.add(["foo","bar",1000])
|
52
|
+
@mr.inputs.should == [["foo","bar",1000]]
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should use a bucket name as the single input" do
|
56
|
+
@mr.add(Riakpb::Bucket.new(@client, "foo"))
|
57
|
+
@mr.inputs.should == "foo"
|
58
|
+
@mr.add("docs")
|
59
|
+
@mr.inputs.should == "docs"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
[:map, :reduce].each do |type|
|
64
|
+
describe "adding #{type} phases" do
|
65
|
+
it "should return self for chaining" do
|
66
|
+
@mr.send(type, "function(){}").should == @mr
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should accept a function string" do
|
70
|
+
@mr.send(type, "function(){}")
|
71
|
+
@mr.query.should have(1).items
|
72
|
+
phase = @mr.query.first
|
73
|
+
phase.function.should == "function(){}"
|
74
|
+
phase.type.should == type
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should accept a function and options" do
|
78
|
+
@mr.send(type, "function(){}", :keep => true)
|
79
|
+
@mr.query.should have(1).items
|
80
|
+
phase = @mr.query.first
|
81
|
+
phase.function.should == "function(){}"
|
82
|
+
phase.type.should == type
|
83
|
+
phase.keep.should be_true
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should accept a module/function pair" do
|
87
|
+
@mr.send(type, ["riak","mapsomething"])
|
88
|
+
@mr.query.should have(1).items
|
89
|
+
phase = @mr.query.first
|
90
|
+
phase.function.should == ["riak", "mapsomething"]
|
91
|
+
phase.type.should == type
|
92
|
+
phase.language.should == "erlang"
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should accept a module/function pair with extra options" do
|
96
|
+
@mr.send(type, ["riak", "mapsomething"], :arg => [1000])
|
97
|
+
@mr.query.should have(1).items
|
98
|
+
phase = @mr.query.first
|
99
|
+
phase.function.should == ["riak", "mapsomething"]
|
100
|
+
phase.type.should == type
|
101
|
+
phase.language.should == "erlang"
|
102
|
+
phase.arg.should == [1000]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "adding link phases" do
|
108
|
+
it "should return self for chaining" do
|
109
|
+
@mr.link({}).should == @mr
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should accept a Link" do
|
113
|
+
@mr.link({:tag => "next"})
|
114
|
+
@mr.query.should have(1).items
|
115
|
+
phase = @mr.query.first
|
116
|
+
phase.type.should == :link
|
117
|
+
# phase.function.should be_kind_of(Riakpb::WalkSpec)
|
118
|
+
phase.function[:tag].should == "next"
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should accept a Link and a statement to keep" do
|
122
|
+
@mr.link({:bucket => "foo", :keep => true})
|
123
|
+
@mr.query.should have(1).items
|
124
|
+
phase = @mr.query.first
|
125
|
+
phase.type.should == :link
|
126
|
+
# phase.function.should be_kind_of(Riakpb::WalkSpec)
|
127
|
+
phase.function[:bucket].should == "foo"
|
128
|
+
phase.keep.should be_true
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "converting to JSON for the job" do
|
133
|
+
it "should include the inputs and query keys" do
|
134
|
+
@mr.to_json.should =~ /"inputs":/
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should map phases to their JSON equivalents" do
|
138
|
+
phase = Riakpb::MapReduce::Phase.new(:type => :map, :function => "function(){}")
|
139
|
+
@mr.query << phase
|
140
|
+
@mr.to_json.should include('"source":"function(){}"')
|
141
|
+
@mr.to_json.should include('"query":[{"map":{')
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should emit only the bucket name when the input is the whole bucket" do
|
145
|
+
@mr.add("foo")
|
146
|
+
@mr.to_json.should include('"inputs":"foo"')
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should emit an array of inputs when there are multiple inputs" do
|
150
|
+
@mr.add("foo","bar",1000).add("foo","baz")
|
151
|
+
@mr.to_json.should include('"inputs":[["foo","bar",1000],["foo","baz"]]')
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should add the timeout value when set" do
|
155
|
+
@mr.timeout(50000)
|
156
|
+
@mr.to_json.should include('"timeout":50000')
|
157
|
+
end
|
158
|
+
end
|
159
|
+
=begin
|
160
|
+
describe "executing the map reduce job" do
|
161
|
+
it "should issue POST request to the mapred endpoint" do
|
162
|
+
@http.should_receive(:post).with(200, "/mapred", @mr.to_json, hash_including("Content-Type" => "application/json")).and_return({:headers => {'content-type' => ["application/json"]}, :body => "{}"})
|
163
|
+
@mr.run
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should vivify JSON responses" do
|
167
|
+
@http.stub!(:post).and_return(:headers => {'content-type' => ["application/json"]}, :body => '{"key":"value"}')
|
168
|
+
@mr.run.should == {"key" => "value"}
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should return the full response hash for non-JSON responses" do
|
172
|
+
response = {:code => 200, :headers => {'content-type' => ["text/plain"]}, :body => 'This is some text.'}
|
173
|
+
@http.stub!(:post).and_return(response)
|
174
|
+
@mr.run.should == response
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should interpret failed requests with JSON content-types as map reduce errors" do
|
178
|
+
@http.stub!(:post).and_raise(Riakpb::FailedRequest.new(:post, 200, 500, {"content-type" => ["application/json"]}, '{"error":"syntax error"}'))
|
179
|
+
lambda { @mr.run }.should raise_error(Riakpb::MapReduceError)
|
180
|
+
begin
|
181
|
+
@mr.run
|
182
|
+
rescue Riakpb::MapReduceError => mre
|
183
|
+
mre.message.should == '{"error":"syntax error"}'
|
184
|
+
else
|
185
|
+
fail "No exception raised!"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should re-raise non-JSON error responses" do
|
190
|
+
@http.stub!(:post).and_raise(Riakpb::FailedRequest.new(:post, 200, 500, {"content-type" => ["text/plain"]}, 'Oops, you bwoke it.'))
|
191
|
+
lambda { @mr.run }.should raise_error(Riakpb::FailedRequest)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
=end
|
195
|
+
end
|
196
|
+
|
197
|
+
describe Riakpb::MapReduce::Phase do
|
198
|
+
before :each do
|
199
|
+
@fun = "function(v,_,_){ return v['values'][0]['data']; }"
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should initialize with a type and a function" do
|
203
|
+
phase = Riakpb::MapReduce::Phase.new(:type => :map, :function => @fun, :language => "javascript")
|
204
|
+
phase.type.should == :map
|
205
|
+
phase.function.should == @fun
|
206
|
+
phase.language.should == "javascript"
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should initialize with a type and an MF" do
|
210
|
+
phase = Riakpb::MapReduce::Phase.new(:type => :map, :function => ["module", "function"], :language => "erlang")
|
211
|
+
phase.type.should == :map
|
212
|
+
phase.function.should == ["module", "function"]
|
213
|
+
phase.language.should == "erlang"
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should initialize with a type and a bucket/key" do
|
217
|
+
phase = Riakpb::MapReduce::Phase.new(:type => :map, :function => {:bucket => "funs", :key => "awesome_map"}, :language => "javascript")
|
218
|
+
phase.type.should == :map
|
219
|
+
phase.function.should == {:bucket => "funs", :key => "awesome_map"}
|
220
|
+
phase.language.should == "javascript"
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should assume the language is erlang when the function is an array" do
|
224
|
+
phase = Riakpb::MapReduce::Phase.new(:type => :map, :function => ["module", "function"])
|
225
|
+
phase.language.should == "erlang"
|
226
|
+
end
|
227
|
+
|
228
|
+
it "should assume the language is javascript when the function is a string" do
|
229
|
+
phase = Riakpb::MapReduce::Phase.new(:type => :map, :function => @fun)
|
230
|
+
phase.language.should == "javascript"
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should assume the language is javascript when the function is a hash" do
|
234
|
+
phase = Riakpb::MapReduce::Phase.new(:type => :map, :function => {:bucket => "jobs", :key => "awesome_map"})
|
235
|
+
phase.language.should == "javascript"
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should accept a WalkSpec for the function when a link phase" do
|
239
|
+
# phase = Riakpb::MapReduce::Phase.new(:type => :link, :function => Riakpb::WalkSpec.new({}))
|
240
|
+
# phase.function.should be_kind_of(Riakpb::WalkSpec)
|
241
|
+
end
|
242
|
+
|
243
|
+
it "should raise an error if a WalkSpec is given for a phase type other than :link" do
|
244
|
+
# lambda { Riakpb::MapReduce::Phase.new(:type => :map, :function => Riakpb::WalkSpec.new({})) }.should raise_error(ArgumentError)
|
245
|
+
end
|
246
|
+
|
247
|
+
describe "converting to JSON for the job" do
|
248
|
+
before :each do
|
249
|
+
@phase = Riakpb::MapReduce::Phase.new(:type => :map, :function => "")
|
250
|
+
end
|
251
|
+
|
252
|
+
[:map, :reduce].each do |type|
|
253
|
+
describe "when a #{type} phase" do
|
254
|
+
before :each do
|
255
|
+
@phase.type = type
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should be an object with a single key of '#{type}'" do
|
259
|
+
@phase.to_json.should =~ /^\{"#{type}":/
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should include the language" do
|
263
|
+
@phase.to_json.should =~ /"language":/
|
264
|
+
end
|
265
|
+
|
266
|
+
it "should include the keep value" do
|
267
|
+
@phase.to_json.should =~ /"keep":false/
|
268
|
+
@phase.keep = true
|
269
|
+
@phase.to_json.should =~ /"keep":true/
|
270
|
+
end
|
271
|
+
|
272
|
+
it "should include the function source when the function is a source string" do
|
273
|
+
@phase.function = "function(v,_,_){ return v; }"
|
274
|
+
@phase.to_json.should include(@phase.function)
|
275
|
+
@phase.to_json.should =~ /"source":/
|
276
|
+
end
|
277
|
+
|
278
|
+
it "should include the function name when the function is not a lambda" do
|
279
|
+
@phase.function = "Riakpb.mapValues"
|
280
|
+
@phase.to_json.should include('"name":"Riakpb.mapValues"')
|
281
|
+
@phase.to_json.should_not include('"source"')
|
282
|
+
end
|
283
|
+
|
284
|
+
it "should include the bucket and key when referring to a stored function" do
|
285
|
+
@phase.function = {:bucket => "design", :key => "wordcount_map"}
|
286
|
+
@phase.to_json.should include('"bucket":"design"')
|
287
|
+
@phase.to_json.should include('"key":"wordcount_map"')
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should include the module and function when invoking an Erlang function" do
|
291
|
+
@phase.function = ["riak_mapreduce", "mapreduce_fun"]
|
292
|
+
@phase.to_json.should include('"module":"riak_mapreduce"')
|
293
|
+
@phase.to_json.should include('"function":"mapreduce_fun"')
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
describe "when a link phase" do
|
299
|
+
before :each do
|
300
|
+
@phase.type = :link
|
301
|
+
@phase.function = {}
|
302
|
+
end
|
303
|
+
|
304
|
+
it "should be an object of a single key 'link'" do
|
305
|
+
@phase.to_json.should == "{\"link\":{\"keep\":false}}"
|
306
|
+
end
|
307
|
+
|
308
|
+
it "should include the bucket" do
|
309
|
+
@phase.function[:bucket] = "foo"
|
310
|
+
@phase.to_json.should == "{\"link\":{\"bucket\":\"foo\",\"keep\":false}}"
|
311
|
+
end
|
312
|
+
|
313
|
+
it "should include the tag" do
|
314
|
+
@phase.function[:tag] = "parent"
|
315
|
+
@phase.to_json.should == "{\"link\":{\"tag\":\"parent\",\"keep\":false}}"
|
316
|
+
end
|
317
|
+
|
318
|
+
it "should include the keep value" do
|
319
|
+
@phase.keep = true
|
320
|
+
@phase.to_json.should == "{\"link\":{\"keep\":true}}"
|
321
|
+
@phase.keep = false
|
322
|
+
@phase.function[:keep] = true
|
323
|
+
@phase.to_json.should == "{\"link\":{\"keep\":false}}"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|