creeper 1.0.9 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.rvmrc +48 -0
- data/Gemfile +17 -1
- data/Guardfile +32 -0
- data/Rakefile +9 -1
- data/bin/creeper +10 -58
- data/bin/creeperctl +74 -0
- data/config.ru +18 -0
- data/creeper.gemspec +19 -9
- data/lib/creeper.rb +108 -413
- data/lib/creeper/beanstalk_connection.rb +35 -0
- data/lib/creeper/cli.rb +225 -0
- data/lib/creeper/client.rb +93 -0
- data/lib/creeper/core_ext.rb +54 -0
- data/lib/creeper/exception_handler.rb +30 -0
- data/lib/creeper/extensions/action_mailer.rb +33 -0
- data/lib/creeper/extensions/active_record.rb +30 -0
- data/lib/creeper/extensions/generic_proxy.rb +26 -0
- data/lib/creeper/fetch.rb +94 -0
- data/lib/creeper/legacy.rb +46 -0
- data/lib/creeper/logging.rb +46 -0
- data/lib/creeper/manager.rb +164 -0
- data/lib/creeper/middleware/chain.rb +100 -0
- data/lib/creeper/middleware/server/active_record.rb +13 -0
- data/lib/creeper/middleware/server/logging.rb +31 -0
- data/lib/creeper/middleware/server/retry_jobs.rb +79 -0
- data/lib/creeper/middleware/server/timeout.rb +21 -0
- data/lib/creeper/paginator.rb +31 -0
- data/lib/creeper/processor.rb +116 -0
- data/lib/creeper/rails.rb +21 -0
- data/lib/creeper/redis_connection.rb +28 -0
- data/lib/creeper/testing.rb +44 -0
- data/lib/creeper/util.rb +45 -0
- data/lib/creeper/version.rb +1 -1
- data/lib/creeper/web.rb +248 -0
- data/lib/creeper/worker.rb +62 -313
- data/spec/dummy/.gitignore +15 -0
- data/spec/dummy/Gemfile +51 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/images/rails.png +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/work_controller.rb +71 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/mailers/user_mailer.rb +9 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/post.rb +8 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/app/views/user_mailer/greetings.html.erb +3 -0
- data/spec/dummy/app/views/work/index.html.erb +1 -0
- data/spec/dummy/app/workers/fast_worker.rb +10 -0
- data/spec/dummy/app/workers/hard_worker.rb +11 -0
- data/spec/dummy/app/workers/lazy_worker.rb +12 -0
- data/spec/dummy/app/workers/suicidal_worker.rb +33 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +68 -0
- data/spec/dummy/config/boot.rb +6 -0
- data/spec/dummy/config/creeper.yml +9 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/creeper.rb +8 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +13 -0
- data/spec/dummy/db/migrate/20120123214055_create_posts.rb +10 -0
- data/spec/dummy/db/schema.rb +23 -0
- data/spec/dummy/db/seeds.rb +7 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/lib/tasks/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/index.html +241 -0
- data/spec/dummy/public/robots.txt +5 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/vendor/assets/javascripts/.gitkeep +0 -0
- data/spec/dummy/vendor/assets/stylesheets/.gitkeep +0 -0
- data/spec/dummy/vendor/plugins/.gitkeep +0 -0
- data/spec/lib/creeper/cli_spec.rb +208 -0
- data/spec/lib/creeper/client_spec.rb +110 -0
- data/spec/lib/creeper/exception_handler_spec.rb +110 -0
- data/spec/lib/creeper/processor_spec.rb +92 -0
- data/spec/lib/creeper/testing_spec.rb +105 -0
- data/spec/lib/creeper_spec.rb +54 -120
- data/spec/spec_helper.rb +81 -7
- data/spec/support/config.yml +9 -0
- data/spec/support/fake_env.rb +0 -0
- data/spec/support/workers/base_worker.rb +11 -0
- data/spec/support/workers/my_worker.rb +4 -0
- data/spec/support/workers/queued_worker.rb +5 -0
- data/spec/support/workers/real_worker.rb +10 -0
- data/web/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
- data/web/assets/images/bootstrap/glyphicons-halflings.png +0 -0
- data/web/assets/javascripts/application.js +49 -0
- data/web/assets/javascripts/vendor/bootstrap.js +12 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-alert.js +91 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-button.js +98 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-carousel.js +154 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-collapse.js +136 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-dropdown.js +92 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-modal.js +210 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-popover.js +95 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-scrollspy.js +125 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-tab.js +130 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-tooltip.js +270 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-transition.js +51 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-typeahead.js +271 -0
- data/web/assets/javascripts/vendor/jquery.js +9266 -0
- data/web/assets/javascripts/vendor/jquery.timeago.js +148 -0
- data/web/assets/stylesheets/application.css +6 -0
- data/web/assets/stylesheets/layout.css +26 -0
- data/web/assets/stylesheets/vendor/bootstrap-responsive.css +567 -0
- data/web/assets/stylesheets/vendor/bootstrap.css +3365 -0
- data/web/views/_paging.slim +15 -0
- data/web/views/_summary.slim +9 -0
- data/web/views/_workers.slim +14 -0
- data/web/views/index.slim +10 -0
- data/web/views/layout.slim +37 -0
- data/web/views/poll.slim +3 -0
- data/web/views/queue.slim +15 -0
- data/web/views/queues.slim +19 -0
- data/web/views/retries.slim +31 -0
- data/web/views/retry.slim +52 -0
- data/web/views/scheduled.slim +27 -0
- metadata +341 -23
- data/lib/creeper/celluloid_ext.rb +0 -42
- data/lib/creeper/creep.rb +0 -25
- data/lib/creeper/err_logger.rb +0 -37
- data/lib/creeper/launcher.rb +0 -44
- data/lib/creeper/out_logger.rb +0 -39
- data/spec/lib/creeper/session_spec.rb +0 -15
- data/spec/lib/creeper/worker_spec.rb +0 -21
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'creeper/client'
|
3
|
+
require 'creeper/worker'
|
4
|
+
|
5
|
+
describe Creeper::Client do
|
6
|
+
|
7
|
+
context 'with mock beanstalk and redis' do
|
8
|
+
before do
|
9
|
+
@beanstalk = double('beanstalk')
|
10
|
+
@connection = double('connection')
|
11
|
+
def @beanstalk.on_tube(tube); yield @connection if block_given?; end
|
12
|
+
Creeper.instance_variable_set(:@beanstalk, @beanstalk)
|
13
|
+
@redis = double('redis')
|
14
|
+
def @redis.multi; [yield] * 2 if block_given?; end
|
15
|
+
def @redis.set(*); true; end
|
16
|
+
def @redis.sadd(*); true; end
|
17
|
+
def @redis.srem(*); true; end
|
18
|
+
def @redis.get(*); nil; end
|
19
|
+
def @redis.del(*); nil; end
|
20
|
+
def @redis.incrby(*); nil; end
|
21
|
+
def @redis.setex(*); true; end
|
22
|
+
def @redis.expire(*); true; end
|
23
|
+
def @redis.watch(*); true; end
|
24
|
+
def @redis.with_connection; yield self; end
|
25
|
+
def @redis.with; yield self; end
|
26
|
+
def @redis.exec; true; end
|
27
|
+
Creeper.instance_variable_set(:@redis, @redis)
|
28
|
+
end
|
29
|
+
|
30
|
+
after do
|
31
|
+
Creeper.instance_variable_set(:@beanstalk, nil)
|
32
|
+
Creeper.instance_variable_set(:@redis, nil)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'raises ArgumentError with invalid params' do
|
36
|
+
expect do
|
37
|
+
Creeper::Client.push('foo', 1)
|
38
|
+
end.to raise_error(ArgumentError)
|
39
|
+
|
40
|
+
expect do
|
41
|
+
Creeper::Client.push('foo', :class => 'Foo', :noargs => [1, 2])
|
42
|
+
end.to raise_error(ArgumentError)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'pushes messages to beanstalk' do
|
46
|
+
@beanstalk.should_receive(:on_tube).with('foo').and_yield(@connection)
|
47
|
+
@connection.should_receive(:put).with(%q(["foo",{"retry":true,"queue":"foo","class":"MyWorker","args":[1,2]}])).and_return(24)
|
48
|
+
pushed = Creeper::Client.push('queue' => 'foo', 'class' => MyWorker, 'args' => [1, 2])
|
49
|
+
expect(pushed).to eq(24)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'has default options' do
|
53
|
+
expect(Creeper::Worker::ClassMethods::DEFAULT_OPTIONS).to eq(MyWorker.get_creeper_options)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'handles perform_async' do
|
57
|
+
@beanstalk.should_receive(:on_tube).with('default').and_yield(@connection)
|
58
|
+
@connection.should_receive(:put).with(%q(["default",{"retry":true,"queue":"default","class":"MyWorker","args":[1,2]}])).and_return(24)
|
59
|
+
pushed = MyWorker.perform_async(1, 2)
|
60
|
+
expect(pushed).to eq(24)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'handles perform_async on failure' do
|
64
|
+
@beanstalk.should_receive(:on_tube).with('default').and_yield(@connection)
|
65
|
+
@connection.should_receive(:put).with(%q(["default",{"retry":true,"queue":"default","class":"MyWorker","args":[1,2]}])).and_return(nil)
|
66
|
+
pushed = MyWorker.perform_async(1, 2)
|
67
|
+
expect(pushed).to be_nil
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'enqueues messages to beanstalk' do
|
71
|
+
@beanstalk.should_receive(:on_tube).with('default').and_yield(@connection)
|
72
|
+
@connection.should_receive(:put).with(%q(["default",{"retry":true,"queue":"default","class":"MyWorker","args":[1,2]}])).and_return(24)
|
73
|
+
pushed = Creeper::Client.enqueue(MyWorker, 1, 2)
|
74
|
+
expect(pushed).to eq(24)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'enqueues messages to beanstalk using legacy method' do
|
78
|
+
@beanstalk.should_receive(:on_tube).with('my.worker').and_yield(@connection)
|
79
|
+
@connection.should_receive(:put).with(%q(["my.worker",{"retry":true,"queue":"my.worker","args":[1,2],"class":"MyWorker","delay":0,"priority":65536,"time_to_run":120}]), 65536, 0, 120).and_return(24)
|
80
|
+
pushed = Creeper.enqueue('my.worker', 1, 2)
|
81
|
+
expect(pushed).to eq(24)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'enqueues to the named queue' do
|
85
|
+
@beanstalk.should_receive(:on_tube).with(:flimflam).and_yield(@connection)
|
86
|
+
@connection.should_receive(:put).with(%q(["flimflam",{"retry":true,"queue":"flimflam","timeout":1,"class":"QueuedWorker","args":[1,2]}])).and_return(24)
|
87
|
+
pushed = QueuedWorker.perform_async(1, 2)
|
88
|
+
expect(pushed).to eq(24)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'retrieves queues' do
|
92
|
+
@redis.should_receive(:smembers).with('queues').and_return(['bob'])
|
93
|
+
expect(Creeper::Client.registered_queues).to eq(['bob'])
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'retrieves workers' do
|
97
|
+
@redis.should_receive(:smembers).with('workers').and_return(['bob'])
|
98
|
+
expect(Creeper::Client.registered_workers).to eq(['bob'])
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
describe 'inheritance' do
|
104
|
+
it 'should inherit creeper options' do
|
105
|
+
expect(AWorker.get_creeper_options['retry']).to eq('base')
|
106
|
+
expect(BWorker.get_creeper_options['retry']).to eq('b')
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'creeper'
|
3
|
+
require 'creeper/exception_handler'
|
4
|
+
require 'stringio'
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
describe Creeper::ExceptionHandler do
|
8
|
+
|
9
|
+
before do
|
10
|
+
stub_const('ExceptionHandlerTestException', Class.new(StandardError))
|
11
|
+
stub_const('TEST_EXCEPTION', ExceptionHandlerTestException.new("Something didn't work!"))
|
12
|
+
end
|
13
|
+
|
14
|
+
class Component
|
15
|
+
include Creeper::Util
|
16
|
+
|
17
|
+
def invoke_exception(args)
|
18
|
+
raise TEST_EXCEPTION
|
19
|
+
rescue ExceptionHandlerTestException => e
|
20
|
+
handle_exception(e,args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "with mock logger" do
|
25
|
+
before do
|
26
|
+
@old_logger = Creeper.logger
|
27
|
+
@str_logger = StringIO.new
|
28
|
+
Creeper.logger = Logger.new(@str_logger)
|
29
|
+
end
|
30
|
+
|
31
|
+
after do
|
32
|
+
Creeper.logger = @old_logger
|
33
|
+
end
|
34
|
+
|
35
|
+
it "logs the exception to Creeper.logger" do
|
36
|
+
Component.new.invoke_exception(:a => 1)
|
37
|
+
@str_logger.rewind
|
38
|
+
log = @str_logger.readlines
|
39
|
+
expect(log[0]).to match(/a=>1/) # didn't include the context
|
40
|
+
expect(log[1]).to match(/Something didn't work!/) # didn't include the exception message
|
41
|
+
expect(log[2]).to match(/spec\/lib\/creeper\/exception_handler_spec\.rb/) # didn't include the backtrace
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "with fake Airbrake" do
|
46
|
+
before do
|
47
|
+
::Airbrake = double('Airbrake')
|
48
|
+
end
|
49
|
+
|
50
|
+
after do
|
51
|
+
Object.send(:remove_const, "Airbrake") # HACK should probably inject Airbrake etc into this class in the future
|
52
|
+
end
|
53
|
+
|
54
|
+
it "notifies Airbrake" do
|
55
|
+
::Airbrake.should_receive(:notify).with(TEST_EXCEPTION,:parameters => { :a => 1 }).and_return(nil)
|
56
|
+
Component.new.invoke_exception(:a => 1)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "with fake ExceptionNotifier" do
|
61
|
+
before do
|
62
|
+
::ExceptionNotifier = Module.new
|
63
|
+
::ExceptionNotifier::Notifier = double('ExceptionNotifier::Notifier')
|
64
|
+
end
|
65
|
+
|
66
|
+
after do
|
67
|
+
Object.send(:remove_const, "ExceptionNotifier")
|
68
|
+
end
|
69
|
+
|
70
|
+
it "notifies ExceptionNotifier" do
|
71
|
+
mail = double('mail')
|
72
|
+
mail.should_receive(:deliver).and_return(nil)
|
73
|
+
::ExceptionNotifier::Notifier.should_receive(:background_exception_notification).with(TEST_EXCEPTION, :data => { :message => { :b => 2 } }).and_return(mail)
|
74
|
+
Component.new.invoke_exception(:b => 2)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "with fake Exceptional" do
|
79
|
+
before do
|
80
|
+
::Exceptional = Class.new do
|
81
|
+
|
82
|
+
def self.context(msg)
|
83
|
+
@msg = msg
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.check_context
|
87
|
+
@msg
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
::Exceptional::Config = double('Exceptional::Config')
|
92
|
+
::Exceptional::Remote = double('Exceptional::Remote')
|
93
|
+
::Exceptional::ExceptionData = double('Exceptional::ExceptionData')
|
94
|
+
end
|
95
|
+
|
96
|
+
after do
|
97
|
+
Object.send(:remove_const, "Exceptional")
|
98
|
+
end
|
99
|
+
|
100
|
+
it "notifies Exceptional" do
|
101
|
+
::Exceptional::Config.should_receive(:should_send_to_api?).and_return(true)
|
102
|
+
exception_data = double('exception_data')
|
103
|
+
::Exceptional::Remote.should_receive(:error).with(exception_data).and_return(nil)
|
104
|
+
::Exceptional::ExceptionData.should_receive(:new).with(TEST_EXCEPTION).and_return(exception_data)
|
105
|
+
Component.new.invoke_exception(:c => 3)
|
106
|
+
expect(::Exceptional.check_context).to eq({:c => 3}) # did not record arguments properly
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'creeper/processor'
|
3
|
+
|
4
|
+
describe Creeper::Processor do
|
5
|
+
|
6
|
+
context 'with mock setup' do
|
7
|
+
|
8
|
+
before do
|
9
|
+
stub_const('TestException', Class.new(StandardError))
|
10
|
+
stub_const('TEST_EXCEPTION', TestException.new("kerboom!"))
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:boss) { double('boss') }
|
14
|
+
let(:processor) { ::Creeper::Processor.new(boss) }
|
15
|
+
|
16
|
+
let(:job) { double('job') }
|
17
|
+
let(:conn) { double('conn') }
|
18
|
+
|
19
|
+
before do
|
20
|
+
$invokes = 0
|
21
|
+
Celluloid.logger = nil
|
22
|
+
Creeper.redis = REDIS
|
23
|
+
end
|
24
|
+
|
25
|
+
class MockWorker
|
26
|
+
include Creeper::Worker
|
27
|
+
def perform(args)
|
28
|
+
raise TEST_EXCEPTION if args == 'boom'
|
29
|
+
args.pop if args.is_a? Array
|
30
|
+
$invokes += 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'processes as expected' do
|
35
|
+
msg = Creeper.dump_json({ 'class' => MockWorker.to_s, 'args' => ['myarg'] })
|
36
|
+
|
37
|
+
conn.should_receive(:close).and_return(true)
|
38
|
+
job.should_receive(:id).and_return(1)
|
39
|
+
job.should_receive(:delete).and_return(true)
|
40
|
+
boss.should_receive(:processor_done!).with(processor).and_return(nil)
|
41
|
+
|
42
|
+
processor.process(msg, 'default', job, conn)
|
43
|
+
expect($invokes).to eq(1)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'passes exceptions to ExceptionHandler' do
|
47
|
+
msg = Creeper.dump_json({ 'class' => MockWorker.to_s, 'args' => ['boom'] })
|
48
|
+
|
49
|
+
conn.should_receive(:close).and_return(true)
|
50
|
+
job.should_receive(:id).and_return(1)
|
51
|
+
job.should_receive(:bury).and_return(true)
|
52
|
+
|
53
|
+
expect do
|
54
|
+
processor.process(msg, 'default', job, conn)
|
55
|
+
end.to raise_error(TestException)
|
56
|
+
|
57
|
+
expect($invokes).to eq(0)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 're-raises exceptions after handling' do
|
61
|
+
msg = Creeper.dump_json({ 'class' => MockWorker.to_s, 'args' => ['boom'] })
|
62
|
+
re_raise = false
|
63
|
+
|
64
|
+
conn.should_receive(:close).and_return(true)
|
65
|
+
job.should_receive(:id).and_return(1)
|
66
|
+
job.should_receive(:bury).and_return(true)
|
67
|
+
|
68
|
+
begin
|
69
|
+
processor.process(msg, 'default', job, conn)
|
70
|
+
rescue TestException
|
71
|
+
re_raise = true
|
72
|
+
end
|
73
|
+
|
74
|
+
expect(re_raise).to be_true # does not re-raise exceptions after handling
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'does not modify original arguments' do
|
78
|
+
msg = { 'class' => MockWorker.to_s, 'args' => [['myarg']] }
|
79
|
+
msgstr = Creeper.dump_json(msg)
|
80
|
+
|
81
|
+
conn.should_receive(:close).and_return(true)
|
82
|
+
job.should_receive(:id).and_return(1)
|
83
|
+
job.should_receive(:delete).and_return(true)
|
84
|
+
boss.should_receive(:processor_done!).with(processor).and_return(nil)
|
85
|
+
|
86
|
+
processor.process(msgstr, 'default', job, conn)
|
87
|
+
expect(msg['args']).to eq([['myarg']])
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'creeper'
|
3
|
+
require 'creeper/worker'
|
4
|
+
require 'active_record'
|
5
|
+
require 'action_mailer'
|
6
|
+
require 'creeper/rails'
|
7
|
+
require 'creeper/extensions/action_mailer'
|
8
|
+
require 'creeper/extensions/active_record'
|
9
|
+
|
10
|
+
Creeper.hook_rails!
|
11
|
+
|
12
|
+
describe Creeper::Worker do
|
13
|
+
|
14
|
+
context 'creeper testing' do
|
15
|
+
class PerformError < RuntimeError; end
|
16
|
+
|
17
|
+
class DirectWorker
|
18
|
+
include Creeper::Worker
|
19
|
+
def perform(a, b)
|
20
|
+
a + b
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class EnqueuedWorker
|
25
|
+
include Creeper::Worker
|
26
|
+
def perform(a, b)
|
27
|
+
a + b
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class StoredWorker
|
32
|
+
include Creeper::Worker
|
33
|
+
def perform(error)
|
34
|
+
raise PerformError if error
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class FooMailer < ActionMailer::Base
|
39
|
+
def bar(str)
|
40
|
+
str
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class FooModel < ActiveRecord::Base
|
45
|
+
def bar(str)
|
46
|
+
str
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
before do
|
51
|
+
load 'creeper/testing.rb'
|
52
|
+
end
|
53
|
+
|
54
|
+
after do
|
55
|
+
# Undo override
|
56
|
+
Creeper::Worker::ClassMethods.class_eval do
|
57
|
+
remove_method :client_push
|
58
|
+
alias_method :client_push, :client_push_old
|
59
|
+
remove_method :client_push_old
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'stubs the async call' do
|
64
|
+
expect(DirectWorker.jobs.size).to eq(0)
|
65
|
+
expect(DirectWorker.perform_async(1, 2)).to be_true
|
66
|
+
expect(DirectWorker.jobs.size).to eq(1)
|
67
|
+
expect(DirectWorker.perform_in(10, 1, 2)).to be_true
|
68
|
+
expect(DirectWorker.jobs.size).to eq(2)
|
69
|
+
expect(DirectWorker.perform_at(10, 1, 2)).to be_true
|
70
|
+
expect(DirectWorker.jobs.size).to eq(3)
|
71
|
+
expect(DirectWorker.jobs.last['at']).to be_within(0.1).of(10.seconds.from_now.to_f)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'stubs the delay call on mailers' do
|
75
|
+
expect(Creeper::Extensions::DelayedMailer.jobs.size).to eq(0)
|
76
|
+
FooMailer.delay.bar('hello!')
|
77
|
+
expect(Creeper::Extensions::DelayedMailer.jobs.size).to eq(1)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'stubs the delay call on models' do
|
81
|
+
expect(Creeper::Extensions::DelayedModel.jobs.size).to eq(0)
|
82
|
+
FooModel.delay.bar('hello!')
|
83
|
+
expect(Creeper::Extensions::DelayedModel.jobs.size).to eq(1)
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'stubs the enqueue call' do
|
87
|
+
expect(EnqueuedWorker.jobs.size).to eq(0)
|
88
|
+
expect(Creeper::Client.enqueue(EnqueuedWorker, 1, 2)).to be_true
|
89
|
+
expect(EnqueuedWorker.jobs.size).to eq(1)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'executes all stored jobs' do
|
93
|
+
expect(StoredWorker.perform_async(false)).to be_true
|
94
|
+
expect(StoredWorker.perform_async(true)).to be_true
|
95
|
+
|
96
|
+
expect(StoredWorker.jobs.size).to eq(2)
|
97
|
+
expect do
|
98
|
+
StoredWorker.drain
|
99
|
+
end.to raise_error(PerformError)
|
100
|
+
expect(StoredWorker.jobs.size).to eq(0)
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
data/spec/lib/creeper_spec.rb
CHANGED
@@ -1,131 +1,65 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'creeper/cli'
|
2
3
|
|
3
4
|
describe Creeper do
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
it "work a job and do it up" do
|
10
|
-
val = rand(999999)
|
11
|
-
Creeper.job('my.job') { |args| $result = args['val'] }
|
12
|
-
Creeper.enqueue('my.job', :val => val)
|
13
|
-
w = Creeper::Worker.new
|
14
|
-
w.stub(:exception_message)
|
15
|
-
w.stub(:log)
|
16
|
-
w.prepare
|
17
|
-
w.work_one_job
|
18
|
-
val.should == $result
|
19
|
-
end
|
20
|
-
|
21
|
-
it "invoke error handler when defined" do
|
22
|
-
with_an_error_handler
|
23
|
-
Creeper.job('my.job') { |args| fail }
|
24
|
-
Creeper.enqueue('my.job', :foo => 123)
|
25
|
-
w = Creeper::Worker.new
|
26
|
-
w.stub(:exception_message)
|
27
|
-
w.stub(:log)
|
28
|
-
w.prepare
|
29
|
-
w.work_one_job
|
30
|
-
$handled.should_not == nil
|
31
|
-
'my.job'.should == $job_name
|
32
|
-
{'foo' => 123}.should == $job_args
|
33
|
-
end
|
34
|
-
|
35
|
-
it "should be compatible with legacy error handlers" do
|
36
|
-
exception = StandardError.new("Oh my, the job has failed!")
|
37
|
-
Creeper.error { |e| $handled = e }
|
38
|
-
Creeper.job('my.job') { |args| raise exception }
|
39
|
-
Creeper.enqueue('my.job')
|
40
|
-
w = Creeper::Worker.new
|
41
|
-
w.stub(:exception_message)
|
42
|
-
w.stub(:log)
|
43
|
-
w.prepare
|
44
|
-
w.work_one_job
|
45
|
-
exception.should == $handled
|
46
|
-
end
|
47
|
-
|
48
|
-
it "continue working when error handler not defined" do
|
49
|
-
Creeper.error { |e| $handled = false }
|
50
|
-
Creeper.job('my.job') { fail }
|
51
|
-
Creeper.enqueue('my.job')
|
52
|
-
w = Creeper::Worker.new
|
53
|
-
w.stub(:exception_message)
|
54
|
-
w.stub(:log)
|
55
|
-
w.prepare
|
56
|
-
w.work_one_job
|
57
|
-
false.should == $handled
|
58
|
-
end
|
59
|
-
|
60
|
-
it "exception raised one second before beanstalk ttr reached" do
|
61
|
-
with_an_error_handler
|
62
|
-
Creeper.job('my.job') { sleep(3); $handled = "didn't time out" }
|
63
|
-
Creeper.enqueue('my.job', {}, :ttr => 2)
|
64
|
-
w = Creeper::Worker.new
|
65
|
-
w.stub(:exception_message)
|
66
|
-
w.stub(:log)
|
67
|
-
w.prepare
|
68
|
-
w.work_one_job
|
69
|
-
$handled.should == "didn't time out"
|
70
|
-
end
|
6
|
+
it 'is able to process a real job' do
|
7
|
+
pending 'still need to figure out how to test the daemon'
|
8
|
+
let(:cli) { Creeper::CLI.instance }
|
71
9
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
10
|
+
before do
|
11
|
+
$rd, $wr = IO.pipe
|
12
|
+
$rd.sync, $wr.sync = true
|
13
|
+
if @pid = fork
|
14
|
+
# parent
|
15
|
+
$wr.close
|
16
|
+
else
|
17
|
+
# child
|
18
|
+
$rd.close
|
19
|
+
cli.parse(['creeper', '-r', './spec/support/fake_env.rb'])
|
20
|
+
sleep 5
|
21
|
+
begin
|
22
|
+
cli.run
|
23
|
+
rescue SystemExit
|
24
|
+
Process.kill(:KILL, $$)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
83
28
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
29
|
+
after do
|
30
|
+
if @pid
|
31
|
+
Process.kill(:TERM, @pid)
|
32
|
+
done = nil
|
33
|
+
# 8.times do
|
34
|
+
# begin
|
35
|
+
# Process.getpgid(@pid)
|
36
|
+
# rescue Errno::ESRCH
|
37
|
+
# done = true
|
38
|
+
# break
|
39
|
+
# end
|
40
|
+
# sleep 1
|
41
|
+
# end
|
42
|
+
# expect(done).to be_true
|
43
|
+
sleep 1
|
44
|
+
Process.kill(:KILL, @pid)
|
45
|
+
child_pid, status = Process.waitpid2(-1, Process::WNOHANG)
|
46
|
+
expect(child_pid).to eq(@pid)
|
47
|
+
# expect(status).to be_success
|
95
48
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
w = Creeper::Worker.new
|
101
|
-
w.stub(:exception_message)
|
102
|
-
w.stub(:log)
|
103
|
-
w.prepare
|
104
|
-
w.work_one_job
|
105
|
-
true.should == $handled
|
106
|
-
end
|
107
|
-
|
108
|
-
it "before filter invokes error handler when defined" do
|
109
|
-
with_an_error_handler
|
110
|
-
Creeper.before { |name| fail }
|
111
|
-
Creeper.job('my.job') { }
|
112
|
-
Creeper.enqueue('my.job', :foo => 123)
|
113
|
-
w = Creeper::Worker.new
|
114
|
-
w.stub(:exception_message)
|
115
|
-
w.stub(:log)
|
116
|
-
w.prepare
|
117
|
-
w.work_one_job
|
118
|
-
$handled.should_not == nil
|
119
|
-
'my.job'.should == $job_name
|
120
|
-
{'foo' => 123}.should == $job_args
|
121
|
-
end
|
49
|
+
$rd.close rescue nil
|
50
|
+
$wr.close rescue nil
|
51
|
+
end
|
52
|
+
end
|
122
53
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
54
|
+
it 'is able to process a real job' do
|
55
|
+
pending 'still need to figure out how to test the daemon'
|
56
|
+
RealWorker.perform_async(1, 2)
|
57
|
+
result = nil
|
58
|
+
Timeout::timeout(10) do
|
59
|
+
result = $rd.read
|
60
|
+
end
|
61
|
+
expect(result).to eq('[1,2]')
|
128
62
|
end
|
129
63
|
end
|
130
|
-
|
64
|
+
|
131
65
|
end
|