qu-scheduler 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/MIT-LICENSE +2 -1
  2. data/README.md +191 -0
  3. data/lib/qu-scheduler.rb +1 -4
  4. data/lib/qu-scheduler/tasks.rb +35 -0
  5. data/lib/qu/extensions/scheduler.rb +102 -0
  6. data/lib/qu/extensions/scheduler/redis.rb +199 -0
  7. data/lib/qu/scheduler.rb +227 -0
  8. data/lib/qu/scheduler/version.rb +2 -2
  9. data/test/delayed_queue_test.rb +277 -0
  10. data/test/redis-test.conf +115 -0
  11. data/test/scheduler_args_test.rb +156 -0
  12. data/test/scheduler_hooks_test.rb +51 -0
  13. data/test/scheduler_test.rb +181 -0
  14. data/test/test_helper.rb +91 -7
  15. metadata +64 -76
  16. data/README.rdoc +0 -7
  17. data/lib/tasks/qu-scheduler_tasks.rake +0 -4
  18. data/test/dummy/Rakefile +0 -7
  19. data/test/dummy/app/assets/javascripts/application.js +0 -9
  20. data/test/dummy/app/assets/stylesheets/application.css +0 -7
  21. data/test/dummy/app/controllers/application_controller.rb +0 -3
  22. data/test/dummy/app/helpers/application_helper.rb +0 -2
  23. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  24. data/test/dummy/config.ru +0 -4
  25. data/test/dummy/config/application.rb +0 -45
  26. data/test/dummy/config/boot.rb +0 -10
  27. data/test/dummy/config/database.yml +0 -25
  28. data/test/dummy/config/environment.rb +0 -5
  29. data/test/dummy/config/environments/development.rb +0 -30
  30. data/test/dummy/config/environments/production.rb +0 -60
  31. data/test/dummy/config/environments/test.rb +0 -39
  32. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  33. data/test/dummy/config/initializers/inflections.rb +0 -10
  34. data/test/dummy/config/initializers/mime_types.rb +0 -5
  35. data/test/dummy/config/initializers/secret_token.rb +0 -7
  36. data/test/dummy/config/initializers/session_store.rb +0 -8
  37. data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
  38. data/test/dummy/config/locales/en.yml +0 -5
  39. data/test/dummy/config/routes.rb +0 -58
  40. data/test/dummy/db/test.sqlite3 +0 -0
  41. data/test/dummy/log/test.log +0 -0
  42. data/test/dummy/public/404.html +0 -26
  43. data/test/dummy/public/422.html +0 -26
  44. data/test/dummy/public/500.html +0 -26
  45. data/test/dummy/public/favicon.ico +0 -0
  46. data/test/dummy/script/rails +0 -6
