resque_unit 0.4.8 → 1.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1aad0f765a2b78da6d5f17cdbf2f5646066d8b8d
4
- data.tar.gz: c694a2f46b3ec5892f7a83ebfae9d734dd0371ec
3
+ metadata.gz: d6be35403595c85939faf33cbacfaa990dfadd1e
4
+ data.tar.gz: e4891ea3f28fee039c921bd4c8f6264d9c2468da
5
5
  SHA512:
6
- metadata.gz: 812552beeb3b8c3de00be72b6c13b8dfc4fd2808df1114f308c3eb5aaba2635108d58d9583c5a8c879248b91b716c0bf21c38fad6b2d1453a0405c272daa3f3b
7
- data.tar.gz: 97110758541b5c3335906743fdd0dbfd2f1514106ec90ea98dbb9149bef0f86b0932974cc2ed2455d9ea119fffae551aaf9764302ab64101195f4ae3977dec8a
6
+ metadata.gz: db13bb8dd32e9cae5abfed2c9cb5e3162fef21186ca46e829b4bc3959e3a5d362dc562a223766853d2000f220cd95a9abf843e6b7e0f9e0c3736b4680baa629d
7
+ data.tar.gz: da321605d44889632e4adb15a5c7178ce28d0b3f1801d852bce8263b396d82b5c05efc691d986b0b8403e4fe949e348608281dfaf2ffa75b461b8c78918a6389
data/README.md CHANGED
@@ -3,7 +3,7 @@ ResqueUnit
3
3
 
4
4
  ResqueUnit provides some extra assertions and a mock Resque for
5
5
  testing Rails code that depends on Resque. You can install it as
6
- either a gem or a plugin:
6
+ either a gem:
7
7
 
8
8
  gem install resque_unit
9
9
 
@@ -11,12 +11,6 @@ and in your test.rb:
11
11
 
12
12
  config.gem 'resque_unit'
13
13
 
14
- If you'd rather install it as a plugin, you should be able to run
15
-
16
- script/plugin install git://github.com/justinweiss/resque_unit.git
17
-
18
- inside your Rails projects.
19
-
20
14
  Examples
21
15
  ========
22
16
 
@@ -103,10 +97,6 @@ Caveats
103
97
 
104
98
  * You should make sure that you call `Resque.reset!` in your test's
105
99
  setup method to clear all of the test queues.
106
- * Hooks support is optional. Just because you probably don't want to call
107
- them during unit tests if they play with a DB. Call `Resque.enable_hooks!`
108
- in your tests's setup method to enable hooks. To disable hooks, call
109
- `Resque.disable_hooks!`.
110
100
 
111
101
  Resque-Scheduler Support
112
102
  ========================
data/lib/resque_unit.rb CHANGED
@@ -6,14 +6,12 @@ begin
6
6
  rescue LoadError
7
7
  require 'json'
8
8
  end
9
-
9
+ require 'resque'
10
10
  require 'resque_unit/helpers'
11
11
  require 'resque_unit/resque'
12
- require 'resque_unit/errors'
13
12
  require 'resque_unit/assertions'
14
- require 'resque_unit/plugin'
15
13
 
16
- if defined?(Test::Unit)
14
+ if defined?(Test::Unit::TestCase)
17
15
  Test::Unit::TestCase.send(:include, ResqueUnit::Assertions)
18
16
  end
19
17
 
@@ -1,7 +1,6 @@
1
1
  # These are a group of assertions you can use in your unit tests to
2
2
  # verify that your code is using Resque correctly.
3
3
  module ResqueUnit::Assertions
4
-
5
4
  # Asserts that +klass+ has been queued into its appropriate queue at
6
5
  # least once. If +args+ is nil, it only asserts that the klass has
7
6
  # been queued. Otherwise, it asserts that the klass has been queued
@@ -9,14 +8,14 @@ module ResqueUnit::Assertions
9
8
  # want to assert that klass has been queued without arguments. Pass a block
10
9
  # if you want to assert something was queued within its execution.
