siberite-client 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe Siberite::Client::Json do
4
+ describe "Instance Methods" do
5
+ before do
6
+ @raw_client = Siberite::Client.new(*Siberite::Config.default)
7
+ @client = Siberite::Client::Json.new(@raw_client)
8
+ end
9
+
10
+ describe "#get" do
11
+ it "parses json" do
12
+ mock(@raw_client).get('a_queue') { '{"a": 1, "b": [{"c": 2}]}' }
13
+ @client.get('a_queue').should == {"a" => 1, "b" => ["c" => 2]}
14
+ end
15
+
16
+ # wtf is up with this test?
17
+ it "uses a HashWithIndifferentAccess" do
18
+ mock(@raw_client).get('a_queue') { '{"a": 1, "b": [{"c": 2}]}' }
19
+ @client.get('a_queue').class.should == HashWithIndifferentAccess
20
+ end
21
+
22
+ it "passes through non-strings" do
23
+ mock(@raw_client).get('a_queue') { {:key => "value"} }
24
+ @client.get('a_queue').should == {:key => "value"}
25
+ end
26
+
27
+ it "passes through strings that are not json" do
28
+ mock(@raw_client).get('a_queue') { "I am not JSON" }
29
+ @client.get('a_queue').should == "I am not JSON"
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ class HashWithIndifferentAccess < Hash
36
+ def initialize(hash = {})
37
+ super()
38
+ merge!(hash)
39
+ end
40
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Siberite::Client::Namespace do
4
+ describe "Instance Methods" do
5
+ before do
6
+ @raw_client = Siberite::Client.new(*Siberite::Config.default)
7
+ @client = Siberite::Client::Namespace.new('some_namespace', @raw_client)
8
+ end
9
+
10
+ describe "#set" do
11
+ it "prepends a namespace to the key" do
12
+ mock(@raw_client).set('some_namespace:a_queue', :mcguffin)
13
+ @client.set('a_queue', :mcguffin)
14
+ end
15
+ end
16
+
17
+ describe "#get" do
18
+ it "prepends a namespace to the key" do
19
+ mock(@raw_client).get('some_namespace:a_queue')
20
+ @client.get('a_queue')
21
+ end
22
+ end
23
+
24
+ describe "#delete" do
25
+ it "prepends a namespace to the key" do
26
+ mock(@raw_client).delete('some_namespace:a_queue')
27
+ @client.delete('a_queue')
28
+ end
29
+ end
30
+
31
+ describe "#flush" do
32
+ it "prepends a namespace to the key" do
33
+ mock(@raw_client).flush('some_namespace:a_queue')
34
+ @client.flush('a_queue')
35
+ end
36
+ end
37
+
38
+ describe "#stat" do
39
+ it "prepends a namespace to the key" do
40
+ mock(@raw_client).stat('some_namespace:a_queue')
41
+ @client.stat('a_queue')
42
+ end
43
+ end
44
+
45
+ describe "#available_queues" do
46
+ it "only returns namespaced queues" do
47
+ @raw_client.set('some_namespace:namespaced_queue', 'foo')
48
+ @raw_client.set('unnamespaced_queue', 'foo')
49
+
50
+ @client.available_queues.should == ['namespaced_queue']
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Siberite::Client::Partitioning do
4
+ before do
5
+ @client_1 = Siberite::Client.new(*Siberite::Config.default)
6
+ @client_2 = Siberite::Client.new(*Siberite::Config.default)
7
+ @default_client = Siberite::Client.new(*Siberite::Config.default)
8
+
9
+ @client = Siberite::Client::Partitioning.new(
10
+ 'queue1' => @client_1,
11
+ ['queue2', 'queue3'] => @client_2,
12
+ default: @default_client
13
+ )
14
+ end
15
+
16
+ %w(set get delete flush stat).each do |method|
17
+ describe "##{method}" do
18
+ it "routes to the correct client" do
19
+ mock(@client_1).__send__(method, 'queue1')
20
+ @client.send(method, 'queue1')
21
+
22
+ mock(@client_2).__send__(method, 'queue2')
23
+ @client.send(method, 'queue2')
24
+
25
+ mock(@client_2).__send__(method, 'queue3/queue_arg')
26
+ @client.send(method, 'queue3/queue_arg')
27
+
28
+ mock(@default_client).__send__(method, 'queue4')
29
+ @client.send(method, 'queue4')
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,273 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Siberite::Client::Transactional" do
4
+ before do
5
+ @raw_client = Siberite::Client.new(*Siberite::Config.default)
6
+ @client = Siberite::Client::Transactional.new(@raw_client)
7
+ @queue = "some_queue"
8
+ end
9
+
10
+ describe "integration" do
11
+ def get_job
12
+ job = nil
13
+ job = @client.get(@queue) until job
14
+ job
15
+ end
16
+
17
+ it "processes normal jobs" do
18
+ returns = [:mcguffin]
19
+ stub(@raw_client).get(@queue, anything) { returns.shift }
20
+ stub(@raw_client).get_from_last(@queue + "_errors", anything)
21
+
22
+ mock(@raw_client).get_from_last(@queue, :close => true)
23
+
24
+ get_job.should == :mcguffin
25
+ @client.current_try.should == 1
26
+ @client.get(@queue) # simulate next get run
27
+ end
28
+
29
+ it "processes successful retries" do
30
+ returns = [Siberite::Client::Transactional::RetryableJob.new(1, :mcguffin)]
31
+ stub(@raw_client).get_from_last(@queue + "_errors", anything) { returns.shift }
32
+ stub(@raw_client).get(@queue, anything)
33
+
34
+ mock(@raw_client).get_from_last(@queue + "_errors", :close => true)
35
+
36
+ get_job.should == :mcguffin
37
+ @client.current_try.should == 2
38
+ @client.get(@queue) # simulate next get run
39
+ end
40
+
41
+ it "processes normal jobs that should retry" do
42
+ returns = [:mcguffin]
43
+ stub(@raw_client).get(@queue, anything) { returns.shift }
44
+ stub(@raw_client).get_from_last(@queue + "_errors", anything)
45
+
46
+ mock(@raw_client).set(@queue + "_errors", anything) do |q,j|
47
+ j.retries.should == 1
48
+ j.job.should == :mcguffin
49
+ end
50
+ mock(@raw_client).get_from_last(@queue, :close => true)
51
+
52
+ get_job.should == :mcguffin
53
+ @client.current_try.should == 1
54
+
55
+ @client.retry
56
+ @client.get(@queue) # simulate next get run
57
+ end
58
+
59
+ it "processes retries that should retry" do
60
+ returns = [Siberite::Client::Transactional::RetryableJob.new(1, :mcguffin)]
61
+ stub(@raw_client).get_from_last(@queue + "_errors", :open => true) { returns.shift }
62
+ stub(@raw_client).get(@queue, anything)
63
+ mock(@raw_client).set(@queue + "_errors", anything) do |q,j|
64
+ j.retries.should == 2
65
+ j.job.should == :mcguffin
66
+ end
67
+ mock(@raw_client).get_from_last(@queue + "_errors", :close => true)
68
+
69
+ get_job.should == :mcguffin
70
+ @client.current_try.should == 2
71
+
72
+ @client.retry
73
+ @client.get(@queue) # simulate next get run
74
+ end
75
+
76
+ it "processes retries that should give up" do
77
+ returns = [Siberite::Client::Transactional::RetryableJob.new(Siberite::Client::Transactional::DEFAULT_RETRIES - 1, :mcguffin)]
78
+ stub(@raw_client).get_from_last(@queue + "_errors", :open => true) { returns.shift }
79
+ stub(@raw_client).get(@queue, anything)
80
+ mock(@raw_client).set.never
81
+ mock(@raw_client).get_from_last(@queue + "_errors", :close => true)
82
+
83
+ get_job.should == :mcguffin
84
+ @client.current_try.should == Siberite::Client::Transactional::DEFAULT_RETRIES
85
+
86
+ lambda { @client.retry }.should raise_error(Siberite::Client::Transactional::RetriesExceeded)
87
+ @client.get(@queue) # simulate next get run
88
+ end
89
+ end
90
+
91
+ describe "Instance Methods" do
92
+ before do
93
+ stub(@client).rand { 1 }
94
+ end
95
+
96
+ describe "#get" do
97
+ it "asks for a transaction" do
98
+ mock(@raw_client).get(@queue, :open => true) { :mcguffin }
99
+ @client.get(@queue).should == :mcguffin
100
+ end
101
+
102
+ it "is nil when the primary queue is empty and selected" do
103
+ mock(@client).rand { Siberite::Client::Transactional::ERROR_PROCESSING_RATE + 0.05 }
104
+ mock(@raw_client).get(@queue, anything) { nil }
105
+ mock(@raw_client).get(@queue + "_errors", :open => true).never
106
+ @client.get(@queue).should be_nil
107
+ end
108
+
109
+ it "is nil when the error queue is empty and selected" do
110
+ mock(@client).rand { Siberite::Client::Transactional::ERROR_PROCESSING_RATE - 0.05 }
111
+ mock(@raw_client).get(@queue, anything).never
112
+ mock(@raw_client).get_from_last(@queue + "_errors", :open => true) { nil }
113
+ @client.get(@queue).should be_nil
114
+ end
115
+
116
+ it "returns the payload of a RetryableJob" do
117
+ stub(@client).rand { 0 }
118
+ mock(@raw_client).get_from_last(@queue + "_errors", anything) do
119
+ Siberite::Client::Transactional::RetryableJob.new(1, :mcmuffin)
120
+ end
121
+
122
+ @client.get(@queue).should == :mcmuffin
123
+ end
124
+
125
+ it "closes an open transaction with no retries" do
126
+ stub(@raw_client).get(@queue, anything) { :mcguffin }
127
+ @client.get(@queue)
128
+
129
+ mock(@raw_client).get_from_last(@queue, :close => true)
130
+ @client.get(@queue)
131
+ end
132
+
133
+ it "closes an open transaction with retries" do
134
+ stub(@client).rand { 0 }
135
+ stub(@raw_client).get_from_last(@queue + "_errors", :open => true) do
136
+ Siberite::Client::Transactional::RetryableJob.new(1, :mcmuffin)
137
+ end
138
+ @client.get(@queue)
139
+
140
+ mock(@raw_client).get_from_last(@queue + "_errors", :close => true)
141
+ @client.get(@queue)
142
+ end
143
+
144
+ it "prevents transactional gets across multiple queues" do
145
+ stub(@raw_client).get(@queue, anything) { :mcguffin }
146
+ @client.get(@queue)
147
+
148
+ lambda do
149
+ @client.get("transaction_fail")
150
+ end.should raise_error(Siberite::Client::Transactional::MultipleQueueException)
151
+ end
152
+ end
153
+
154
+ describe "#retry" do
155
+ before do
156
+ stub(@raw_client).get(@queue, anything) { :mcmuffin }
157
+ stub(@raw_client).get_from_last
158
+ @client.get(@queue)
159
+ end
160
+
161
+ it "raises an exception if called when there is no open transaction" do
162
+ @client.close_last_transaction
163
+ lambda { @client.retry }.should raise_error(Siberite::Client::Transactional::NoOpenTransaction)
164
+ end
165
+
166
+ it "raises an exception if retry has already been called" do
167
+ @client.retry
168
+ lambda { @client.retry }.should raise_error(Siberite::Client::Transactional::NoOpenTransaction)
169
+ end
170
+
171
+ it "enqueues a fresh failed job to the errors queue with a retry count" do
172
+ mock(@raw_client).set(@queue + "_errors", anything) do |queue, job|
173
+ job.retries.should == 1
174
+ job.job.should == :mcmuffin
175
+ end
176
+ @client.retry.should be_truthy
177
+ end
178
+
179
+ it "allows specification of the job to retry" do
180
+ mock(@raw_client).set(@queue + "_errors", anything) do |queue, job|
181
+ job.retries.should == 1
182
+ job.job.should == :revised_mcmuffin
183
+ end
184
+ @client.retry(:revised_mcmuffin).should be_truthy
185
+ end
186
+
187
+ it "increments the retry count and re-enqueues the retried job" do
188
+ stub(@client).rand { 0 }
189
+ stub(@raw_client).get_from_last(@queue + "_errors", anything) do
190
+ Siberite::Client::Transactional::RetryableJob.new(1, :mcmuffin)
191
+ end
192
+
193
+ mock(@raw_client).set(@queue + "_errors", anything) do |queue, job|
194
+ job.retries.should == 2
195
+ job.job.should == :mcmuffin
196
+ end
197
+
198
+ @client.get(@queue)
199
+ @client.retry.should be_truthy
200
+ end
201
+
202
+ it "does not enqueue the retried job after too many tries" do
203
+ stub(@client).rand { 0 }
204
+ stub(@raw_client).get_from_last(@queue + "_errors", :open => true) do
205
+ Siberite::Client::Transactional::RetryableJob.new(Siberite::Client::Transactional::DEFAULT_RETRIES - 1, :mcmuffin)
206
+ end
207
+ mock(@raw_client).set(@queue + "_errors", anything).never
208
+ mock(@raw_client).get_from_last(@queue + "_errors", :close => true)
209
+ @client.get(@queue)
210
+ lambda { @client.retry }.should raise_error(Siberite::Client::Transactional::RetriesExceeded)
211
+ end
212
+
213
+ it "closes an open transaction with no retries" do
214
+ stub(@raw_client).get(@queue, anything) { :mcguffin }
215
+ @client.get(@queue)
216
+
217
+ mock(@raw_client).get_from_last(@queue, :close => true)
218
+ @client.retry
219
+ end
220
+
221
+ it "closes an open transaction with retries" do
222
+ stub(@client).rand { 0 }
223
+ stub(@raw_client).get_from_last(@queue + "_errors", :open => true) do
224
+ Siberite::Client::Transactional::RetryableJob.new(1, :mcmuffin)
225
+ end
226
+ @client.get(@queue)
227
+
228
+ mock(@raw_client).get_from_last(@queue + "_errors", :close => true)
229
+ @client.retry
230
+ end
231
+ end
232
+
233
+ describe "#read_from_error_queue?" do
234
+ it "returns the error queue ERROR_PROCESSING_RATE pct. of the time" do
235
+ mock(@client).rand { Siberite::Client::Transactional::ERROR_PROCESSING_RATE - 0.05 }
236
+ @client.send(:read_from_error_queue?).should == true
237
+ end
238
+
239
+ it "returns the normal queue most of the time" do
240
+ mock(@client).rand { Siberite::Client::Transactional::ERROR_PROCESSING_RATE + 0.05 }
241
+ @client.send(:read_from_error_queue?).should == false
242
+ end
243
+ end
244
+
245
+ describe "#close_last_transaction" do
246
+ it "does nothing if there is no last transaction" do
247
+ mock(@raw_client).get_from_last(@queue, :close => true).never
248
+ mock(@raw_client).get_from_last(@queue + "_errors", :close => true).never
249
+ @client.send(:close_last_transaction)
250
+ end
251
+
252
+ it "closes the normal queue if the job was pulled off of the normal queue" do
253
+ mock(@client).read_from_error_queue? { false }
254
+ mock(@raw_client).get(@queue, :open => true) { :mcguffin }
255
+ mock(@raw_client).get_from_last(@queue, :close => true)
256
+ mock(@raw_client).get_from_last(@queue + "_errors", :close => true).never
257
+
258
+ @client.get(@queue).should == :mcguffin
259
+ @client.send(:close_last_transaction)
260
+ end
261
+
262
+ it "closes the error queue if the job was pulled off of the error queue" do
263
+ mock(@client).read_from_error_queue? { true }
264
+ mock(@raw_client).get_from_last(@queue + "_errors", :open => true) { Siberite::Client::Transactional::RetryableJob.new 1, :mcguffin }
265
+ mock(@raw_client).get_from_last(@queue, :close => true).never
266
+ mock(@raw_client).get_from_last(@queue + "_errors", :close => true)
267
+
268
+ @client.get(@queue).should == :mcguffin
269
+ @client.send(:close_last_transaction)
270
+ end
271
+ end
272
+ end
273
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe Siberite::Client::Unmarshal do
4
+ describe "Instance Methods" do
5
+ before do
6
+ @raw_client = Siberite::Client.new(*Siberite::Config.default)
7
+ @client = Siberite::Client::Unmarshal.new(@raw_client)
8
+ end
9
+
10
+ describe "#get" do
11
+ it "integrates" do
12
+ @client.set('a_queue', "foo")
13
+ @client.get('a_queue').should == 'foo'
14
+ end
15
+
16
+ it "unmarshals marshaled objects" do
17
+ test_object = {:a => 1, :b => [1, 2, 3]}
18
+ mock(@raw_client).get('a_queue', :raw => true) { Marshal.dump(test_object) }
19
+ @client.get('a_queue').should == test_object
20
+ end
21
+
22
+ it "does not unmarshal when raw is true" do
23
+ test_object = {:a => 1, :b => [1, 2, 3]}
24
+ mock(@raw_client).get('a_queue', :raw => true) { Marshal.dump(test_object) }
25
+ @client.get('a_queue', :raw => true).should == Marshal.dump(test_object)
26
+ end
27
+
28
+ it "passes through objects" do
29
+ test_object = Object.new
30
+ mock(@raw_client).get('a_queue', :raw => true) { test_object }
31
+ @client.get('a_queue').should == test_object
32
+ end
33
+
34
+ it "passes through strings" do
35
+ mock(@raw_client).get('a_queue', :raw => true) { "I am not marshaled" }
36
+ @client.get('a_queue').should == "I am not marshaled"
37
+ end
38
+ end
39
+
40
+ describe "#isMarshaled" do
41
+ it "should foo" do
42
+ @client.is_marshaled?("foo").should be_falsey
43
+ @client.is_marshaled?(Marshal.dump("foo")).should be_truthy
44
+ @client.is_marshaled?(Marshal.dump("foo")).should be_truthy
45
+
46
+ @client.is_marshaled?({}).should be_falsey
47
+ @client.is_marshaled?(Marshal.dump({})).should be_truthy
48
+ @client.is_marshaled?(Marshal.dump({})).should be_truthy
49
+
50
+ @client.is_marshaled?(BadObject.new).should be_falsey
51
+ @client.is_marshaled?(Marshal.dump(BadObject.new)).should be_truthy
52
+ @client.is_marshaled?(Marshal.dump(BadObject.new)).should be_truthy
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ class BadObject
59
+ def to_s
60
+ raise Exception
61
+ end
62
+ end
63
+
64
+ module Marshal
65
+ def self.load_with_constantize(source, loaded_constants = [])
66
+ self.load(source)
67
+ end
68
+ end