perfectqueue 0.8.44.1 → 0.8.45
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog +5 -2
- data/lib/perfectqueue/application/decider.rb +2 -2
- data/lib/perfectqueue/application/router.rb +1 -1
- data/lib/perfectqueue/backend/rdb_compat.rb +40 -58
- data/lib/perfectqueue/engine.rb +2 -5
- data/lib/perfectqueue/multiprocess/thread_processor.rb +1 -1
- data/lib/perfectqueue/task.rb +2 -1
- data/lib/perfectqueue/task_metadata.rb +2 -4
- data/lib/perfectqueue/version.rb +1 -1
- data/perfectqueue.gemspec +1 -2
- data/spec/application/base_spec.rb +81 -0
- data/spec/application/decider_spec.rb +56 -0
- data/spec/application/dispatch_spec.rb +22 -0
- data/spec/application/router_spec.rb +48 -0
- data/spec/backend_spec.rb +9 -0
- data/spec/blocking_flag_spec.rb +103 -0
- data/spec/client_spec.rb +28 -0
- data/spec/daemons_logger_spec.rb +35 -0
- data/spec/engine_spec.rb +159 -0
- data/spec/multiprocess/child_process_monitor_spec.rb +300 -0
- data/spec/multiprocess/child_process_spec.rb +160 -0
- data/spec/multiprocess/fork_processor_spec.rb +170 -0
- data/spec/multiprocess/thread_processor_spec.rb +52 -0
- data/spec/queue_spec.rb +73 -68
- data/spec/rdb_compat_backend_spec.rb +481 -19
- data/spec/runner_spec.rb +32 -0
- data/spec/signal_thread_spec.rb +43 -0
- data/spec/stress.rb +1 -1
- data/spec/supervisor_spec.rb +188 -33
- data/spec/task_metadata_spec.rb +69 -0
- data/spec/task_monitor_spec.rb +42 -0
- data/spec/task_spec.rb +70 -0
- metadata +40 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca67d530351a8a7e0ff2f950d10248bc23eb5e2e
|
4
|
+
data.tar.gz: 5b66bc3219182f721af358e0bce7836ffdca356d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 282b4e05105a5b6fe92cfb3c406b415221784350cb1449e87fe42b481fa7adc0b5d5318c67cc5922e9deafbd244939b2481e76c10ced8b15e981605eacddde35
|
7
|
+
data.tar.gz: 7f4a15e96b072fa04144d04f6f92396a730b76de07c0cb45c6f63d9c30d84bbfff88d7cab526a7a895d6265ed74cc897f9d0b8293d3a7c8271d67cba65c4acee
|
data/ChangeLog
CHANGED
@@ -19,7 +19,7 @@
|
|
19
19
|
module PerfectQueue
|
20
20
|
module Application
|
21
21
|
|
22
|
-
class UndefinedDecisionError
|
22
|
+
class UndefinedDecisionError < StandardError
|
23
23
|
end
|
24
24
|
|
25
25
|
class Decider
|
@@ -39,7 +39,7 @@ module PerfectQueue
|
|
39
39
|
begin
|
40
40
|
m = method(type)
|
41
41
|
rescue NameError
|
42
|
-
raise UndefinedDecisionError, "Undefined decision #{type} options=#{
|
42
|
+
raise UndefinedDecisionError, "Undefined decision #{type} options=#{opts.inspect}"
|
43
43
|
end
|
44
44
|
m.call(opts)
|
45
45
|
end
|
@@ -58,7 +58,7 @@ module PerfectQueue
|
|
58
58
|
when String, Symbol
|
59
59
|
pattern = /\A#{Regexp.escape(pattern)}\z/
|
60
60
|
else
|
61
|
-
raise
|
61
|
+
raise ArgumentError, "pattern should be String or Regexp but got #{pattern.class}: #{pattern.inspect}"
|
62
62
|
end
|
63
63
|
|
64
64
|
@patterns << [pattern, sym]
|
@@ -21,9 +21,6 @@ module PerfectQueue
|
|
21
21
|
class RDBCompatBackend
|
22
22
|
include BackendHelper
|
23
23
|
|
24
|
-
# backport from v0.8.46
|
25
|
-
EVENT_HORIZON = 13_0000_0000 # 2011-03-13 07:06:40 UTC
|
26
|
-
|
27
24
|
class Token < Struct.new(:key)
|
28
25
|
end
|
29
26
|
|
@@ -40,10 +37,7 @@ module PerfectQueue
|
|
40
37
|
#password = config[:password]
|
41
38
|
#user = config[:user]
|
42
39
|
|
43
|
-
|
44
|
-
when /sqlite/i
|
45
|
-
@db = Sequel.connect(url, :max_connections=>1)
|
46
|
-
when /mysql/i
|
40
|
+
if /\Amysql/i =~ url
|
47
41
|
require 'uri'
|
48
42
|
|
49
43
|
uri = URI.parse(url)
|
@@ -63,8 +57,20 @@ module PerfectQueue
|
|
63
57
|
else
|
64
58
|
@use_connection_pooling = !!config[:sslca]
|
65
59
|
end
|
60
|
+
@table_lock = lambda {
|
61
|
+
locked = nil
|
62
|
+
loop do
|
63
|
+
@db.fetch("SELECT GET_LOCK('#{@table}', #{LOCK_WAIT_TIMEOUT}) locked") do |row|
|
64
|
+
locked = true if row[:locked] == 1
|
65
|
+
end
|
66
|
+
break if locked
|
67
|
+
end
|
68
|
+
}
|
69
|
+
@table_unlock = lambda {
|
70
|
+
@db.run("SELECT RELEASE_LOCK('#{@table}')")
|
71
|
+
}
|
66
72
|
else
|
67
|
-
raise ConfigError, "
|
73
|
+
raise ConfigError, "only 'mysql' is supported"
|
68
74
|
end
|
69
75
|
|
70
76
|
@last_time = Time.now.to_i
|
@@ -79,7 +85,6 @@ module PerfectQueue
|
|
79
85
|
SELECT id, timeout, data, created_at, resource
|
80
86
|
FROM `#{@table}`
|
81
87
|
WHERE timeout <= ? AND timeout <= ? AND created_at IS NOT NULL
|
82
|
-
AND #{EVENT_HORIZON} < timeout
|
83
88
|
ORDER BY timeout ASC
|
84
89
|
LIMIT ?
|
85
90
|
SQL
|
@@ -94,37 +99,11 @@ LEFT JOIN (
|
|
94
99
|
GROUP BY resource
|
95
100
|
) AS R ON resource = res
|
96
101
|
WHERE timeout <= ? AND created_at IS NOT NULL AND (max_running-running IS NULL OR max_running-running > 0)
|
97
|
-
AND #{EVENT_HORIZON} < timeout
|
98
102
|
ORDER BY weight DESC, timeout ASC
|
99
103
|
LIMIT ?
|
100
104
|
SQL
|
101
105
|
end
|
102
106
|
|
103
|
-
case url.split('//',2)[0].to_s
|
104
|
-
when /sqlite/i
|
105
|
-
# sqlite always locks tables on BEGIN
|
106
|
-
@table_lock = nil
|
107
|
-
@table_unlock = nil
|
108
|
-
when /mysql/i
|
109
|
-
@table_lock = lambda {
|
110
|
-
locked = nil
|
111
|
-
loop do
|
112
|
-
@db.fetch("SELECT GET_LOCK('#{@table}', #{LOCK_WAIT_TIMEOUT}) locked") do |row|
|
113
|
-
locked = true if row[:locked] == 1
|
114
|
-
end
|
115
|
-
break if locked
|
116
|
-
end
|
117
|
-
}
|
118
|
-
@table_unlock = lambda {
|
119
|
-
@db.run("SELECT RELEASE_LOCK('#{@table}')")
|
120
|
-
}
|
121
|
-
else
|
122
|
-
@table_lock = lambda {
|
123
|
-
@db.run("LOCK TABLE `#{@table}`")
|
124
|
-
}
|
125
|
-
@table_unlock = nil
|
126
|
-
end
|
127
|
-
|
128
107
|
@prefetch_break_types = config[:prefetch_break_types] || []
|
129
108
|
|
130
109
|
@cleanup_interval = config[:cleanup_interval] || DEFAULT_DELETE_INTERVAL
|
@@ -190,6 +169,24 @@ SQL
|
|
190
169
|
}
|
191
170
|
end
|
192
171
|
|
172
|
+
def compress_data(data, compression)
|
173
|
+
if compression == 'gzip'
|
174
|
+
require 'zlib'
|
175
|
+
require 'stringio'
|
176
|
+
io = StringIO.new
|
177
|
+
io.set_encoding(Encoding::ASCII_8BIT)
|
178
|
+
gz = Zlib::GzipWriter.new(io)
|
179
|
+
begin
|
180
|
+
gz.write(data)
|
181
|
+
ensure
|
182
|
+
gz.close
|
183
|
+
end
|
184
|
+
data = io.string
|
185
|
+
data = Sequel::SQL::Blob.new(data)
|
186
|
+
end
|
187
|
+
data
|
188
|
+
end
|
189
|
+
|
193
190
|
# => Task
|
194
191
|
def submit(key, type, data, options)
|
195
192
|
now = (options[:now] || Time.now).to_i
|
@@ -200,23 +197,7 @@ SQL
|
|
200
197
|
max_running = options[:max_running]
|
201
198
|
data = data ? data.dup : {}
|
202
199
|
data['type'] = type
|
203
|
-
|
204
|
-
d = data.to_json
|
205
|
-
|
206
|
-
if options[:compression] == 'gzip'
|
207
|
-
require 'zlib'
|
208
|
-
require 'stringio'
|
209
|
-
io = StringIO.new
|
210
|
-
gz = Zlib::GzipWriter.new(io)
|
211
|
-
begin
|
212
|
-
gz.write(d)
|
213
|
-
ensure
|
214
|
-
gz.close
|
215
|
-
end
|
216
|
-
d = io.string
|
217
|
-
d.force_encoding('ASCII-8BIT') if d.respond_to?(:force_encoding)
|
218
|
-
d = Sequel::SQL::Blob.new(d)
|
219
|
-
end
|
200
|
+
d = compress_data(data.to_json, options[:compression])
|
220
201
|
|
221
202
|
connect {
|
222
203
|
begin
|
@@ -240,7 +221,7 @@ SQL
|
|
240
221
|
|
241
222
|
if @cleanup_interval_count <= 0
|
242
223
|
connect_locked {
|
243
|
-
@db["DELETE FROM `#{@table}` WHERE
|
224
|
+
@db["DELETE FROM `#{@table}` WHERE timeout <= ? AND created_at IS NULL", now].delete
|
244
225
|
@cleanup_interval_count = @cleanup_interval
|
245
226
|
}
|
246
227
|
end
|
@@ -281,8 +262,6 @@ SQL
|
|
281
262
|
|
282
263
|
# => nil
|
283
264
|
def cancel_request(key, options)
|
284
|
-
now = (options[:now] || Time.now).to_i
|
285
|
-
|
286
265
|
# created_at=0 means cancel_requested
|
287
266
|
connect {
|
288
267
|
n = @db["UPDATE `#{@table}` SET created_at=0 WHERE id=? AND created_at IS NOT NULL", key].update
|
@@ -323,7 +302,7 @@ SQL
|
|
323
302
|
params = [sql, next_timeout]
|
324
303
|
if data
|
325
304
|
sql << ", data=?"
|
326
|
-
params << data.to_json
|
305
|
+
params << compress_data(data.to_json, options[:compression])
|
327
306
|
end
|
328
307
|
sql << " WHERE id=? AND created_at IS NOT NULL"
|
329
308
|
params << key
|
@@ -336,8 +315,6 @@ SQL
|
|
336
315
|
raise PreemptedError, "task key=#{key} does not exist or preempted."
|
337
316
|
elsif row[:created_at] == nil
|
338
317
|
raise PreemptedError, "task key=#{key} preempted."
|
339
|
-
elsif row[:created_at] <= 0
|
340
|
-
raise CancelRequestedError, "task key=#{key} is cancel requested."
|
341
318
|
else # row[:timeout] == next_timeout
|
342
319
|
# ok
|
343
320
|
end
|
@@ -395,6 +372,8 @@ SQL
|
|
395
372
|
else
|
396
373
|
STDERR.puts err + "\n abort."
|
397
374
|
end
|
375
|
+
else
|
376
|
+
err = $!
|
398
377
|
end
|
399
378
|
|
400
379
|
STDERR.puts "disconnects current connection: #{err}"
|
@@ -411,6 +390,7 @@ SQL
|
|
411
390
|
GZIP_MAGIC_BYTES = [0x1f, 0x8b].pack('CC')
|
412
391
|
|
413
392
|
def create_attributes(now, row)
|
393
|
+
compression = nil
|
414
394
|
if row[:created_at] === nil
|
415
395
|
created_at = nil # unknown creation time
|
416
396
|
status = TaskStatus::FINISHED
|
@@ -434,6 +414,7 @@ SQL
|
|
434
414
|
if d[0, 2] == GZIP_MAGIC_BYTES
|
435
415
|
require 'zlib'
|
436
416
|
require 'stringio'
|
417
|
+
compression = 'gzip'
|
437
418
|
gz = Zlib::GzipReader.new(StringIO.new(d))
|
438
419
|
begin
|
439
420
|
d = gz.read
|
@@ -464,6 +445,7 @@ SQL
|
|
464
445
|
:max_running => row[:max_running],
|
465
446
|
:message => nil, # not supported
|
466
447
|
:node => nil, # not supported
|
448
|
+
:compression => compression,
|
467
449
|
}
|
468
450
|
end
|
469
451
|
|
data/lib/perfectqueue/engine.rb
CHANGED
@@ -56,7 +56,7 @@ module PerfectQueue
|
|
56
56
|
@processors << @processor_class.new(@runner, @processors.size+1, config)
|
57
57
|
end
|
58
58
|
elsif extra < 0
|
59
|
-
-extra.times do
|
59
|
+
(-extra).times do
|
60
60
|
c = @processors.shift
|
61
61
|
c.stop(immediate)
|
62
62
|
c.join
|
@@ -106,10 +106,7 @@ module PerfectQueue
|
|
106
106
|
def replace(immediate, command=[$0]+ARGV)
|
107
107
|
return if @replaced_pid
|
108
108
|
stop(immediate)
|
109
|
-
@replaced_pid = Process.
|
110
|
-
exec(*command)
|
111
|
-
exit!(127)
|
112
|
-
end
|
109
|
+
@replaced_pid = Process.spawn(*command)
|
113
110
|
self
|
114
111
|
end
|
115
112
|
|
@@ -121,7 +121,7 @@ module PerfectQueue
|
|
121
121
|
end
|
122
122
|
|
123
123
|
def process(task)
|
124
|
-
@log.info "acquired task task=#{task.key} id=#{@processor_id}
|
124
|
+
@log.info "acquired task task=#{task.key} id=#{@processor_id}"
|
125
125
|
begin
|
126
126
|
r = @runner.new(task)
|
127
127
|
@tm.set_task(task, r)
|
data/lib/perfectqueue/task.rb
CHANGED
@@ -58,6 +58,7 @@ module PerfectQueue
|
|
58
58
|
class TaskWithMetadata < Task
|
59
59
|
def initialize(client, key, attributes)
|
60
60
|
super(client, key)
|
61
|
+
@compression = attributes.delete(:compression)
|
61
62
|
@attributes = attributes
|
62
63
|
end
|
63
64
|
|
@@ -93,7 +94,7 @@ module PerfectQueue
|
|
93
94
|
def update_data!(hash)
|
94
95
|
data = @attributes[:data] || {}
|
95
96
|
merged = data.merge(hash)
|
96
|
-
heartbeat!(:
|
97
|
+
heartbeat!(data: merged, compression: compression)
|
97
98
|
@attributes[:data] = merged
|
98
99
|
end
|
99
100
|
|
@@ -19,6 +19,7 @@
|
|
19
19
|
module PerfectQueue
|
20
20
|
module TaskMetadataAccessors
|
21
21
|
attr_reader :attributes
|
22
|
+
attr_reader :compression
|
22
23
|
|
23
24
|
def type
|
24
25
|
@attributes[:type]
|
@@ -60,10 +61,6 @@ module PerfectQueue
|
|
60
61
|
status == TaskStatus::FINISHED
|
61
62
|
end
|
62
63
|
|
63
|
-
def running?
|
64
|
-
status == TaskStatus::RUNNING
|
65
|
-
end
|
66
|
-
|
67
64
|
def waiting?
|
68
65
|
status == TaskStatus::WAITING
|
69
66
|
end
|
@@ -83,6 +80,7 @@ module PerfectQueue
|
|
83
80
|
def initialize(client, key, attributes)
|
84
81
|
super(client)
|
85
82
|
@key = key
|
83
|
+
@compression = attributes.delete(:compression)
|
86
84
|
@attributes = attributes
|
87
85
|
end
|
88
86
|
|
data/lib/perfectqueue/version.rb
CHANGED
data/perfectqueue.gemspec
CHANGED
@@ -19,8 +19,7 @@ Gem::Specification.new do |gem|
|
|
19
19
|
|
20
20
|
gem.add_dependency "sequel", "~> 3.48.0"
|
21
21
|
gem.add_development_dependency "rake", "~> 0.9.2"
|
22
|
-
gem.add_development_dependency "rspec", "~>
|
22
|
+
gem.add_development_dependency "rspec", "~> 3.3.0"
|
23
23
|
gem.add_development_dependency "simplecov", "~> 0.10.0"
|
24
|
-
gem.add_development_dependency "sqlite3", "~> 1.3.3"
|
25
24
|
gem.add_development_dependency "mysql2", "~> 0.3.20"
|
26
25
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PerfectQueue::Application::Base do
|
4
|
+
describe '.decider=' do
|
5
|
+
it 'defines .decider which returns the decider' do
|
6
|
+
decider_klass = double('decider_klass')
|
7
|
+
klass = PerfectQueue::Application::Base
|
8
|
+
allow(klass).to receive(:decider).and_call_original
|
9
|
+
allow(klass).to receive(:decider=).with(decider_klass).and_call_original
|
10
|
+
expect(klass.decider = decider_klass).to eq(decider_klass)
|
11
|
+
expect(klass.decider).to eq(decider_klass)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.decider' do
|
16
|
+
it 'returns DefaultDecider' do
|
17
|
+
expect(PerfectQueue::Application::Base.decider).to eq(PerfectQueue::Application::DefaultDecider)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#new' do
|
22
|
+
let (:task){ double('task') }
|
23
|
+
let (:base) { PerfectQueue::Application::Base.new(task) }
|
24
|
+
it 'calls super and set decider'do
|
25
|
+
expect(base).to be_an_instance_of(PerfectQueue::Application::Base)
|
26
|
+
expect(base.instance_variable_get(:@task)).to eq(task)
|
27
|
+
expect(base.instance_variable_get(:@decider)).to be_an_instance_of(Application::DefaultDecider)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#run' do
|
32
|
+
let (:base) { PerfectQueue::Application::Base.new(double('task')) }
|
33
|
+
it 'returns nil if before_perform returns false' do
|
34
|
+
allow(base).to receive(:before_perform).and_return(false)
|
35
|
+
expect(base.run).to be_nil
|
36
|
+
end
|
37
|
+
it 'returns nil' do
|
38
|
+
expect(base).to receive(:before_perform).exactly(:once).and_call_original
|
39
|
+
expect(base).to receive(:perform).exactly(:once).and_return(nil)
|
40
|
+
expect(base).to receive(:after_perform).exactly(:once).and_call_original
|
41
|
+
expect(base.run).to be_nil
|
42
|
+
end
|
43
|
+
it 'calls unexpected_error_raised on error' do
|
44
|
+
allow(base).to receive(:before_perform).exactly(:once).and_call_original
|
45
|
+
allow(base).to receive(:perform).exactly(:once) { raise }
|
46
|
+
allow(base).to receive(:decide!).with(:unexpected_error_raised, error: kind_of(Exception)).exactly(:once)
|
47
|
+
expect(base.run).to be_nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#before_perform' do
|
52
|
+
let (:base) { PerfectQueue::Application::Base.new(double('task')) }
|
53
|
+
it 'returns true' do
|
54
|
+
expect(base.before_perform).to be true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#after_perform' do
|
59
|
+
let (:base) { PerfectQueue::Application::Base.new(double('task')) }
|
60
|
+
it 'returns nil' do
|
61
|
+
expect(base.after_perform).to be_nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#decide!' do
|
66
|
+
let (:base) do
|
67
|
+
decider = double('decider')
|
68
|
+
expect(decider).to receive(:decide!).with(:type, :option).exactly(:once)
|
69
|
+
decider_klass = double('decider_klass')
|
70
|
+
allow(decider_klass).to receive(:new).with(kind_of(PerfectQueue::Application::Base)).and_return(decider)
|
71
|
+
klass = PerfectQueue::Application::Base
|
72
|
+
allow(klass).to receive(:decider).and_call_original
|
73
|
+
allow(klass).to receive(:decider=).with(decider_klass).and_call_original
|
74
|
+
klass.decider = decider_klass
|
75
|
+
klass.new(double('task'))
|
76
|
+
end
|
77
|
+
it 'calls decider.decide' do
|
78
|
+
expect(base.decide!(:type, :option)).to be_nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PerfectQueue::Application::UndefinedDecisionError do
|
4
|
+
it { is_expected.to be_an_instance_of(PerfectQueue::Application::UndefinedDecisionError) }
|
5
|
+
it { is_expected.to be_a(Exception) }
|
6
|
+
end
|
7
|
+
|
8
|
+
describe PerfectQueue::Application::Decider do
|
9
|
+
describe '#new' do
|
10
|
+
let (:decider) { PerfectQueue::Application::Decider.new(nil) }
|
11
|
+
it do
|
12
|
+
expect(decider).to be_an_instance_of(PerfectQueue::Application::Decider)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#queue' do
|
17
|
+
let (:queue){ double('queue') }
|
18
|
+
let (:decider) do
|
19
|
+
base = double('base')
|
20
|
+
allow(base).to receive(:queue).exactly(:once).and_return(queue)
|
21
|
+
PerfectQueue::Application::Decider.new(base)
|
22
|
+
end
|
23
|
+
it 'calls @base.queue' do
|
24
|
+
expect(decider.queue).to eq(queue)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#task' do
|
29
|
+
let (:task){ double('task') }
|
30
|
+
let (:decider) do
|
31
|
+
base = double('base')
|
32
|
+
allow(base).to receive(:task).exactly(:once).and_return(task)
|
33
|
+
PerfectQueue::Application::Decider.new(base)
|
34
|
+
end
|
35
|
+
it 'calls @base.task' do
|
36
|
+
expect(decider.task).to eq(task)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#decide!' do
|
41
|
+
let (:decider) { PerfectQueue::Application::Decider.new(nil) }
|
42
|
+
it 'calls the specified method' do
|
43
|
+
allow(decider).to receive(:foo).exactly(:once).with(72).and_return(42)
|
44
|
+
expect(decider.decide!(:foo, 72)).to eq(42)
|
45
|
+
end
|
46
|
+
it 'raises UndefinedDecisionError on unknown method' do
|
47
|
+
expect{ decider.decide!(:foo, 72) }.to raise_error(PerfectQueue::Application::UndefinedDecisionError)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe PerfectQueue::Application::DefaultDecider do
|
53
|
+
subject { PerfectQueue::Application::DefaultDecider.new(nil) }
|
54
|
+
it { is_expected.to be_a(PerfectQueue::Application::Decider) }
|
55
|
+
it { is_expected.to be_an_instance_of(PerfectQueue::Application::DefaultDecider) }
|
56
|
+
end
|