11
10
  def assert_queued(klass, args = nil, message = nil, &block)
12
- queue_name = Resque.queue_for(klass)
11
+ queue_name = Resque.queue_from_class(klass)
13
12
  assert_job_created(queue_name, klass, args, message, &block)
14
13
  end
15
14
  alias assert_queues assert_queued
16
15
 
17
16
  # The opposite of +assert_queued+.
18
17
  def assert_not_queued(klass = nil, args = nil, message = nil, &block)
19
- queue_name = Resque.queue_for(klass)
18
+ queue_name = Resque.queue_from_class(klass)
20
19
 
21
20
  queue = if block_given?
22
21
  snapshot = Resque.size(queue_name)
@@ -32,9 +31,9 @@ module ResqueUnit::Assertions
32
31
 
33
32
  # Asserts no jobs were queued within the block passed.
34
33
  def assert_nothing_queued(message = nil, &block)
35
- snapshot = Resque.size
34
+ snapshot = total_job_count
36
35
  yield
37
- present = Resque.size
36
+ present = total_job_count
38
37
  assert_equal snapshot, present, message || "No jobs should have been queued"
39
38
  end
40
39
 
@@ -54,6 +53,11 @@ module ResqueUnit::Assertions
54
53
 
55
54
  private
56
55
 
56
+ # The total count of all the jobs in Resque.
57
+ def total_job_count
58
+ Resque.queues.inject(0) { |acc, queue| acc + Resque.size(queue) }
59
+ end
60
+
57
61
  # In Test::Unit, +assert_block+ displays only the message on a test
58
62
  # failure and +assert+ always appends a message to the end of the
59
63
  # passed-in assertion message. In MiniTest, it's the other way
