sidekiq-cron 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.travis.yml +18 -0
- data/Changes.md +8 -0
- data/Gemfile +30 -0
- data/LICENSE.txt +20 -0
- data/README.md +152 -0
- data/Rakefile +61 -0
- data/VERSION +1 -0
- data/config.ru +14 -0
- data/examples/web-cron-ui.png +0 -0
- data/lib/sidekiq-cron.rb +4 -0
- data/lib/sidekiq/cron.rb +29 -0
- data/lib/sidekiq/cron/job.rb +397 -0
- data/lib/sidekiq/cron/locales/en.yml +7 -0
- data/lib/sidekiq/cron/poller.rb +65 -0
- data/lib/sidekiq/cron/views/cron.slim +47 -0
- data/lib/sidekiq/cron/web_extension.rb +75 -0
- data/lib/sidekiq/launcher.rb +46 -0
- data/sidekiq-cron.gemspec +104 -0
- data/test/test_helper.rb +67 -0
- data/test/unit/job_test.rb +529 -0
- data/test/unit/poller_test.rb +157 -0
- data/test/unit/web_extesion_test.rb +92 -0
- metadata +313 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'sidekiq'
|
2
|
+
require 'sidekiq/util'
|
3
|
+
require 'sidekiq/actor'
|
4
|
+
require 'sidekiq/cron'
|
5
|
+
|
6
|
+
module Sidekiq
|
7
|
+
module Cron
|
8
|
+
|
9
|
+
POLL_INTERVAL = 10
|
10
|
+
|
11
|
+
##
|
12
|
+
# The Poller checks Redis every N seconds for sheduled cron jobs
|
13
|
+
class Poller
|
14
|
+
include Util
|
15
|
+
include Actor
|
16
|
+
|
17
|
+
def poll(first_time=false)
|
18
|
+
watchdog('scheduling cron poller thread died!') do
|
19
|
+
add_jitter if first_time
|
20
|
+
|
21
|
+
begin
|
22
|
+
time_now = Time.now
|
23
|
+
|
24
|
+
#go through all jobs
|
25
|
+
Sidekiq::Cron::Job.all.each do |job|
|
26
|
+
#test if job should be enequed
|
27
|
+
# if yes add job to queue
|
28
|
+
begin
|
29
|
+
job.test_and_enque_for_time! time_now if job && job.valid?
|
30
|
+
rescue => ex
|
31
|
+
#problem somewhere in one job
|
32
|
+
logger.error "CRON JOB: #{ex.message}"
|
33
|
+
logger.error "CRON JOB: #{ex.backtrace.first}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
rescue => ex
|
38
|
+
# Most likely a problem with redis networking.
|
39
|
+
# Punt and try again at the next interval
|
40
|
+
logger.error ex.message
|
41
|
+
logger.error ex.backtrace.first
|
42
|
+
end
|
43
|
+
|
44
|
+
after(poll_interval) { poll }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def poll_interval
|
51
|
+
Sidekiq.options[:poll_interval] || POLL_INTERVAL
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_jitter
|
55
|
+
begin
|
56
|
+
sleep(poll_interval * rand)
|
57
|
+
rescue Celluloid::Task::TerminatedError
|
58
|
+
# Hit Ctrl-C when Sidekiq is finished booting and we have a chance
|
59
|
+
# to get here.
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
header.row
|
2
|
+
.span5
|
3
|
+
h3 = t('CronJobs')
|
4
|
+
- if @cron_jobs.size > 0
|
5
|
+
|
6
|
+
table class="table table-hover table-bordered table-striped"
|
7
|
+
thead
|
8
|
+
th = t('Status')
|
9
|
+
th = t('Name')
|
10
|
+
th = t('Cron')
|
11
|
+
th = t('Last enque')
|
12
|
+
th = t('Arguments')
|
13
|
+
th width="253px"
|
14
|
+
= t('Actions')
|
15
|
+
|
16
|
+
|
17
|
+
- @cron_jobs.sort{|a,b| a.sort_name <=> b.sort_name }.each_with_index do |job, index|
|
18
|
+
- style = "#{job.status == 'disabled' ? "background: #ecc": ""}"
|
19
|
+
tr
|
20
|
+
td[style="#{style}"]= job.status
|
21
|
+
td[style="#{style}"]= job.name
|
22
|
+
td[style="#{style}"]
|
23
|
+
b == job.cron.gsub(" ", " ")
|
24
|
+
td[style="#{style}"]== relative_time(job.last_run_time)
|
25
|
+
td[style="#{style}"]
|
26
|
+
- if job.message and job.message.to_s.size > 100
|
27
|
+
button data-toggle="collapse" data-target=".worker_#{index}" class="btn btn-mini" = t('ShowAll')
|
28
|
+
.toggle[class="worker_#{index}" style="display: inline;"]= job.message[0..100] + "... "
|
29
|
+
.toggle[class="worker_#{index}" style="display: none;"]= job.message
|
30
|
+
- else
|
31
|
+
= job.message
|
32
|
+
td[style="#{style}"]
|
33
|
+
-if job.status == 'enabled'
|
34
|
+
form action="#{root_path}cron/#{job.name}/enque" method="post"
|
35
|
+
input.btn.btn-small.pull-left type="submit" name="enque" value="#{t('EnqueueNow')}"
|
36
|
+
form action="#{root_path}cron/#{job.name}/disable" method="post"
|
37
|
+
input.btn.btn-small.pull-left type="submit" name="disable" value="#{t('Disable')}"
|
38
|
+
-else
|
39
|
+
form action="#{root_path}cron/#{job.name}/enque" method="post"
|
40
|
+
input.btn.btn-small.pull-left type="submit" name="enque" value="#{t('EnqueueNow')}"
|
41
|
+
form action="#{root_path}cron/#{job.name}/enable" method="post"
|
42
|
+
input.btn.btn-small.pull-left type="submit" name="enable" value="#{t('Enable')}"
|
43
|
+
form action="#{root_path}cron/#{job.name}/delete" method="post"
|
44
|
+
input.btn.btn-danger.btn-small type="submit" name="delete" value="#{t('Delete')}" data-confirm="#{t('AreYouSureDeleteCronJob', :job => job.name)}"
|
45
|
+
|
46
|
+
- else
|
47
|
+
.alert.alert-success = t('NoCronJobsFound')
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Sidekiq
|
2
|
+
module Cron
|
3
|
+
module WebExtension
|
4
|
+
|
5
|
+
def self.registered(app)
|
6
|
+
|
7
|
+
#very bad way of loading locales for cron jobs
|
8
|
+
#should be rewritten
|
9
|
+
app.helpers do
|
10
|
+
|
11
|
+
alias_method :old_strings, :strings
|
12
|
+
|
13
|
+
def strings
|
14
|
+
#only on first load!
|
15
|
+
unless @strings
|
16
|
+
#load all locales from Sidekiq
|
17
|
+
old_strings
|
18
|
+
|
19
|
+
Dir["#{File.join(File.expand_path("..", __FILE__), "locales")}/*.yml"].each do |file|
|
20
|
+
YAML.load(File.open(file)).each do |locale, translations|
|
21
|
+
translations.each do |key, value|
|
22
|
+
@strings[locale][key] = value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
@strings
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
#index page of cron jobs
|
32
|
+
app.get '/cron' do
|
33
|
+
view_path = File.join(File.expand_path("..", __FILE__), "views")
|
34
|
+
|
35
|
+
@cron_jobs = Sidekiq::Cron::Job.all
|
36
|
+
|
37
|
+
render(:slim, File.read(File.join(view_path, "cron.slim")))
|
38
|
+
end
|
39
|
+
|
40
|
+
#enque cron job
|
41
|
+
app.post '/cron/:name/enque' do |name|
|
42
|
+
if job = Sidekiq::Cron::Job.find(name)
|
43
|
+
job.enque!
|
44
|
+
end
|
45
|
+
redirect "#{root_path}cron"
|
46
|
+
end
|
47
|
+
|
48
|
+
#delete schedule
|
49
|
+
app.post '/cron/:name/delete' do |name|
|
50
|
+
if job = Sidekiq::Cron::Job.find(name)
|
51
|
+
job.destroy
|
52
|
+
end
|
53
|
+
redirect "#{root_path}cron"
|
54
|
+
end
|
55
|
+
|
56
|
+
#enable job
|
57
|
+
app.post '/cron/:name/enable' do |name|
|
58
|
+
if job = Sidekiq::Cron::Job.find(name)
|
59
|
+
job.enable!
|
60
|
+
end
|
61
|
+
redirect "#{root_path}cron"
|
62
|
+
end
|
63
|
+
|
64
|
+
#disable job
|
65
|
+
app.post '/cron/:name/disable' do |name|
|
66
|
+
if job = Sidekiq::Cron::Job.find(name)
|
67
|
+
job.disable!
|
68
|
+
end
|
69
|
+
redirect "#{root_path}cron"
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'sidekiq/cron/poller'
|
2
|
+
|
3
|
+
|
4
|
+
# For Cron we need to add some methods to Launcher
|
5
|
+
# so look at the code bellow.
|
6
|
+
#
|
7
|
+
# we are creating new cron poller instance and
|
8
|
+
# adding start and stop commands to launcher
|
9
|
+
module Sidekiq
|
10
|
+
class Launcher
|
11
|
+
|
12
|
+
#Add cron poller to launcher
|
13
|
+
attr_reader :cron_poller
|
14
|
+
|
15
|
+
|
16
|
+
#remember old initialize
|
17
|
+
alias_method :old_initialize, :initialize
|
18
|
+
|
19
|
+
#add cron poller and execute normal initialize of Sidekiq launcher
|
20
|
+
def initialize(options)
|
21
|
+
@cron_poller = Sidekiq::Cron::Poller.new
|
22
|
+
old_initialize options
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
#remember old run
|
27
|
+
alias_method :old_run, :run
|
28
|
+
|
29
|
+
#execute normal run of launcher and run cron poller
|
30
|
+
def run
|
31
|
+
old_run
|
32
|
+
cron_poller.async.poll(true)
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
#remember old stop
|
37
|
+
alias_method :old_stop, :stop
|
38
|
+
|
39
|
+
#execute normal stop of launcher and stop cron poller
|
40
|
+
def stop
|
41
|
+
cron_poller.async.terminate if poller.alive?
|
42
|
+
old_stop
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "sidekiq-cron"
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Ondrej Bartas"]
|
12
|
+
s.date = "2013-08-25"
|
13
|
+
s.description = "Enables to set jobs to be run in specified time (using CRON notation)"
|
14
|
+
s.email = "ondrej@bartas.cz"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".travis.yml",
|
22
|
+
"Changes.md",
|
23
|
+
"Gemfile",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.md",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"config.ru",
|
29
|
+
"examples/web-cron-ui.png",
|
30
|
+
"lib/sidekiq-cron.rb",
|
31
|
+
"lib/sidekiq/cron.rb",
|
32
|
+
"lib/sidekiq/cron/job.rb",
|
33
|
+
"lib/sidekiq/cron/locales/en.yml",
|
34
|
+
"lib/sidekiq/cron/poller.rb",
|
35
|
+
"lib/sidekiq/cron/views/cron.slim",
|
36
|
+
"lib/sidekiq/cron/web_extension.rb",
|
37
|
+
"lib/sidekiq/launcher.rb",
|
38
|
+
"sidekiq-cron.gemspec",
|
39
|
+
"test/test_helper.rb",
|
40
|
+
"test/unit/job_test.rb",
|
41
|
+
"test/unit/poller_test.rb",
|
42
|
+
"test/unit/web_extesion_test.rb"
|
43
|
+
]
|
44
|
+
s.homepage = "http://github.com/ondrejbartas/sidekiq-cron"
|
45
|
+
s.licenses = ["MIT"]
|
46
|
+
s.require_paths = ["lib"]
|
47
|
+
s.rubygems_version = "1.8.25"
|
48
|
+
s.summary = "Sidekiq Cron helps to add repeated scheduled jobs"
|
49
|
+
|
50
|
+
if s.respond_to? :specification_version then
|
51
|
+
s.specification_version = 3
|
52
|
+
|
53
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
54
|
+
s.add_runtime_dependency(%q<sidekiq>, [">= 2.13.1"])
|
55
|
+
s.add_runtime_dependency(%q<parse-cron>, [">= 0.1.2"])
|
56
|
+
s.add_development_dependency(%q<bundler>, [">= 0"])
|
57
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
58
|
+
s.add_development_dependency(%q<shoulda-context>, [">= 0"])
|
59
|
+
s.add_development_dependency(%q<turn>, [">= 0"])
|
60
|
+
s.add_development_dependency(%q<rack>, [">= 0"])
|
61
|
+
s.add_development_dependency(%q<rack-test>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
|
63
|
+
s.add_development_dependency(%q<sdoc>, [">= 0"])
|
64
|
+
s.add_development_dependency(%q<slim>, [">= 0"])
|
65
|
+
s.add_development_dependency(%q<sinatra>, [">= 0"])
|
66
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
67
|
+
s.add_development_dependency(%q<coveralls>, [">= 0"])
|
68
|
+
s.add_development_dependency(%q<shotgun>, [">= 0"])
|
69
|
+
else
|
70
|
+
s.add_dependency(%q<sidekiq>, [">= 2.13.1"])
|
71
|
+
s.add_dependency(%q<parse-cron>, [">= 0.1.2"])
|
72
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
73
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
74
|
+
s.add_dependency(%q<shoulda-context>, [">= 0"])
|
75
|
+
s.add_dependency(%q<turn>, [">= 0"])
|
76
|
+
s.add_dependency(%q<rack>, [">= 0"])
|
77
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
78
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
79
|
+
s.add_dependency(%q<sdoc>, [">= 0"])
|
80
|
+
s.add_dependency(%q<slim>, [">= 0"])
|
81
|
+
s.add_dependency(%q<sinatra>, [">= 0"])
|
82
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
83
|
+
s.add_dependency(%q<coveralls>, [">= 0"])
|
84
|
+
s.add_dependency(%q<shotgun>, [">= 0"])
|
85
|
+
end
|
86
|
+
else
|
87
|
+
s.add_dependency(%q<sidekiq>, [">= 2.13.1"])
|
88
|
+
s.add_dependency(%q<parse-cron>, [">= 0.1.2"])
|
89
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
90
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
91
|
+
s.add_dependency(%q<shoulda-context>, [">= 0"])
|
92
|
+
s.add_dependency(%q<turn>, [">= 0"])
|
93
|
+
s.add_dependency(%q<rack>, [">= 0"])
|
94
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
95
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
96
|
+
s.add_dependency(%q<sdoc>, [">= 0"])
|
97
|
+
s.add_dependency(%q<slim>, [">= 0"])
|
98
|
+
s.add_dependency(%q<sinatra>, [">= 0"])
|
99
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
100
|
+
s.add_dependency(%q<coveralls>, [">= 0"])
|
101
|
+
s.add_dependency(%q<shotgun>, [">= 0"])
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'simplecov'
|
12
|
+
SimpleCov.start do
|
13
|
+
add_filter "/test/"
|
14
|
+
|
15
|
+
add_group 'SidekiqCron', 'lib/'
|
16
|
+
end
|
17
|
+
require 'coveralls'
|
18
|
+
Coveralls.wear!
|
19
|
+
|
20
|
+
require "minitest/autorun"
|
21
|
+
require 'shoulda-context'
|
22
|
+
require 'turn'
|
23
|
+
require "rack/test"
|
24
|
+
require "mocha/setup"
|
25
|
+
|
26
|
+
#SIDEKIQ Require - need to have sidekiq running!
|
27
|
+
require 'celluloid/autostart'
|
28
|
+
require 'sidekiq'
|
29
|
+
require 'sidekiq/util'
|
30
|
+
require 'sidekiq/web'
|
31
|
+
|
32
|
+
Sidekiq.logger.level = Logger::ERROR
|
33
|
+
|
34
|
+
require 'sidekiq/redis_connection'
|
35
|
+
redis_url = ENV['REDIS_URL'] || 'redis://localhost/15'
|
36
|
+
REDIS = Sidekiq::RedisConnection.create(:url => redis_url, :namespace => 'testy')
|
37
|
+
|
38
|
+
Sidekiq.configure_client do |config|
|
39
|
+
config.redis = { :url => redis_url, :namespace => 'testy' }
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
44
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
45
|
+
require 'sidekiq-cron'
|
46
|
+
|
47
|
+
class Test::Unit::TestCase
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
class CronTestClass
|
52
|
+
include Sidekiq::Worker
|
53
|
+
|
54
|
+
def perform args = {}
|
55
|
+
puts "super croned job #{args}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class CronTestClassWithQueue
|
60
|
+
include Sidekiq::Worker
|
61
|
+
sidekiq_options :queue => :super, :retry => false, :backtrace => true
|
62
|
+
|
63
|
+
def perform args = {}
|
64
|
+
puts "super croned job #{args}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -0,0 +1,529 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require './test/test_helper'
|
3
|
+
|
4
|
+
class CronJobTest < Test::Unit::TestCase
|
5
|
+
context "Cron Job" do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
#clear all previous saved data from redis
|
9
|
+
Sidekiq.redis do |conn|
|
10
|
+
conn.keys("cron_job*").each do |key|
|
11
|
+
conn.del(key)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
#clear all queues
|
16
|
+
Sidekiq::Queue.all.each do |queue|
|
17
|
+
queue.clear
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
should "be initialized" do
|
22
|
+
job = Sidekiq::Cron::Job.new()
|
23
|
+
assert job.is_a?(Sidekiq::Cron::Job)
|
24
|
+
end
|
25
|
+
|
26
|
+
context "class methods" do
|
27
|
+
should "have create method" do
|
28
|
+
assert Sidekiq::Cron::Job.respond_to?(:create)
|
29
|
+
end
|
30
|
+
|
31
|
+
should "have destroy method" do
|
32
|
+
assert Sidekiq::Cron::Job.respond_to?(:destroy)
|
33
|
+
end
|
34
|
+
|
35
|
+
should "have count" do
|
36
|
+
assert Sidekiq::Cron::Job.respond_to?(:count)
|
37
|
+
end
|
38
|
+
|
39
|
+
should "have all" do
|
40
|
+
assert Sidekiq::Cron::Job.respond_to?(:all)
|
41
|
+
end
|
42
|
+
|
43
|
+
should "have find" do
|
44
|
+
assert Sidekiq::Cron::Job.respond_to?(:find)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "instance methods" do
|
49
|
+
setup do
|
50
|
+
@job = Sidekiq::Cron::Job.new()
|
51
|
+
end
|
52
|
+
|
53
|
+
should "have save method" do
|
54
|
+
assert @job.respond_to?(:save)
|
55
|
+
end
|
56
|
+
should "have valid? method" do
|
57
|
+
assert @job.respond_to?("valid?".to_sym)
|
58
|
+
end
|
59
|
+
should "have destroy method" do
|
60
|
+
assert @job.respond_to?(:destroy)
|
61
|
+
end
|
62
|
+
|
63
|
+
should 'have sort_name - used for sorting enabled disbaled jobs on frontend' do
|
64
|
+
job = Sidekiq::Cron::Job.new(name: "TestName")
|
65
|
+
assert_equal job.sort_name, "0_testname"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "invalid job" do
|
70
|
+
|
71
|
+
setup do
|
72
|
+
@job = Sidekiq::Cron::Job.new()
|
73
|
+
end
|
74
|
+
|
75
|
+
should "return false on valid? and errors" do
|
76
|
+
refute @job.valid?
|
77
|
+
assert @job.errors.is_a?(Array)
|
78
|
+
|
79
|
+
assert @job.errors.any?{|e| e.include?("name")}, "Should have error for name"
|
80
|
+
assert @job.errors.any?{|e| e.include?("cron")}, "Should have error for cron"
|
81
|
+
assert @job.errors.any?{|e| e.include?("klass")}, "Should have error for klass"
|
82
|
+
end
|
83
|
+
|
84
|
+
should "return false on valid? with invalid cron" do
|
85
|
+
@job.cron = "* s *"
|
86
|
+
refute @job.valid?
|
87
|
+
assert @job.errors.is_a?(Array)
|
88
|
+
assert @job.errors.any?{|e| e.include?("cron")}, "Should have error for cron"
|
89
|
+
end
|
90
|
+
|
91
|
+
should "return false on save" do
|
92
|
+
refute @job.save
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "new" do
|
97
|
+
setup do
|
98
|
+
@args = {
|
99
|
+
name: "Test",
|
100
|
+
cron: "* * * * *"
|
101
|
+
}
|
102
|
+
@job = Sidekiq::Cron::Job.new(@args)
|
103
|
+
end
|
104
|
+
|
105
|
+
should "have all setted attributes" do
|
106
|
+
@args.each do |key, value|
|
107
|
+
assert_equal @job.send(key), value, "New job should have #{key} with value #{value} but it has: #{@job.send(key)}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
should "have to_hash method" do
|
112
|
+
[:name,:klass,:cron,:args,:message,:status, :last_run_time].each do |key|
|
113
|
+
assert @job.to_hash.has_key?(key), "to_hash must have key: #{key}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "new with different class inputs" do
|
119
|
+
should "be initialized by 'klass' and Class" do
|
120
|
+
assert_nothing_raised do
|
121
|
+
Sidekiq::Cron::Job.new('klass' => CronTestClass)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
should "be initialized by 'klass' and string Class" do
|
126
|
+
assert_nothing_raised do
|
127
|
+
Sidekiq::Cron::Job.new('klass' => 'CronTestClass')
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
should "be initialized by 'class' and string Class" do
|
132
|
+
assert_nothing_raised do
|
133
|
+
Sidekiq::Cron::Job.new('class' => 'CronTestClass')
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
should "be initialized by 'class' and Class" do
|
138
|
+
assert_nothing_raised do
|
139
|
+
Sidekiq::Cron::Job.new('class' => CronTestClass)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "new should find klass specific settings (queue, retry ...)" do
|
145
|
+
should "nothing raise on unknown klass" do
|
146
|
+
assert_nothing_raised do
|
147
|
+
job = Sidekiq::Cron::Job.new('klass' => 'UnknownCronClass')
|
148
|
+
assert_equal job.message, {"class"=>"UnknownCronClass", "args"=>[], "queue"=>"default"}
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
should "be initialized with default attributes" do
|
153
|
+
assert_nothing_raised do
|
154
|
+
job = Sidekiq::Cron::Job.new('klass' => 'CronTestClass')
|
155
|
+
|
156
|
+
assert_equal job.message, {"retry"=>true, "queue"=>"default", "class"=>"CronTestClass", "args"=>[]}
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
should "be initialized with class specified attributes" do
|
161
|
+
assert_nothing_raised do
|
162
|
+
job = Sidekiq::Cron::Job.new('class' => 'CronTestClassWithQueue')
|
163
|
+
assert_equal job.message, {"retry"=>false,
|
164
|
+
"queue"=>:super,
|
165
|
+
"backtrace"=>true,
|
166
|
+
"class"=>"CronTestClassWithQueue",
|
167
|
+
"args"=>[]}
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
should "be initialized with 'class' and overwrite queue by settings" do
|
172
|
+
assert_nothing_raised do
|
173
|
+
job = Sidekiq::Cron::Job.new('class' => CronTestClassWithQueue, queue: 'my_testing_queue')
|
174
|
+
|
175
|
+
assert_equal job.message, {"retry"=>false,
|
176
|
+
"queue"=>'my_testing_queue',
|
177
|
+
"backtrace"=>true,
|
178
|
+
"class"=>"CronTestClassWithQueue",
|
179
|
+
"args"=>[]}
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context "cron test" do
|
185
|
+
setup do
|
186
|
+
@job = Sidekiq::Cron::Job.new()
|
187
|
+
end
|
188
|
+
|
189
|
+
should "return previous minute" do
|
190
|
+
@job.cron = "* * * * *"
|
191
|
+
time = Time.now
|
192
|
+
assert_equal @job.last_time(time).strftime("%Y-%m-%d-%H-%M-%S"), time.strftime("%Y-%m-%d-%H-%M-00")
|
193
|
+
end
|
194
|
+
|
195
|
+
should "return previous hour" do
|
196
|
+
@job.cron = "1 * * * *"
|
197
|
+
time = Time.now
|
198
|
+
assert_equal @job.last_time(time).strftime("%Y-%m-%d-%H-%M-%S"), time.strftime("%Y-%m-%d-%H-01-00")
|
199
|
+
end
|
200
|
+
|
201
|
+
should "return previous day" do
|
202
|
+
@job.cron = "1 2 * * *"
|
203
|
+
time = Time.now
|
204
|
+
assert_equal @job.last_time(time).strftime("%Y-%m-%d-%H-%M-%S"), time.strftime("%Y-%m-%d-02-01-00")
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
context "save" do
|
210
|
+
setup do
|
211
|
+
@args = {
|
212
|
+
name: "Test",
|
213
|
+
cron: "* * * * *",
|
214
|
+
klass: "CronTestClass"
|
215
|
+
}
|
216
|
+
@job = Sidekiq::Cron::Job.new(@args)
|
217
|
+
end
|
218
|
+
|
219
|
+
should "be saved" do
|
220
|
+
assert @job.save
|
221
|
+
end
|
222
|
+
|
223
|
+
|
224
|
+
should "be saved and found by name" do
|
225
|
+
assert @job.save, "not saved"
|
226
|
+
assert Sidekiq::Cron::Job.find("Test").is_a?(Sidekiq::Cron::Job)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
context "nonexisting job" do
|
231
|
+
should "not be found" do
|
232
|
+
assert Sidekiq::Cron::Job.find("nonexisting").nil?, "should return nil"
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
context "disabled/enabled" do
|
237
|
+
setup do
|
238
|
+
@args = {
|
239
|
+
name: "Test",
|
240
|
+
cron: "* * * * *",
|
241
|
+
klass: "CronTestClass"
|
242
|
+
}
|
243
|
+
end
|
244
|
+
|
245
|
+
should "be created and enabled" do
|
246
|
+
Sidekiq::Cron::Job.create(@args)
|
247
|
+
job = Sidekiq::Cron::Job.find(@args)
|
248
|
+
assert_equal job.status, "enabled"
|
249
|
+
end
|
250
|
+
|
251
|
+
should "be created and then enabled and disabled" do
|
252
|
+
Sidekiq::Cron::Job.create(@args)
|
253
|
+
job = Sidekiq::Cron::Job.find(@args)
|
254
|
+
assert_equal job.status, "enabled"
|
255
|
+
|
256
|
+
job.enable!
|
257
|
+
assert_equal job.status, "enabled"
|
258
|
+
|
259
|
+
job.disable!
|
260
|
+
assert_equal job.status, "disabled"
|
261
|
+
end
|
262
|
+
|
263
|
+
should "be created with status disabled" do
|
264
|
+
Sidekiq::Cron::Job.create(@args.merge(status: "disabled"))
|
265
|
+
job = Sidekiq::Cron::Job.find(@args)
|
266
|
+
assert_equal job.status, "disabled"
|
267
|
+
end
|
268
|
+
|
269
|
+
should "be created with status enabled and disable it afterwards" do
|
270
|
+
Sidekiq::Cron::Job.create(@args)
|
271
|
+
job = Sidekiq::Cron::Job.find(@args)
|
272
|
+
assert_equal job.status, "enabled"
|
273
|
+
job.disable!
|
274
|
+
assert_equal job.status, "disabled", "directly after call"
|
275
|
+
job = Sidekiq::Cron::Job.find(@args)
|
276
|
+
assert_equal job.status, "disabled", "after find"
|
277
|
+
end
|
278
|
+
|
279
|
+
should "status shouldn't be rewritten after save without status" do
|
280
|
+
Sidekiq::Cron::Job.create(@args)
|
281
|
+
job = Sidekiq::Cron::Job.find(@args)
|
282
|
+
assert_equal job.status, "enabled"
|
283
|
+
job.disable!
|
284
|
+
assert_equal job.status, "disabled", "directly after call"
|
285
|
+
job = Sidekiq::Cron::Job.find(@args)
|
286
|
+
assert_equal job.status, "disabled", "after find"
|
287
|
+
|
288
|
+
Sidekiq::Cron::Job.create(@args)
|
289
|
+
assert_equal job.status, "disabled", "after second create"
|
290
|
+
job = Sidekiq::Cron::Job.find(@args)
|
291
|
+
assert_equal job.status, "disabled", "after second find"
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
context "create & find methods" do
|
296
|
+
setup do
|
297
|
+
@args = {
|
298
|
+
name: "Test",
|
299
|
+
cron: "* * * * *",
|
300
|
+
klass: "CronTestClass"
|
301
|
+
}
|
302
|
+
end
|
303
|
+
|
304
|
+
should "create first three jobs" do
|
305
|
+
assert_equal Sidekiq::Cron::Job.count, 0, "Should have 0 jobs"
|
306
|
+
Sidekiq::Cron::Job.create(@args)
|
307
|
+
Sidekiq::Cron::Job.create(@args.merge(name: "Test2"))
|
308
|
+
Sidekiq::Cron::Job.create(@args.merge(name: "Test3"))
|
309
|
+
assert_equal Sidekiq::Cron::Job.count, 3, "Should have 3 jobs"
|
310
|
+
end
|
311
|
+
|
312
|
+
should "create first three jobs - 1 has same name" do
|
313
|
+
assert_equal Sidekiq::Cron::Job.count, 0, "Should have 0 jobs"
|
314
|
+
Sidekiq::Cron::Job.create(@args)
|
315
|
+
Sidekiq::Cron::Job.create(@args.merge(name: "Test2"))
|
316
|
+
Sidekiq::Cron::Job.create(@args.merge(cron: "1 * * * *"))
|
317
|
+
assert_equal Sidekiq::Cron::Job.count, 2, "Should have 2 jobs"
|
318
|
+
end
|
319
|
+
|
320
|
+
should "be found by method all" do
|
321
|
+
Sidekiq::Cron::Job.create(@args)
|
322
|
+
Sidekiq::Cron::Job.create(@args.merge(name: "Test2"))
|
323
|
+
Sidekiq::Cron::Job.create(@args.merge(name: "Test3"))
|
324
|
+
assert_equal Sidekiq::Cron::Job.all.size, 3, "Should have 3 jobs"
|
325
|
+
assert Sidekiq::Cron::Job.all.all?{|j| j.is_a?(Sidekiq::Cron::Job)}, "All returned jobs should be Job class"
|
326
|
+
end
|
327
|
+
|
328
|
+
should "be found by method all - defect in set" do
|
329
|
+
Sidekiq::Cron::Job.create(@args)
|
330
|
+
Sidekiq::Cron::Job.create(@args.merge(name: "Test2"))
|
331
|
+
Sidekiq::Cron::Job.create(@args.merge(name: "Test3"))
|
332
|
+
|
333
|
+
Sidekiq.redis do |conn|
|
334
|
+
conn.sadd Sidekiq::Cron::Job.jobs_key, "some_other_key"
|
335
|
+
end
|
336
|
+
|
337
|
+
assert_equal Sidekiq::Cron::Job.all.size, 3, "All have to return only valid 3 jobs"
|
338
|
+
end
|
339
|
+
|
340
|
+
should "be found by string name" do
|
341
|
+
Sidekiq::Cron::Job.create(@args)
|
342
|
+
assert Sidekiq::Cron::Job.find("Test")
|
343
|
+
end
|
344
|
+
|
345
|
+
should "be found by hash with key name" do
|
346
|
+
Sidekiq::Cron::Job.create(@args)
|
347
|
+
assert Sidekiq::Cron::Job.find(name: "Test"), "symbol keys keys"
|
348
|
+
|
349
|
+
Sidekiq::Cron::Job.create(@args)
|
350
|
+
assert Sidekiq::Cron::Job.find('name' => "Test"), "String keys"
|
351
|
+
end
|
352
|
+
|
353
|
+
end
|
354
|
+
|
355
|
+
context "destroy" do
|
356
|
+
setup do
|
357
|
+
@args = {
|
358
|
+
name: "Test",
|
359
|
+
cron: "* * * * *",
|
360
|
+
klass: "CronTestClass"
|
361
|
+
}
|
362
|
+
end
|
363
|
+
|
364
|
+
should "create and then destroy by hash" do
|
365
|
+
Sidekiq::Cron::Job.create(@args)
|
366
|
+
assert_equal Sidekiq::Cron::Job.all.size, 1, "Should have 1 job"
|
367
|
+
|
368
|
+
assert Sidekiq::Cron::Job.destroy(@args)
|
369
|
+
assert_equal Sidekiq::Cron::Job.all.size, 0, "Should have 0 job after destroy"
|
370
|
+
end
|
371
|
+
|
372
|
+
should "return false on destroying nonexisting" do
|
373
|
+
assert_equal Sidekiq::Cron::Job.all.size, 0, "Should have 0 jobs"
|
374
|
+
refute Sidekiq::Cron::Job.destroy("nonexisting")
|
375
|
+
end
|
376
|
+
|
377
|
+
should "return destroy by string name" do
|
378
|
+
Sidekiq::Cron::Job.create(@args)
|
379
|
+
assert Sidekiq::Cron::Job.destroy("Test")
|
380
|
+
end
|
381
|
+
|
382
|
+
should "return destroy by hash with key name" do
|
383
|
+
Sidekiq::Cron::Job.create(@args)
|
384
|
+
assert Sidekiq::Cron::Job.destroy(name: "Test"), "symbol keys keys"
|
385
|
+
|
386
|
+
Sidekiq::Cron::Job.create(@args)
|
387
|
+
assert Sidekiq::Cron::Job.destroy('name' => "Test"), "String keys"
|
388
|
+
end
|
389
|
+
|
390
|
+
end
|
391
|
+
|
392
|
+
context "test of enque" do
|
393
|
+
setup do
|
394
|
+
@args = {
|
395
|
+
name: "Test",
|
396
|
+
cron: "* * * * *",
|
397
|
+
klass: "CronTestClass"
|
398
|
+
}
|
399
|
+
#first time is allways
|
400
|
+
#after next cron time!
|
401
|
+
@time = Time.now + 120
|
402
|
+
end
|
403
|
+
should "be allways false when status is disabled" do
|
404
|
+
refute Sidekiq::Cron::Job.new(@args.merge(status: 'disabled')).should_enque? @time
|
405
|
+
refute Sidekiq::Cron::Job.new(@args.merge(status: 'disabled')).should_enque? @time - 60
|
406
|
+
refute Sidekiq::Cron::Job.new(@args.merge(status: 'disabled')).should_enque? @time - 120
|
407
|
+
assert_equal Sidekiq::Queue.all.size, 0, "Sidekiq 0 queues"
|
408
|
+
end
|
409
|
+
|
410
|
+
should "be false for same times" do
|
411
|
+
assert Sidekiq::Cron::Job.new(@args).should_enque?(@time), "First time - true"
|
412
|
+
refute Sidekiq::Cron::Job.new(@args).should_enque? @time
|
413
|
+
refute Sidekiq::Cron::Job.new(@args).should_enque? @time
|
414
|
+
end
|
415
|
+
|
416
|
+
should "be false for same times but true for next time" do
|
417
|
+
assert Sidekiq::Cron::Job.new(@args).should_enque?(@time), "First time - true"
|
418
|
+
refute Sidekiq::Cron::Job.new(@args).should_enque? @time
|
419
|
+
assert Sidekiq::Cron::Job.new(@args).should_enque? @time + 135
|
420
|
+
refute Sidekiq::Cron::Job.new(@args).should_enque? @time + 135
|
421
|
+
assert Sidekiq::Cron::Job.new(@args).should_enque? @time + 235
|
422
|
+
refute Sidekiq::Cron::Job.new(@args).should_enque? @time + 235
|
423
|
+
|
424
|
+
#just for check
|
425
|
+
refute Sidekiq::Cron::Job.new(@args).should_enque? @time
|
426
|
+
refute Sidekiq::Cron::Job.new(@args).should_enque? @time + 135
|
427
|
+
refute Sidekiq::Cron::Job.new(@args).should_enque? @time + 235
|
428
|
+
end
|
429
|
+
|
430
|
+
should "remove old enque times + should be enqeued" do
|
431
|
+
assert Sidekiq::Cron::Job.new(@args).test_and_enque_for_time!(@time), "should enqueue"
|
432
|
+
refute Sidekiq::Cron::Job.new(@args).test_and_enque_for_time!(@time), "should not enqueue"
|
433
|
+
Sidekiq.redis do |conn|
|
434
|
+
assert_equal conn.zcard(Sidekiq::Cron::Job.new(@args).send(:job_enqueued_key)), 2, "Should have two enqueued job (first was in save, second in enque)"
|
435
|
+
end
|
436
|
+
assert_equal Sidekiq::Queue.all.first.size, 1, "Sidekiq queue 1 job in queue"
|
437
|
+
|
438
|
+
# 20 hours after
|
439
|
+
assert Sidekiq::Cron::Job.new(@args).test_and_enque_for_time! @time + 1 * 60 * 60
|
440
|
+
refute Sidekiq::Cron::Job.new(@args).test_and_enque_for_time! @time + 1 * 60 * 60
|
441
|
+
|
442
|
+
Sidekiq.redis do |conn|
|
443
|
+
assert_equal conn.zcard(Sidekiq::Cron::Job.new(@args).send(:job_enqueued_key)), 3, "Should have two enqueued job + one from start"
|
444
|
+
end
|
445
|
+
assert_equal Sidekiq::Queue.all.first.size, 2, "Sidekiq queue 2 jobs in queue"
|
446
|
+
|
447
|
+
# 26 hour after
|
448
|
+
assert Sidekiq::Cron::Job.new(@args).test_and_enque_for_time! @time + 26 * 60 * 60
|
449
|
+
refute Sidekiq::Cron::Job.new(@args).test_and_enque_for_time! @time + 26 * 60 * 60
|
450
|
+
|
451
|
+
Sidekiq.redis do |conn|
|
452
|
+
assert_equal conn.zcard(Sidekiq::Cron::Job.new(@args).send(:job_enqueued_key)), 1, "Should have one enqueued job - old jobs should be deleted"
|
453
|
+
end
|
454
|
+
assert_equal Sidekiq::Queue.all.first.size, 3, "Sidekiq queue 3 jobs in queue"
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
context "load" do
|
459
|
+
|
460
|
+
context "from hash" do
|
461
|
+
setup do
|
462
|
+
@jobs_hash = {
|
463
|
+
'name_of_job' => {
|
464
|
+
'class' => 'MyClass',
|
465
|
+
'cron' => '1 * * * *',
|
466
|
+
'args' => '(OPTIONAL) [Array or Hash]'
|
467
|
+
},
|
468
|
+
'My super iber cool job' => {
|
469
|
+
'class' => 'SecondClass',
|
470
|
+
'cron' => '*/5 * * * *'
|
471
|
+
}
|
472
|
+
}
|
473
|
+
end
|
474
|
+
|
475
|
+
should "create new jobs and update old one with same settings" do
|
476
|
+
assert_equal Sidekiq::Cron::Job.all.size, 0, "Should have 0 jobs before load"
|
477
|
+
out = Sidekiq::Cron::Job.load_from_hash @jobs_hash
|
478
|
+
assert_equal out.size, 0, "should have no errors"
|
479
|
+
assert_equal Sidekiq::Cron::Job.all.size, 2, "Should have 2 jobs after load"
|
480
|
+
end
|
481
|
+
|
482
|
+
should "return errors on loaded jobs" do
|
483
|
+
assert_equal Sidekiq::Cron::Job.all.size, 0, "Should have 0 jobs before load"
|
484
|
+
#set something bag to hash
|
485
|
+
@jobs_hash['name_of_job']['cron'] = "bad cron"
|
486
|
+
out = Sidekiq::Cron::Job.load_from_hash @jobs_hash
|
487
|
+
assert_equal out.size, 1, "should have 1 error"
|
488
|
+
assert_equal out, {"name_of_job"=>["'cron' -> bad cron: Bad Vixie-style specification bad"]}
|
489
|
+
assert_equal Sidekiq::Cron::Job.all.size, 1, "Should have only 1 job after load"
|
490
|
+
end
|
491
|
+
|
492
|
+
should "create new jobs and then destroy them all" do
|
493
|
+
assert_equal Sidekiq::Cron::Job.all.size, 0, "Should have 0 jobs before load"
|
494
|
+
out = Sidekiq::Cron::Job.load_from_hash @jobs_hash
|
495
|
+
assert_equal out.size, 0, "should have no errors"
|
496
|
+
assert_equal Sidekiq::Cron::Job.all.size, 2, "Should have 2 jobs after load"
|
497
|
+
Sidekiq::Cron::Job.destroy_all!
|
498
|
+
assert_equal Sidekiq::Cron::Job.all.size, 0, "Should have 0 jobs after destroy all"
|
499
|
+
end
|
500
|
+
|
501
|
+
end
|
502
|
+
|
503
|
+
context "from array" do
|
504
|
+
setup do
|
505
|
+
@jobs_array = [
|
506
|
+
{
|
507
|
+
'name' => 'name_of_job',
|
508
|
+
'class' => 'MyClass',
|
509
|
+
'cron' => '1 * * * *',
|
510
|
+
'args' => '(OPTIONAL) [Array or Hash]'
|
511
|
+
},
|
512
|
+
{
|
513
|
+
'name' => 'Cool Job for Second Class',
|
514
|
+
'class' => 'SecondClass',
|
515
|
+
'cron' => '*/5 * * * *'
|
516
|
+
}
|
517
|
+
]
|
518
|
+
end
|
519
|
+
|
520
|
+
should "create new jobs and update old one with same settings" do
|
521
|
+
assert_equal Sidekiq::Cron::Job.all.size, 0, "Should have 0 jobs before load"
|
522
|
+
out = Sidekiq::Cron::Job.load_from_array @jobs_array
|
523
|
+
assert_equal out.size, 0, "should have 0 error"
|
524
|
+
assert_equal Sidekiq::Cron::Job.all.size, 2, "Should have 2 jobs after load"
|
525
|
+
end
|
526
|
+
end
|
527
|
+
end
|
528
|
+
end
|
529
|
+
end
|