howler 1.0.0
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/.gemrc +1 -0
- data/.gitignore +5 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +68 -0
- data/LICENSE +19 -0
- data/README.md +104 -0
- data/bin/howler +3 -0
- data/howler.gemspec +24 -0
- data/lib/howler.rb +34 -0
- data/lib/howler/async.rb +15 -0
- data/lib/howler/config.ru +4 -0
- data/lib/howler/exceptions.rb +5 -0
- data/lib/howler/exceptions/error.rb +25 -0
- data/lib/howler/exceptions/failed.rb +9 -0
- data/lib/howler/exceptions/notify.rb +15 -0
- data/lib/howler/exceptions/retry.rb +12 -0
- data/lib/howler/manager.rb +123 -0
- data/lib/howler/message.rb +15 -0
- data/lib/howler/queue.rb +138 -0
- data/lib/howler/runner.rb +34 -0
- data/lib/howler/support/config.rb +33 -0
- data/lib/howler/support/logger.rb +57 -0
- data/lib/howler/support/util.rb +23 -0
- data/lib/howler/support/version.rb +3 -0
- data/lib/howler/web.rb +47 -0
- data/lib/howler/web/public/application.css +24 -0
- data/lib/howler/web/public/bootstrap.css +3990 -0
- data/lib/howler/web/public/bootstrap.min.css +689 -0
- data/lib/howler/web/public/queues.css +19 -0
- data/lib/howler/web/views/failed_messages.erb +27 -0
- data/lib/howler/web/views/html.erb +10 -0
- data/lib/howler/web/views/index.erb +11 -0
- data/lib/howler/web/views/navigation.erb +25 -0
- data/lib/howler/web/views/notification_messages.erb +24 -0
- data/lib/howler/web/views/notifications.erb +15 -0
- data/lib/howler/web/views/pending_messages.erb +24 -0
- data/lib/howler/web/views/processed_messages.erb +28 -0
- data/lib/howler/web/views/queue.erb +36 -0
- data/lib/howler/web/views/queue_table.erb +27 -0
- data/lib/howler/web/views/queues.erb +15 -0
- data/lib/howler/worker.rb +17 -0
- data/spec/models/async_spec.rb +76 -0
- data/spec/models/exceptions/failed_spec.rb +15 -0
- data/spec/models/exceptions/message_spec.rb +53 -0
- data/spec/models/exceptions/notify_spec.rb +26 -0
- data/spec/models/exceptions/retry_spec.rb +49 -0
- data/spec/models/howler_spec.rb +69 -0
- data/spec/models/manager_spec.rb +397 -0
- data/spec/models/message_spec.rb +78 -0
- data/spec/models/queue_spec.rb +539 -0
- data/spec/models/runner_spec.rb +109 -0
- data/spec/models/support/config_spec.rb +56 -0
- data/spec/models/support/logger_spec.rb +147 -0
- data/spec/models/support/util_spec.rb +44 -0
- data/spec/models/worker_spec.rb +54 -0
- data/spec/requests/web_spec.rb +220 -0
- data/spec/spec_helper.rb +93 -0
- metadata +265 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Howler::Message::Notify do
|
4
|
+
let!(:ex) { generate_exception }
|
5
|
+
subject { Howler::Message::Notify.new(ex) }
|
6
|
+
|
7
|
+
it "should inherit from Howler::Message::Error" do
|
8
|
+
Howler::Message::Notify.ancestors.should include(Howler::Message::Error)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#cause" do
|
12
|
+
it "should store the cause" do
|
13
|
+
subject.cause.should == ex
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#env" do
|
18
|
+
it "should store the hostname" do
|
19
|
+
subject.env['hostname'].should == `hostname`.chomp
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should store the ruby version" do
|
23
|
+
subject.env['ruby_version'].should == `ruby -v`.chomp
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Howler::Message::Retry do
|
4
|
+
it "should inherit from Howler::Message::Error" do
|
5
|
+
Howler::Message::Retry.ancestors.should include(Howler::Message::Error)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#at" do
|
9
|
+
subject { Howler::Message::Retry.new(:at => Time.now.utc + 1.day) }
|
10
|
+
|
11
|
+
it "should store the retry at time" do
|
12
|
+
Timecop.freeze(DateTime.now) do
|
13
|
+
subject.at.should == Time.now.utc + 1.day
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "when given the after attribute" do
|
18
|
+
subject { Howler::Message::Retry.new(:after => 5.minutes) }
|
19
|
+
|
20
|
+
it "should set the retry at value" do
|
21
|
+
Timecop.freeze(DateTime.now) do
|
22
|
+
subject.at.should == Time.now.utc + 5.minutes
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#ttl" do
|
29
|
+
describe "when not given the ttl" do
|
30
|
+
it "should default to zero" do
|
31
|
+
subject.ttl.should == 0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "when given the ttl" do
|
36
|
+
subject { Howler::Message::Retry.new(:ttl => 5.days) }
|
37
|
+
|
38
|
+
it "should store the ttl" do
|
39
|
+
subject.ttl.should_not be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should be the 'ttl' minutes in the future" do
|
43
|
+
Timecop.freeze(DateTime.now) do
|
44
|
+
subject.ttl.should == Time.now.utc + 5.days
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Howler do
|
4
|
+
describe ".next" do
|
5
|
+
before do
|
6
|
+
Howler.unstub(:next)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should return a number" do
|
10
|
+
Howler.next(:id).class.should == Fixnum
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should be increase by one" do
|
14
|
+
before = Howler.next(:id)
|
15
|
+
|
16
|
+
(before + 1).should == Howler.next(:id)
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "for multiple keys" do
|
20
|
+
before do
|
21
|
+
Howler.next(:foo)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should have different counts" do
|
25
|
+
[
|
26
|
+
Howler.next(:id),
|
27
|
+
Howler.next(:id),
|
28
|
+
Howler.next(:foo),
|
29
|
+
Howler.next(:foo)
|
30
|
+
].should == [1, 2, 2, 3]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe ".args" do
|
36
|
+
it "should remove square brackets" do
|
37
|
+
Howler.args([]).should == ""
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should remove only leading and trailing brackets" do
|
41
|
+
Howler.args([10, {'akey' => 'avalue'}]).should == '10, {"akey"=>"avalue"}'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe ".redis" do
|
46
|
+
let(:pool) { mock("ConnectionPool2") }
|
47
|
+
|
48
|
+
xit "should be a ConnectionPool" do
|
49
|
+
ConnectionPool.should_receive(:new).with(:timeout => 1, :size => 5)
|
50
|
+
|
51
|
+
Howler.redis
|
52
|
+
end
|
53
|
+
|
54
|
+
xit "should cache the ConnectionPool" do
|
55
|
+
ConnectionPool.stub(:new).and_return(pool, mock("ConnPool"))
|
56
|
+
|
57
|
+
Howler.redis.should == Howler.redis
|
58
|
+
end
|
59
|
+
|
60
|
+
xit "should be an key-value store" do
|
61
|
+
Howler.redis.stub(:with).and_yield(Howler.send(:_redis))
|
62
|
+
|
63
|
+
Howler.redis.with do |redis|
|
64
|
+
redis.set("key", "value")
|
65
|
+
redis.get("key").should == "value"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,397 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Howler::Manager do
|
4
|
+
subject { Howler::Manager.new }
|
5
|
+
|
6
|
+
before do
|
7
|
+
subject.wrapped_object.stub(:sleep)
|
8
|
+
Howler::Manager.stub(:current).and_return(subject)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe ".new" do
|
12
|
+
it "should create a Logger" do
|
13
|
+
Howler::Logger.should_receive(:new)
|
14
|
+
|
15
|
+
Howler::Manager.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe ".current" do
|
20
|
+
before do
|
21
|
+
subject.wrapped_object.stub(:sleep)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should return the current manager instance" do
|
25
|
+
Howler::Manager.current.wrapped_object.class.should == Howler::Manager
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#run" do
|
30
|
+
def build_message(klass, method)
|
31
|
+
Howler::Message.new(
|
32
|
+
'class' => klass.to_s,
|
33
|
+
'method' => method,
|
34
|
+
'args' => [],
|
35
|
+
'created_at' => Time.now.to_f
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
before do
|
40
|
+
subject.wrapped_object.stub(:done?).and_return(true)
|
41
|
+
Howler::Config[:concurrency] = 10
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should create workers" do
|
45
|
+
Howler::Worker.should_receive(:new_link).exactly(10)
|
46
|
+
|
47
|
+
subject.run
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "when there are no pending messages" do
|
51
|
+
before do
|
52
|
+
subject.wrapped_object.stub(:done?).and_return(false, true)
|
53
|
+
end
|
54
|
+
|
55
|
+
class SampleEx < Exception; end
|
56
|
+
|
57
|
+
describe "when there are no messages" do
|
58
|
+
it "should sleep for one second" do
|
59
|
+
subject.wrapped_object.should_receive(:sleep).with(1)
|
60
|
+
|
61
|
+
subject.run
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "when there are pending messages" do
|
67
|
+
before do
|
68
|
+
Howler::Config[:concurrency] = 3
|
69
|
+
|
70
|
+
@workers = 3.times.collect do
|
71
|
+
mock(Howler::Worker, :perform! => nil)
|
72
|
+
end
|
73
|
+
|
74
|
+
subject.wrapped_object.stub(:build_workers).and_return(@workers)
|
75
|
+
subject.wrapped_object.stub(:sleep)
|
76
|
+
subject.wrapped_object.stub(:done?).and_return(false, true)
|
77
|
+
|
78
|
+
@messages = {
|
79
|
+
'length' => build_message(Array, :length),
|
80
|
+
'collect' => build_message(Array, :collect),
|
81
|
+
'max' => build_message(Array, :max),
|
82
|
+
'to_s' => build_message(Array, :to_s)
|
83
|
+
}
|
84
|
+
|
85
|
+
%w(length collect max to_s).each do |method|
|
86
|
+
Howler::Message.stub(:new).with(hash_including('method' => method)).and_return(@messages[method])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "when there is a single message in the queue" do
|
91
|
+
before do
|
92
|
+
subject.push(Array, :length, [])
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should not sleep" do
|
96
|
+
subject.wrapped_object.should_not_receive(:sleep)
|
97
|
+
|
98
|
+
subject.run
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should perform the message on a worker" do
|
102
|
+
@workers[2].should_receive(:perform!).with(@messages['length'], Howler::Queue::DEFAULT)
|
103
|
+
|
104
|
+
@workers[0].should_not_receive(:perform!)
|
105
|
+
@workers[1].should_not_receive(:perform!)
|
106
|
+
|
107
|
+
subject.run
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "when a message gets taken by a worker" do
|
111
|
+
before do
|
112
|
+
@original_workers = @workers.dup
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should make the worker unavailable" do
|
116
|
+
subject.run
|
117
|
+
|
118
|
+
subject.should have(2).workers
|
119
|
+
subject.should have(1).chewing
|
120
|
+
|
121
|
+
subject.workers.should == @original_workers.first(2)
|
122
|
+
subject.chewing.should == @original_workers.last(1)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "when there are many messages in the queue" do
|
128
|
+
before do
|
129
|
+
[:length, :collect, :max].each do |method|
|
130
|
+
subject.push(Array, method, [])
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "more workers then messages" do
|
135
|
+
it "should perform all messages" do
|
136
|
+
@workers[2].should_receive(:perform!).with(@messages['length'], anything)
|
137
|
+
@workers[1].should_receive(:perform!).with(@messages['collect'], anything)
|
138
|
+
@workers[0].should_receive(:perform!).with(@messages['max'], anything)
|
139
|
+
|
140
|
+
subject.run
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "more messages then workers" do
|
145
|
+
before do
|
146
|
+
subject.wrapped_object.stub(:done?).and_return(false, false, true)
|
147
|
+
|
148
|
+
Howler::Config[:concurrency] = 2
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should scale and only remove as many messages as workers" do
|
152
|
+
@workers[0].unstub(:perform!)
|
153
|
+
|
154
|
+
@workers[1].should_receive(:perform!).with(@messages['length'], anything)
|
155
|
+
@workers[0].should_receive(:perform!).with(@messages['collect'], anything)
|
156
|
+
|
157
|
+
subject.run
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe "run messages in the future" do
|
162
|
+
let!(:worker) { mock(Howler::Worker) }
|
163
|
+
|
164
|
+
before do
|
165
|
+
subject.wrapped_object.stub(:done?).and_return(false, false, true)
|
166
|
+
Howler::Config[:concurrency] = 4
|
167
|
+
|
168
|
+
Howler::Worker.should_receive(:new).once.and_return(worker)
|
169
|
+
|
170
|
+
subject.push(Array, :to_s, [], Time.now + 5.minutes)
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should only enqueue messages that are scheduled before now" do
|
174
|
+
Timecop.freeze(Time.now) do
|
175
|
+
worker.should_receive(:perform!).with(@messages['length'], anything).ordered
|
176
|
+
@workers[2].should_receive(:perform!).with(@messages['collect'], anything)
|
177
|
+
@workers[1].should_receive(:perform!).with(@messages['max'], anything)
|
178
|
+
|
179
|
+
subject.run
|
180
|
+
|
181
|
+
subject.wrapped_object.stub(:done?).and_return(false, true)
|
182
|
+
|
183
|
+
Timecop.travel(5.minutes) do
|
184
|
+
@workers[0].should_receive(:perform!).with(@messages['to_s'], anything).ordered
|
185
|
+
subject.run
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe "logging" do
|
195
|
+
let!(:logger) { mock(Howler::Logger) }
|
196
|
+
let!(:log) { mock(Howler::Logger, :info => nil, :debug => nil) }
|
197
|
+
|
198
|
+
before do
|
199
|
+
Howler::Config[:concurrency] = 3
|
200
|
+
|
201
|
+
@workers = 3.times.collect do
|
202
|
+
mock(Howler::Worker, :perform! => nil)
|
203
|
+
end
|
204
|
+
|
205
|
+
subject.wrapped_object.stub(:build_workers).and_return(@workers)
|
206
|
+
subject.wrapped_object.stub(:done?).and_return(false, true)
|
207
|
+
subject.wrapped_object.instance_variable_set(:@logger, logger)
|
208
|
+
logger.stub(:log).and_yield(log)
|
209
|
+
|
210
|
+
[:send_notification, :enforce_avgs].each_with_index do |method, i|
|
211
|
+
subject.push(Array, method, [i, ((i+1)*100).to_s(36)])
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe "information" do
|
216
|
+
before do
|
217
|
+
Howler::Config[:log] = 'info'
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should log the number of messages to be processed" do
|
221
|
+
log.should_receive(:info).with("Processing 2 Messages")
|
222
|
+
|
223
|
+
subject.run
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
describe "debug" do
|
228
|
+
before do
|
229
|
+
Howler::Config[:log] = 'debug'
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should show a digest of the messages" do
|
233
|
+
log.should_receive(:debug).with('MESG - 123 Array.new.send_notification(0, "2s")')
|
234
|
+
log.should_receive(:debug).with('MESG - 123 Array.new.enforce_avgs(1, "5k")')
|
235
|
+
|
236
|
+
subject.run
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
describe "#done_chewing" do
|
242
|
+
before do
|
243
|
+
worker = mock(Howler::Worker)
|
244
|
+
@chewing_worker = mock(Howler::Worker, :alive? => true)
|
245
|
+
|
246
|
+
subject.wrapped_object.stub(:build_workers).and_return([worker])
|
247
|
+
subject.wrapped_object.instance_variable_set(:@chewing, [@chewing_worker])
|
248
|
+
|
249
|
+
end
|
250
|
+
|
251
|
+
it "should remove the worker from chewing" do
|
252
|
+
subject.chewing.should include(@chewing_worker)
|
253
|
+
|
254
|
+
subject.done_chewing(@chewing_worker)
|
255
|
+
|
256
|
+
subject.chewing.should_not include(@chewing_worker)
|
257
|
+
end
|
258
|
+
|
259
|
+
it "should make the worker available" do
|
260
|
+
subject.workers.should_not include(@chewing_worker)
|
261
|
+
|
262
|
+
subject.done_chewing(@chewing_worker)
|
263
|
+
|
264
|
+
subject.workers.should include(@chewing_worker)
|
265
|
+
end
|
266
|
+
|
267
|
+
describe "when a worker has died" do
|
268
|
+
before do
|
269
|
+
@chewing_worker.stub(:alive?).and_return(false)
|
270
|
+
end
|
271
|
+
|
272
|
+
it "should make in un-available" do
|
273
|
+
subject.chewing.should include(@chewing_worker)
|
274
|
+
|
275
|
+
subject.done_chewing(@chewing_worker)
|
276
|
+
|
277
|
+
subject.chewing.should_not include(@chewing_worker)
|
278
|
+
subject.workers.should_not include(@chewing_worker)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
describe "#worker_death" do
|
284
|
+
before do
|
285
|
+
subject.wrapped_object.stub(:done?).and_return(true)
|
286
|
+
|
287
|
+
worker = mock(Howler::Worker)
|
288
|
+
@chewing_worker = mock(Howler::Worker)
|
289
|
+
@chewing_workers = [@chewing_worker]
|
290
|
+
|
291
|
+
subject.wrapped_object.stub(:build_workers).and_return([worker])
|
292
|
+
subject.wrapped_object.instance_variable_set(:@chewing, @chewing_workers)
|
293
|
+
|
294
|
+
Howler::Config[:concurrency] = 3
|
295
|
+
subject.run
|
296
|
+
end
|
297
|
+
|
298
|
+
describe "when the worker is alive" do
|
299
|
+
before do
|
300
|
+
@chewing_worker.stub(:alive?).and_return(true)
|
301
|
+
end
|
302
|
+
|
303
|
+
it "should create a new worker" do
|
304
|
+
Howler::Worker.should_receive(:new_link)
|
305
|
+
|
306
|
+
subject.worker_death
|
307
|
+
end
|
308
|
+
|
309
|
+
it "should add a worker" do
|
310
|
+
subject.should have(1).workers
|
311
|
+
|
312
|
+
subject.worker_death
|
313
|
+
|
314
|
+
subject.should have(2).workers
|
315
|
+
end
|
316
|
+
|
317
|
+
it "should make in un-available" do
|
318
|
+
@chewing_workers.should_receive(:delete).with(@chewing_worker)
|
319
|
+
|
320
|
+
subject.worker_death(@chewing_worker)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
describe "#shutdown" do
|
326
|
+
before do
|
327
|
+
subject.wrapped_object.stub(:done?).and_return(true)
|
328
|
+
|
329
|
+
Howler::Config[:concurrency] = 2
|
330
|
+
subject.wrapped_object.instance_variable_set(:@chewing, [mock(Howler::Worker)])
|
331
|
+
end
|
332
|
+
|
333
|
+
it "should not accept more work" do
|
334
|
+
subject.wrapped_object.unstub(:done?)
|
335
|
+
subject.should_not be_done
|
336
|
+
|
337
|
+
subject.shutdown
|
338
|
+
|
339
|
+
subject.should be_done
|
340
|
+
end
|
341
|
+
|
342
|
+
it "should remove non active workers from the list" do
|
343
|
+
subject.run
|
344
|
+
|
345
|
+
subject.should have(2).workers
|
346
|
+
subject.should have(1).chewing
|
347
|
+
|
348
|
+
subject.shutdown.should == 2
|
349
|
+
|
350
|
+
subject.should have(0).workers
|
351
|
+
subject.should have(1).chewing
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
describe "#push" do
|
356
|
+
let!(:queue) { Howler::Queue.new(Howler::Manager::DEFAULT) }
|
357
|
+
|
358
|
+
def create_message(klass, method, args)
|
359
|
+
{
|
360
|
+
:id => 123,
|
361
|
+
:class => klass.to_s,
|
362
|
+
:method => method,
|
363
|
+
:args => args,
|
364
|
+
:created_at => Time.now.to_f
|
365
|
+
}
|
366
|
+
end
|
367
|
+
|
368
|
+
before do
|
369
|
+
Howler::Queue.stub(:new).and_return(queue)
|
370
|
+
end
|
371
|
+
|
372
|
+
describe "when given a class, method, and name" do
|
373
|
+
it "should push a message" do
|
374
|
+
Timecop.freeze(DateTime.now) do
|
375
|
+
message = create_message("Array", :length, [1234])
|
376
|
+
queue.should_receive(:push).with(message, Time.now)
|
377
|
+
|
378
|
+
subject.push(Array, :length, [1234])
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
it "should enqueue the message" do
|
383
|
+
should_change(Howler::Manager::DEFAULT).length_by(1) do
|
384
|
+
subject.push(Array, :length, [])
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
describe "when given the 'wait until' time" do
|
390
|
+
it "should enqueue the message" do
|
391
|
+
should_change(Howler::Manager::DEFAULT).length_by(1) do
|
392
|
+
subject.push(Array, :length, [], Time.now + 5.minutes)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|