@@ -1,224 +1,15 @@
1
- # The fake Resque class. This needs to be loaded after the real Resque
2
- # for the assertions in +ResqueUnit::Assertions+ to work.
3
- module Resque
4
- include ResqueUnit::Helpers
5
- extend self
6
-
7
- # Resets all the queues to the empty state. This should be called in
8
- # your test's +setup+ method until I can figure out a way for it to
9
- # automatically be called.
10
- #
11
- # If <tt>queue_name</tt> is given, then resets only that queue.
12
- def reset!(queue_name = nil)
13
- if @queue && queue_name
14
- @queue[queue_name] = []
15
- else
16
- @queue = Hash.new { |h, k| h[k] = [] }
17
- end
18
- end
19
-
20
- # Returns a hash of all the queue names and jobs that have been queued. The
21
- # format is <tt>{queue_name => [job, ..]}</tt>.
22
- def self.queues
23
- @queue || reset!
24
- end
25
-
26
- # Returns an array of all the jobs that have been queued. Each
27
- # element is of the form +{"class" => klass, "args" => args}+ where
28
- # +klass+ is the job's class and +args+ is an array of the arguments
29
- # passed to the job.
30
- def queue(queue_name)
31
- queues[queue_name]
32
- end
33
-
34
- # Return an array of all jobs' payloads for queue
35
- # Elements are decoded
36
- def all(queue_name)
37
- result = list_range(queue_name, 0, size(queue_name))
38
- result.is_a?(Array) ? result : [ result ]
39
- end
40
-
41
- # Returns an array of jobs' payloads for queue.
42
- #
43
- # start and count should be integer and can be used for pagination.
44
- # start is the item to begin, count is how many items to return.
45
- #
46
- # To get the 3rd page of a 30 item, paginatied list one would use:
47
- # Resque.peek('my_list', 59, 30)
48
- def peek(queue_name, start = 0, count = 1)
49
- list_range(queue_name, start, count)
50
- end
51
-
52
- # Gets a range of jobs' payloads from queue.
53
- # Returns single element if count equal 1
54
- # Elements are decoded
55
- def list_range(key, start = 0, count = 1)
56
- data = if count == 1
57
- decode(queues[key][start])
58
- else
59
- (queues[key][start...start + count] || []).map { |entry| decode(entry) }
60
- end
61
- end
62
-
63
- # Yes, all Resque hooks!
64
- def enable_hooks!
65
- @hooks_enabled = true
66
- end
67
-
68
- def disable_hooks!
69
- @hooks_enabled = nil
70
- end
71
-
72
- # Executes all jobs in all queues in an undefined order.
73
- def run!
74
- payloads = []
75
- @queue.each do |queue_name, queue|
76
- payloads.concat queue.slice!(0, queue.size)
77
- end
78
- exec_payloads payloads.shuffle
79
- end
80
-
81
- def run_for!(queue_name, limit=false)
82
- queue = @queue[queue_name]
83
- exec_payloads queue.slice!(0, ( limit ? limit : queue.size) ).shuffle
84
- end
85
-
86
- def exec_payloads(raw_payloads)
87
- raw_payloads.each do |raw_payload|
88
- job_payload = decode(raw_payload)
89
- @hooks_enabled ? perform_with_hooks(job_payload) : perform_without_hooks(job_payload)
90
- end
91
- end
92
- private :exec_payloads
93
-
94
- # 1. Execute all jobs in all queues in an undefined order,
95
- # 2. Check if new jobs were announced, and execute them.
96
- # 3. Repeat 3
97
- def full_run!
98
- run! until empty_queues?
99
- end
100
-
101
- # Returns the size of the given queue
102
- def size(queue_name = nil)
103
- if queue_name
104
- queues[queue_name].length
105
- else
106
- queues.values.flatten.length
107
- end
108
- end
109
-
110
- # :nodoc:
111
- def enqueue(klass, *args)
112
- enqueue_to( queue_for(klass), klass, *args)
113
- end
114
-
115
- # :nodoc:
116
- def enqueue_to( queue_name, klass, *args )
117
- # Behaves like Resque, raise if no queue was specifed
118
- raise NoQueueError.new("Jobs must be placed onto a queue.") unless queue_name
119
- enqueue_unit(queue_name, {"class" => klass.to_s, "args" => args })
120
- end
121
-
122
- # :nodoc:
123
- def queue_for(klass)
124
- klass.instance_variable_get(:@queue) || (klass.respond_to?(:queue) && klass.queue)
125
- end
126
- alias :queue_from_class :queue_for
127
-
128
- # :nodoc:
129
- def empty_queues?
130
- queues.all? do |k, v|
131
- v.empty?
132
- end
133
- end
134
-
135
- def enqueue_unit(queue_name, hash)
136
- klass = constantize(hash["class"])
137
- if @hooks_enabled
138
- before_hooks = Plugin.before_enqueue_hooks(klass).map do |hook|
139
- klass.send(hook, *hash["args"])
140
- end
141
- return nil if before_hooks.any? { |result| result == false }
142
- end
143
- queue(queue_name) << encode(hash)
144
- if @hooks_enabled
145
- Plugin.after_enqueue_hooks(klass).each do |hook|
146
- klass.send(hook, *hash["args"])
147
- end
148
- end
149
- queue(queue_name).size
150
- end
151
-
152
- # Call perform on the job class
153
- def perform_without_hooks(job_payload)
154
- constantize(job_payload["class"]).perform(*job_payload["args"])
155
- end
156
-
157
- # Call perform on the job class, and adds support for Resque hooks.
158
- def perform_with_hooks(job_payload)
159
- job_class = constantize(job_payload["class"])
160
- before_hooks = Resque::Plugin.before_hooks(job_class)
161
- around_hooks = Resque::Plugin.around_hooks(job_class)
162
- after_hooks = Resque::Plugin.after_hooks(job_class)
163
- failure_hooks = Resque::Plugin.failure_hooks(job_class)
164
-
165
- begin
166
- # Execute before_perform hook. Abort the job gracefully if
167
- # Resque::DontPerform is raised.
168
- begin
169
- before_hooks.each do |hook|
170
- job_class.send(hook, *job_payload["args"])
171
- end
172
- rescue Resque::Job::DontPerform
173
- return false
174
- end
175
-
176
- # Execute the job. Do it in an around_perform hook if available.
177
- if around_hooks.empty?
178
- perform_without_hooks(job_payload)
179
- job_was_performed = true
180
- else
181
- # We want to nest all around_perform plugins, with the last one
182
- # finally calling perform
183
- stack = around_hooks.reverse.inject(nil) do |last_hook, hook|
184
- if last_hook
185
- lambda do
186
- job_class.send(hook, *job_payload["args"]) { last_hook.call }
187
- end
188
- else
189
- lambda do
190
- job_class.send(hook, *job_payload["args"]) do
191
- result = perform_without_hooks(job_payload)
192
- job_was_performed = true
193
- result
194
- end
195
- end
196
- end
197
- end
198
- stack.call
199
- end
200
-
201
- # Execute after_perform hook
202
- after_hooks.each do |hook|
203
- job_class.send(hook, *job_payload["args"])
204
- end
205
-
206
- # Return true if the job was performed
207
- return job_was_performed
208
-
209
- # If an exception occurs during the job execution, look for an
210
- # on_failure hook then re-raise.
211
- rescue Object => e
212
- failure_hooks.each { |hook| job_class.send(hook, e, *job_payload["args"]) }
213
- raise e
214
- end
215
- end
216
-
217
- class Job
218
- extend ResqueUnit::Helpers
219
- def self.create(queue, klass_name, *args)
220
- Resque.enqueue_unit(queue, {"class" => constantize(klass_name).to_s, "args" => args})
221
- end
222
- end
223
-
224
- end
1
+ require 'resque'
2
+ require 'resque_unit/resque/test_extensions'
3
+
4
+ # This is a little weird. Fakeredis registers itself as the default
5
+ # redis driver after you load it. This might not be what you want,
6
+ # though -- resque_unit needs fakeredis, but you may have a reason to
7
+ # use a different redis driver for the rest of your test code. So
8
+ # we'll store the old default here, and restore it afer we're done
9
+ # loading fakeredis. Then, we'll point resque at fakeredis
10
+ # specifically.
11
+ default_redis_driver = Redis::Connection.drivers.pop
12
+ require 'fakeredis'
13
+ Redis::Connection.drivers << default_redis_driver if default_redis_driver
14
+
15
+ Resque.extend Resque::TestExtensions
@@ -0,0 +1,68 @@
1
+ module Resque
2
+ module TestExtensions
3
+ include ResqueUnit::Helpers
4
+
5
+ # A redis connection that always uses fakeredis.
6
+ def fake_redis
7
+ @fake_redis ||= Redis.new(driver: :memory)
8
+ end
9
+
10
+ # Always return the fake redis.
11
+ def redis
12
+ fake_redis
13
+ end
14
+
15
+ # Resets all the queues to the empty state. This should be called in
16
+ # your test's +setup+ method until I can figure out a way for it to
17
+ # automatically be called.
18
+ #
19
+ # If <tt>queue_name</tt> is given, then resets only that queue.
20
+ def reset!(queue = nil)
21
+ if queue
22
+ remove_queue(queue)
23
+ else
24
+ redis.flushall
25
+ end
26
+ end
27
+
28
+ # Return an array of all jobs' payloads for queue
29
+ # Elements are decoded
30
+ def all(queue_name)
31
+ jobs = peek(queue_name, 0, size(queue_name))
32
+ jobs.kind_of?(Array) ? jobs : [jobs]
33
+ end
34
+ alias queue all
35
+
36
+ # Executes all jobs in all queues in an undefined order.
37
+ def run!
38
+ payloads = []
39
+ queues.each do |queue|
40
+ size(queue).times { payloads << pop(queue) }
41
+ end
42
+ exec_payloads payloads.shuffle
43
+ end
44
+
45
+ def run_for!(queue, limit = Float::INFINITY)
46
+ job_count = [limit, size(queue)].min
47
+ payloads = []
48
+
49
+ job_count.times { payloads << pop(queue) }
50
+ exec_payloads payloads.shuffle
51
+ end
52
+
53
+ def exec_payloads(raw_payloads)
54
+ raw_payloads.each do |raw_payload|
55
+ Resque::Job.new(:inline, raw_payload).perform
56
+ end
57
+ end
58
+
59
+ private :exec_payloads
60
+
61
+ # 1. Execute all jobs in all queues in an undefined order,
62
+ # 2. Check if new jobs were announced, and execute them.
63
+ # 3. Repeat 3
64
+ def full_run!
65
+ run! until queues.all? { |queue| size(queue) == 0 }
66
+ end
67
+ end
68
+ end
@@ -3,7 +3,7 @@
3
3
  require 'time'