@@ -0,0 +1,115 @@
1
+ # Redis configuration file example
2
+
3
+ # By default Redis does not run as a daemon. Use 'yes' if you need it.
4
+ # Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
5
+ daemonize yes
6
+
7
+ # When run as a daemon, Redis write a pid file in /var/run/redis.pid by default.
8
+ # You can specify a custom pid file location here.
9
+ pidfile ./test/redis-test.pid
10
+
11
+ # Accept connections on the specified port, default is 6379
12
+ port 9736
13
+
14
+ # If you want you can bind a single interface, if the bind option is not
15
+ # specified all the interfaces will listen for connections.
16
+ #
17
+ # bind 127.0.0.1
18
+
19
+ # Close the connection after a client is idle for N seconds (0 to disable)
20
+ timeout 300
21
+
22
+ # Save the DB on disk:
23
+ #
24
+ # save <seconds> <changes>
25
+ #
26
+ # Will save the DB if both the given number of seconds and the given
27
+ # number of write operations against the DB occurred.
28
+ #
29
+ # In the example below the behaviour will be to save:
30
+ # after 900 sec (15 min) if at least 1 key changed
31
+ # after 300 sec (5 min) if at least 10 keys changed
32
+ # after 60 sec if at least 10000 keys changed
33
+ save 900 1
34
+ save 300 10
35
+ save 60 10000
36
+
37
+ # The filename where to dump the DB
38
+ dbfilename dump.rdb
39
+
40
+ # For default save/load DB in/from the working directory
41
+ # Note that you must specify a directory not a file name.
42
+ dir ./test/
43
+
44
+ # Set server verbosity to 'debug'
45
+ # it can be one of:
46
+ # debug (a lot of information, useful for development/testing)
47
+ # notice (moderately verbose, what you want in production probably)
48
+ # warning (only very important / critical messages are logged)
49
+ loglevel debug
50
+
51
+ # Specify the log file name. Also 'stdout' can be used to force
52
+ # the demon to log on the standard output. Note that if you use standard
53
+ # output for logging but daemonize, logs will be sent to /dev/null
54
+ logfile stdout
55
+
56
+ # Set the number of databases. The default database is DB 0, you can select
57
+ # a different one on a per-connection basis using SELECT <dbid> where
58
+ # dbid is a number between 0 and 'databases'-1
59
+ databases 16
60
+
61
+ ################################# REPLICATION #################################
62
+
63
+ # Master-Slave replication. Use slaveof to make a Redis instance a copy of
64
+ # another Redis server. Note that the configuration is local to the slave
65
+ # so for example it is possible to configure the slave to save the DB with a
66
+ # different interval, or to listen to another port, and so on.
67
+
68
+ # slaveof <masterip> <masterport>
69
+
70
+ ################################## SECURITY ###################################
71
+
72
+ # Require clients to issue AUTH <PASSWORD> before processing any other
73
+ # commands. This might be useful in environments in which you do not trust
74
+ # others with access to the host running redis-server.
75
+ #
76
+ # This should stay commented out for backward compatibility and because most
77
+ # people do not need auth (e.g. they run their own servers).
78
+
79
+ # requirepass foobared
80
+
81
+ ################################### LIMITS ####################################
82
+
83
+ # Set the max number of connected clients at the same time. By default there
84
+ # is no limit, and it's up to the number of file descriptors the Redis process
85
+ # is able to open. The special value '0' means no limts.
86
+ # Once the limit is reached Redis will close all the new connections sending
87
+ # an error 'max number of clients reached'.
88
+
89
+ # maxclients 128
90
+
91
+ # Don't use more memory than the specified amount of bytes.
92
+ # When the memory limit is reached Redis will try to remove keys with an
93
+ # EXPIRE set. It will try to start freeing keys that are going to expire
94
+ # in little time and preserve keys with a longer time to live.
95
+ # Redis will also try to remove objects from free lists if possible.
96
+ #
97
+ # If all this fails, Redis will start to reply with errors to commands
98
+ # that will use more memory, like SET, LPUSH, and so on, and will continue
99
+ # to reply to most read-only commands like GET.
100
+ #
101
+ # WARNING: maxmemory can be a good idea mainly if you want to use Redis as a
102
+ # 'state' server or cache, not as a real DB. When Redis is used as a real
103
+ # database the memory usage will grow over the weeks, it will be obvious if
104
+ # it is going to use too much memory in the long run, and you'll have the time
105
+ # to upgrade. With maxmemory after the limit is reached you'll start to get
106
+ # errors for write operations, and this may even lead to DB inconsistency.
107
+
108
+ # maxmemory <bytes>
109
+
110
+ ############################### ADVANCED CONFIG ###############################
111
+
112
+ # Glue small output buffers together in order to send small replies in a
113
+ # single TCP packet. Uses a bit more CPU but most of the times it is a win
114
+ # in terms of number of queries per second. Use 'yes' if unsure.
115
+ glueoutputbuf yes
@@ -0,0 +1,156 @@
1
+ require 'test_helper'
2
+
3
+ context "scheduling jobs with arguments" do
4
+ def run_job_for_queue(*queues)
5
+ worker = Qu::Worker.new(*queues)
6
+ Qu.backend.register_worker(worker)
7
+ worker.work
8
+ Qu.backend.unregister_worker(worker)
9
+ end
10
+
11
+ setup do
12
+ Qu::Scheduler.clear_schedule!
13
+ Qu::Scheduler.dynamic = false
14
+ end
15
+
16
+ test "enqueue_from_config with_every_syntax" do
17
+ Qu.backend.stubs(:enqueue).once.returns(true).with(all_of(instance_of(Qu::Payload), responds_with(:klass, SomeIvarJob), responds_with(:args, '/tmp'), responds_with(:every, '1m')))
18
+ Qu::Scheduler.enqueue_from_config('every' => '1m', 'klass' => 'SomeIvarJob', 'args' => '/tmp', 'queue' => 'james_queue')
19
+ end
20
+
21
+ test "enqueue_from_config puts jobs in the resque queue" do
22
+ Qu.backend.stubs(:enqueue).once.returns(true).with(all_of(instance_of(Qu::Payload), responds_with(:klass, SomeIvarJob), responds_with(:args, '/tmp'), responds_with(:queue, 'ivar')))
23
+ Qu::Scheduler.enqueue_from_config('cron' => "* * * * *", 'klass' => 'SomeIvarJob', 'args' => "/tmp")
24
+ end
25
+
26
+ test "enqueue_from_config with custom_class_job in resque" do
27
+ FakeCustomJobClass.stubs(:scheduled).once.returns(true).with('ivar', 'FakeCustomJobClass', '/tmp')
28
+ Qu::Scheduler.enqueue_from_config('cron' => "* * * * *", 'klass' => 'SomeIvarJob', 'custom_job_class' => 'FakeCustomJobClass', 'args' => "/tmp")
29
+ end
30
+
31
+ test "enqueue_from_config puts stuff in resquewhen rails_env matches" do
32
+ # The job should be loaded : its rails_env config matches the RAILS_ENV variable:
33
+ ENV['RAILS_ENV'] = 'production'
34
+ assert_equal(0, Qu::Scheduler.rufus_scheduler.all_jobs.size)
35
+
36
+ Qu.schedule = {:some_ivar_job => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp", 'rails_env' => 'production'}}
37
+ Qu::Scheduler.load_schedule!
38
+ assert_equal(1, Qu::Scheduler.rufus_scheduler.all_jobs.size)
39
+
40
+ # we allow multiple rails_env definition, it should work also:
41
+ Qu.schedule = {:some_ivar_job => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp", 'rails_env' => 'staging, production'}}
42
+ Qu::Scheduler.load_schedule!
43
+ assert_equal(2, Qu::Scheduler.rufus_scheduler.all_jobs.size)
44
+ end
45
+
46
+ test "enqueue_from_config doesnt put stuff in resque when rails_env doesnt match" do
47
+ # RAILS_ENV is not set:
48
+ assert_equal(0, Qu::Scheduler.rufus_scheduler.all_jobs.size)
49
+ Qu.schedule = {:some_ivar_job => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp", 'rails_env' => 'staging'}}
50
+ Qu::Scheduler.load_schedule!
51
+ assert_equal(0, Qu::Scheduler.rufus_scheduler.all_jobs.size)
52
+
53
+ # SET RAILS_ENV to a common value:
54
+ ENV['RAILS_ENV'] = 'production'
55
+ Qu.schedule = {:some_ivar_job => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp", 'rails_env' => 'staging'}}
56
+ Qu::Scheduler.load_schedule!
57
+ assert_equal(0, Qu::Scheduler.rufus_scheduler.all_jobs.size)
58
+ end
59
+
60
+ test "enqueue_from_config when rails env arg is not set" do
61
+ # The job should be loaded, since a missing rails_env means ALL envs.
62
+ ENV['RAILS_ENV'] = 'production'
63
+ assert_equal(0, Qu::Scheduler.rufus_scheduler.all_jobs.size)
64
+ Qu.schedule = {:some_ivar_job => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"}}
65
+ Qu::Scheduler.load_schedule!
66
+ assert_equal(1, Qu::Scheduler.rufus_scheduler.all_jobs.size)
67
+ end
68
+
69
+ test "calls the worker without arguments when 'args' is missing from the config" do
70
+ Qu::Scheduler.enqueue_from_config(YAML.load(<<-YAML))
71
+ klass: SomeIvarJob
72
+ YAML
73
+ SomeIvarJob.expects(:perform).once.with()
74
+ run_job_for_queue('ivar')
75
+ end
76
+
77
+ test "calls the worker without arguments when 'args' is blank in the config" do
78
+ Qu::Scheduler.enqueue_from_config(YAML.load(<<-YAML))
79
+ klass: SomeIvarJob
80
+ args:
81
+ YAML
82
+ SomeIvarJob.expects(:perform).once.with()
83
+ run_job_for_queue('ivar')
84
+ end
85
+
86
+ test "calls the worker with a string when the config lists a string" do
87
+ Qu::Scheduler.enqueue_from_config(YAML.load(<<-YAML))
88
+ klass: SomeIvarJob
89
+ args: string
90
+ YAML
91
+ SomeIvarJob.expects(:perform).once.with('string')
92
+ run_job_for_queue('ivar')
93
+ end
94
+
95
+ test "calls the worker with a Fixnum when the config lists an integer" do
96
+ Qu::Scheduler.enqueue_from_config(YAML.load(<<-YAML))
97
+ klass: SomeIvarJob
98
+ args: 1
99
+ YAML
100
+ SomeIvarJob.expects(:perform).once.with(1)
101
+ run_job_for_queue('ivar')
102
+ end
103
+
104
+ test "calls the worker with multiple arguments when the config lists an array" do
105
+ Qu::Scheduler.enqueue_from_config(YAML.load(<<-YAML))
106
+ klass: SomeIvarJob
107
+ args:
108
+ - 1
109
+ - 2
110
+ YAML
111
+ SomeIvarJob.expects(:perform).once.with(1, 2)
112
+ run_job_for_queue('ivar')
113
+ end
114
+
115
+ test "calls the worker with an array when the config lists a nested array" do
116
+ Qu::Scheduler.enqueue_from_config(YAML.load(<<-YAML))
117
+ klass: SomeIvarJob
118
+ args:
119
+ - - 1
120
+ - 2
121
+ YAML
122
+ SomeIvarJob.expects(:perform).once.with([1, 2])
123
+ run_job_for_queue('ivar')
124
+ end
125
+
126
+ test "calls the worker with a hash when the config lists a hash" do
127
+ Qu::Scheduler.enqueue_from_config(YAML.load(<<-YAML))
128
+ klass: SomeIvarJob
129
+ args:
130
+ key: value
131
+ YAML
132
+ SomeIvarJob.expects(:perform).once.with(['key', 'value'])
133
+ run_job_for_queue('ivar')
134
+ end
135
+
136
+ test "calls the worker with a nested hash when the config lists a nested hash" do
137
+ Qu::Scheduler.enqueue_from_config(YAML.load(<<-YAML))
138
+ klass: SomeIvarJob
139
+ args:
140
+ first_key:
141
+ second_key: value
142
+ YAML
143
+ SomeIvarJob.expects(:perform).once.with(['first_key', {'second_key' => 'value'}])
144
+ run_job_for_queue('ivar')
145
+ end
146
+
147
+ test "poll_sleep_amount defaults to 5" do
148
+ assert_equal 5, Qu::Scheduler.poll_sleep_amount
149
+ end
150
+
151
+ test "poll_sleep_amount is settable" do
152
+ Qu::Scheduler.poll_sleep_amount = 1
153
+ assert_equal 1, Qu::Scheduler.poll_sleep_amount
154
+ end
155
+
156
+ end
@@ -0,0 +1,51 @@
1
+ require 'test_helper'
2
+
3
+ context "scheduling jobs with hooks" do
4
+ class JobThatCannotBeScheduledWithoutArguments
5
+ @queue = :job_that_cannot_be_scheduled_without_arguments
6
+ def self.perform(*x);end
7
+ def self.before_schedule_return_nil_if_arguments_not_supplied(*args)
8
+ counters[:before_schedule] += 1
9
+ return false if args.empty?
10
+ end
11
+
12
+ def self.after_schedule_do_something(*args)
13
+ counters[:after_schedule] += 1
14
+ end
15
+
16
+ class << self
17
+ def counters
18
+ @counters ||= Hash.new{|h,k| h[k]=0}
19
+ end
20
+ def clean
21
+ counters.clear
22
+ self
23
+ end
24
+ end
25
+ end
26
+
27
+ setup do
28
+ Qu::Scheduler.dynamic = false
29
+ Qu.backend.redis.del(:schedules)
30
+ Qu.backend.redis.del(:schedules_changed)
31
+ Qu::Scheduler.clear_schedule!
32
+ Qu::Scheduler.send(:class_variable_set, :@@scheduled_jobs, {})
33
+ end
34
+
35
+ test "before_schedule hook that does not return false should not block" do
36
+ enqueue_time = Time.now + 12
37
+ Qu.enqueue_at(enqueue_time, JobThatCannotBeScheduledWithoutArguments.clean, :foo)
38
+ assert_equal(1, Qu.backend.delayed_timestamp_size(enqueue_time.to_i), "delayed queue should have one entry now")
39
+ assert_equal(1, JobThatCannotBeScheduledWithoutArguments.counters[:before_schedule], 'before_schedule was not run')
40
+ assert_equal(1, JobThatCannotBeScheduledWithoutArguments.counters[:after_schedule], 'after_schedule was not run')
41
+ end
42
+
43
+ test "before_schedule hook that returns false should block" do
44
+ enqueue_time = Time.now + 60
45
+ assert_equal(0, JobThatCannotBeScheduledWithoutArguments.clean.counters[:before_schedule], 'before_schedule should be zero')
46
+ Qu.enqueue_at(enqueue_time, JobThatCannotBeScheduledWithoutArguments.clean)
47
+ assert_equal(0, Qu.backend.delayed_timestamp_size(enqueue_time.to_i), "job should not have been put in queue")
48
+ assert_equal(1, JobThatCannotBeScheduledWithoutArguments.counters[:before_schedule], 'before_schedule was not run')
49
+ assert_equal(0, JobThatCannotBeScheduledWithoutArguments.counters[:after_schedule], 'after_schedule was run')
50
+ end
51
+ end
@@ -0,0 +1,181 @@
1
+ require 'test_helper'
2
+
3
+ context "Qu::Scheduler" do
4
+
5
+ setup do
6
+ Qu::Scheduler.dynamic = false
7
+ Qu.backend.redis.del(:schedules)
8
+ Qu.backend.redis.del(:schedules_changed)
9
+ Qu::Scheduler.clear_schedule!
10
+ Qu::Scheduler.send(:class_variable_set, :@@scheduled_jobs, {})
11
+ end
12
+
13
+ test "enqueue constantizes" do
14
+ # The job should be loaded, since a missing rails_env means ALL envs.
15
+ ENV['RAILS_ENV'] = 'production'
16
+ config = {'cron' => "* * * * *", 'klass' => 'SomeRealClass', 'args' => "/tmp"}
17
+ Qu.backend.expects(:enqueue).once.with(all_of(instance_of(Qu::Payload), responds_with(:klass, SomeRealClass), responds_with(:args, '/tmp'), responds_with(:cron, '* * * * *')))
18
+ Qu::Scheduler.enqueue_from_config(config)
19
+ end
20
+
21
+ test "config makes it into the rufus_scheduler" do
22
+ assert_equal(0, Qu::Scheduler.rufus_scheduler.all_jobs.size)
23
+
24
+ Qu.schedule = {:some_ivar_job => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"}}
25
+ Qu::Scheduler.load_schedule!
26
+
27
+ assert_equal(1, Qu::Scheduler.rufus_scheduler.all_jobs.size)
28
+ assert Qu::Scheduler.scheduled_jobs.include?(:some_ivar_job)
29
+ end
30
+
31
+ test "can reload schedule" do
32
+ Qu::Scheduler.dynamic = true
33
+ Qu.schedule = {"some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"}}
34
+
35
+ Qu::Scheduler.load_schedule!
36
+
37
+ assert_equal(1, Qu::Scheduler.rufus_scheduler.all_jobs.size)
38
+ assert Qu::Scheduler.scheduled_jobs.include?("some_ivar_job")
39
+
40
+ Qu.backend.redis.del(:schedules)
41
+ Qu.backend.redis.hset(:schedules, "some_ivar_job2", MultiJson.encode(
42
+ {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"}
43
+ ))
44
+
45
+ Qu::Scheduler.reload_schedule!
46
+
47
+ assert_equal(1, Qu::Scheduler.rufus_scheduler.all_jobs.size)
48
+
49
+ assert_equal '/tmp/2', Qu.schedule["some_ivar_job2"]["args"]
50
+ assert Qu::Scheduler.scheduled_jobs.include?("some_ivar_job2")
51
+ end
52
+
53
+ test "load_schedule_job loads a schedule" do
54
+ Qu::Scheduler.load_schedule_job("some_ivar_job", {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"})
55
+
56
+ assert_equal(1, Qu::Scheduler.rufus_scheduler.all_jobs.size)
57
+ assert_equal(1, Qu::Scheduler.scheduled_jobs.size)
58
+ assert Qu::Scheduler.scheduled_jobs.keys.include?("some_ivar_job")
59
+ end
60
+
61
+ test "load_schedule_job without cron" do
62
+ Qu::Scheduler.load_schedule_job("some_ivar_job", {'class' => 'SomeIvarJob', 'args' => "/tmp"})
63
+
64
+ assert_equal(0, Qu::Scheduler.rufus_scheduler.all_jobs.size)
65
+ assert_equal(0, Qu::Scheduler.scheduled_jobs.size)
66
+ assert !Qu::Scheduler.scheduled_jobs.keys.include?("some_ivar_job")
67
+ end
68
+
69
+ test "load_schedule_job with an empty cron" do
70
+ Qu::Scheduler.load_schedule_job("some_ivar_job", {'cron' => '', 'class' => 'SomeIvarJob', 'args' => "/tmp"})
71
+
72
+ assert_equal(0, Qu::Scheduler.rufus_scheduler.all_jobs.size)
73
+ assert_equal(0, Qu::Scheduler.scheduled_jobs.size)
74
+ assert !Qu::Scheduler.scheduled_jobs.keys.include?("some_ivar_job")
75
+ end
76
+
77
+ test "update_schedule" do
78
+ Qu::Scheduler.dynamic = true
79
+ Qu.schedule = {
80
+ "some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"},
81
+ "another_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/5"},
82
+ "stay_put_job" => {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp"}
83
+ }
84
+
85
+ Qu::Scheduler.load_schedule!
86
+
87
+ Qu.backend.set_schedule("some_ivar_job",
88
+ {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"}
89
+ )
90
+ Qu.backend.set_schedule("new_ivar_job",
91
+ {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp/3"}
92
+ )
93
+ Qu.backend.set_schedule("stay_put_job",
94
+ {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp"}
95
+ )
96
+ Qu.backend.remove_schedule("another_ivar_job")
97
+
98
+ Qu.backend.update_schedule
99
+
100
+ assert_equal(3, Qu::Scheduler.rufus_scheduler.all_jobs.size)
101
+ assert_equal(3, Qu::Scheduler.scheduled_jobs.size)
102
+ %w(some_ivar_job new_ivar_job stay_put_job).each do |job_name|
103
+ assert Qu::Scheduler.scheduled_jobs.keys.include?(job_name)
104
+ assert Qu.schedule.keys.include?(job_name)
105
+ end
106
+ assert !Qu::Scheduler.scheduled_jobs.keys.include?("another_ivar_job")
107
+ assert !Qu.schedule.keys.include?("another_ivar_job")
108
+ assert_equal 0, Qu.backend.redis.scard(:schedules_changed)
109
+ end
110
+
111
+ test "update_schedule with mocks" do
112
+ Qu::Scheduler.dynamic = true
113
+ Qu.schedule = {
114
+ "some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"},
115
+ "another_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/5"},
116
+ "stay_put_job" => {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp"}
117
+ }
118
+
119
+ Qu::Scheduler.load_schedule!
120
+
121
+ Qu::Scheduler.rufus_scheduler.expects(:unschedule).with(Qu::Scheduler.scheduled_jobs["some_ivar_job"].job_id)
122
+ Qu::Scheduler.rufus_scheduler.expects(:unschedule).with(Qu::Scheduler.scheduled_jobs["another_ivar_job"].job_id)
123
+
124
+ Qu.backend.set_schedule("some_ivar_job",
125
+ {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"}
126
+ )
127
+ Qu.backend.set_schedule("new_ivar_job",
128
+ {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp/3"}
129
+ )
130
+ Qu.backend.set_schedule("stay_put_job",
131
+ {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp"}
132
+ )
133
+ Qu.backend.remove_schedule("another_ivar_job")
134
+
135
+ Qu.backend.update_schedule
136
+
137
+ assert_equal(3, Qu::Scheduler.scheduled_jobs.size)
138
+ %w(some_ivar_job new_ivar_job stay_put_job).each do |job_name|
139
+ assert Qu::Scheduler.scheduled_jobs.keys.include?(job_name)
140
+ assert Qu.schedule.keys.include?(job_name)
141
+ end
142
+ assert !Qu::Scheduler.scheduled_jobs.keys.include?("another_ivar_job")
143
+ assert !Qu.schedule.keys.include?("another_ivar_job")
144
+ assert_equal 0, Qu.backend.redis.scard(:schedules_changed)
145
+ end
146
+
147
+ test "schedule= sets the schedule" do
148
+ Qu::Scheduler.dynamic = true
149
+ Qu.schedule = {"my_ivar_job" => {
150
+ 'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/75"
151
+ }}
152
+ assert_equal({'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/75"},
153
+ MultiJson.decode(Qu.backend.redis.hget(:schedules, "my_ivar_job")))
154
+ end
155
+
156
+ test "set_schedule can set an individual schedule" do
157
+ Qu.backend.set_schedule("some_ivar_job", {
158
+ 'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/22"
159
+ })
160
+ assert_equal({'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/22"},
161
+ MultiJson.decode(Qu.backend.redis.hget(:schedules, "some_ivar_job")))
162
+ assert Qu.backend.redis.sismember(:schedules_changed, "some_ivar_job")
163
+ end
164
+
165
+ test "get_schedule returns a schedule" do
166
+ Qu.backend.redis.hset(:schedules, "some_ivar_job2", MultiJson.encode(
167
+ {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/33"}
168
+ ))
169
+ assert_equal({'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/33"},
170
+ Qu.backend.get_schedule("some_ivar_job2"))
171
+ end
172
+
173
+ test "remove_schedule removes a schedule" do
174
+ Qu.backend.redis.hset(:schedules, "some_ivar_job3", MultiJson.encode(
175
+ {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/44"}
176
+ ))
177
+ Qu.backend.remove_schedule("some_ivar_job3")
178
+ assert_equal nil, Qu.backend.redis.hget(:schedules, "some_ivar_job3")
179
+ assert Qu.backend.redis.sismember(:schedules_changed, "some_ivar_job3")
180
+ end
181
+ end