4
4
 
5
5
  module ResqueUnit::SchedulerAssertions
6
-
6
+
7
7
  # Asserts that +klass+ has been queued into its appropriate queue at
8
8
  # least once, with a +timestamp+ less than or equal to
9
9
  # +expected_timestamp+. If the job wasn't queued with a timestamp,
@@ -13,7 +13,7 @@ module ResqueUnit::SchedulerAssertions
13
13
  # +args+ if you want to assert that klass has been queued without
14
14
  # arguments.
15
15
  def assert_queued_at(expected_timestamp, klass, args = nil, message = nil)
16
- queue = Resque.queue_for(klass)
16
+ queue = Resque.queue_from_class(klass)
17
17
  assert in_timestamped_queue?(queue, expected_timestamp, klass, args),
18
18
  (message || "#{klass} should have been queued in #{queue} before #{expected_timestamp}: #{Resque.queue(queue).inspect}.")
19
19
  end
@@ -23,10 +23,10 @@ module ResqueUnit::SchedulerAssertions
23
23
  def assert_queued_in(expected_time_difference, klass, args = nil, message = nil)
24
24
  assert_queued_at(Time.now + expected_time_difference, klass, args, message)
25
25
  end
26
-
26
+
27
27
  # opposite of +assert_queued_at+
28
28
  def assert_not_queued_at(expected_timestamp, klass, args = nil, message = nil)
29
- queue = Resque.queue_for(klass)
29
+ queue = Resque.queue_from_class(klass)
30
30
  assert !in_timestamped_queue?(queue, expected_timestamp, klass, args),
31
31
  (message || "#{klass} should not have been queued in #{queue} before #{expected_timestamp}.")
32
32
  end
@@ -38,10 +38,24 @@ module ResqueUnit::SchedulerAssertions
38
38
 
39
39
  private
40
40
 
41
- def in_timestamped_queue?(queue_name, expected_timestamp, klass, args = nil)
41
+ def in_queue?(queue, klass, args = nil)
42
+ super(queue, klass, args) || !matching_jobs(all_jobs_scheduled_before_or_at(:forever), klass, args).empty?
43
+ end
44
+
45
+ def in_timestamped_queue?(queue_name, max_timestamp, klass, args = nil)
42
46
  # check if we have any matching jobs with a timestamp less than
43
47
  # expected_timestamp
44
- !matching_jobs(Resque.all(queue_name), klass, args).select {|e| e["timestamp"] && Time.parse(e["timestamp"]) <= expected_timestamp}.empty?
48
+ !matching_jobs(all_jobs_scheduled_before_or_at(max_timestamp), klass, args).empty?
45
49
  end
46
-
50
+
51
+ def all_jobs_scheduled_before_or_at(max_timestamp = :forever)
52
+ timestamps = Resque.delayed_queue_peek(0, Resque.delayed_queue_schedule_size).map(&:to_i)
53
+
54
+ if max_timestamp != :forever
55
+ timestamps.select! { |timestamp| Time.at(timestamp) <= Time.at(max_timestamp) }
56
+ end
57
+
58
+ timestamps.flat_map { |timestamp| Resque.delayed_timestamp_peek(timestamp, 0, Resque.delayed_timestamp_size(timestamp)) }
59
+ end
60
+
47
61
  end