rails-queue 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ module Rails
2
+ module Queue
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ # clone Rails 3.2 one directory up from our library
2
+ git clone git://github.com/rails/rails.git ../rails
3
+ cd ../rails
4
+ git checkout v$RAILS_VERSION
@@ -0,0 +1 @@
1
+ Welcome
@@ -0,0 +1,7 @@
1
+ require File.expand_path("../../lib/rails-queue", __FILE__)
2
+
3
+ class Rails::QueueTest < ActiveSupport::TestCase
4
+ test "module definition" do
5
+ assert_kind_of Module, Rails::Queue
6
+ end
7
+ end
@@ -0,0 +1,35 @@
1
+ require "support/isolation_abstract_unit"
2
+ require 'set'
3
+
4
+ module ApplicationTests
5
+ class FrameworksTest < ActiveSupport::TestCase
6
+ include ActiveSupport::Testing::Isolation
7
+
8
+ def setup
9
+ build_app
10
+ boot_rails
11
+ FileUtils.rm_rf "#{app_path}/config/environments"
12
+ end
13
+
14
+ def teardown
15
+ teardown_app
16
+ end
17
+
18
+ test "uses the default queue for ActionMailer" do
19
+ require "#{app_path}/config/environment"
20
+ assert_kind_of Rails::Queue::Queue, ActionMailer::Base.queue
21
+ end
22
+
23
+ test "allows me to configure queue for ActionMailer" do
24
+ app_file "config/environments/development.rb", <<-RUBY
25
+ AppTemplate::Application.configure do
26
+ config.action_mailer.queue = Rails::Queue::TestQueue.new
27
+ end
28
+ RUBY
29
+
30
+ require "#{app_path}/config/environment"
31
+ assert_kind_of Rails::Queue::TestQueue, ActionMailer::Base.queue
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ # Original File: RAILS_ROOT/actionmailer/test/base_test.rb
2
+ require "support/action_mailer_abstract_unit"
3
+ require "rails-queue"
4
+ require "support/mailers"
5
+
6
+ class BaseTest < ActiveSupport::TestCase
7
+ def teardown
8
+ ActionMailer::Base.asset_host = nil
9
+ ActionMailer::Base.assets_dir = nil
10
+ end
11
+
12
+ test "delivering message asynchronously" do
13
+ AsyncMailer.delivery_method = :test
14
+ AsyncMailer.deliveries.clear
15
+
16
+ AsyncMailer.welcome.deliver
17
+ assert_equal 0, AsyncMailer.deliveries.length
18
+
19
+ AsyncMailer.queue.drain
20
+ assert_equal 1, AsyncMailer.deliveries.length
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ # Original File: RAILS_ROOT/activesupport/test/queueing/synchronous_queue_test.rb
2
+ require "support/active_support_abstract_unit"
3
+ require "rails-queue"
4
+
5
+ class SynchronousQueueTest < ActiveSupport::TestCase
6
+ class Job
7
+ attr_reader :ran
8
+ def run; @ran = true end
9
+ end
10
+
11
+ class ExceptionRaisingJob
12
+ def run; raise end
13
+ end
14
+
15
+ def setup
16
+ @queue = Rails::Queue::SynchronousQueue.new
17
+ end
18
+
19
+ def test_runs_jobs_immediately
20
+ job = Job.new
21
+ @queue.push job
22
+ assert job.ran
23
+
24
+ assert_raises RuntimeError do
25
+ @queue.push ExceptionRaisingJob.new
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,147 @@
1
+ # Original File: RAILS_ROOT/activesupport/test/queueing/test_queue_test.rb
2
+ require "support/active_support_abstract_unit"
3
+ require "rails-queue"
4
+
5
+ class TestQueueTest < ActiveSupport::TestCase
6
+ def setup
7
+ @queue = Rails::Queue::TestQueue.new
8
+ end
9
+
10
+ class ExceptionRaisingJob
11
+ def run
12
+ raise
13
+ end
14
+ end
15
+
16
+ def test_drain_raises_exceptions_from_running_jobs
17
+ @queue.push ExceptionRaisingJob.new
18
+ assert_raises(RuntimeError) { @queue.drain }
19
+ end
20
+
21
+ def test_jobs
22
+ @queue.push 1
23
+ @queue.push 2
24
+ assert_equal [1,2], @queue.jobs
25
+ end
26
+
27
+ class EquivalentJob
28
+ def initialize
29
+ @initial_id = self.object_id
30
+ end
31
+
32
+ def run
33
+ end
34
+
35
+ def ==(other)
36
+ other.same_initial_id?(@initial_id)
37
+ end
38
+
39
+ def same_initial_id?(other_id)
40
+ other_id == @initial_id
41
+ end
42
+ end
43
+
44
+ def test_contents
45
+ job = EquivalentJob.new
46
+ assert @queue.empty?
47
+ @queue.push job
48
+ refute @queue.empty?
49
+ assert_equal job, @queue.pop
50
+ end
51
+
52
+ class ProcessingJob
53
+ def self.clear_processed
54
+ @processed = []
55
+ end
56
+
57
+ def self.processed
58
+ @processed
59
+ end
60
+
61
+ def initialize(object)
62
+ @object = object
63
+ end
64
+
65
+ def run
66
+ self.class.processed << @object
67
+ end
68
+ end
69
+
70
+ def test_order
71
+ ProcessingJob.clear_processed
72
+ job1 = ProcessingJob.new(1)
73
+ job2 = ProcessingJob.new(2)
74
+
75
+ @queue.push job1
76
+ @queue.push job2
77
+ @queue.drain
78
+
79
+ assert_equal [1,2], ProcessingJob.processed
80
+ end
81
+
82
+ class ThreadTrackingJob
83
+ attr_reader :thread_id
84
+
85
+ def run
86
+ @thread_id = Thread.current.object_id
87
+ end
88
+
89
+ def ran?
90
+ @thread_id
91
+ end
92
+ end
93
+
94
+ def test_drain
95
+ @queue.push ThreadTrackingJob.new
96
+ job = @queue.jobs.last
97
+ @queue.drain
98
+
99
+ assert @queue.empty?
100
+ assert job.ran?, "The job runs synchronously when the queue is drained"
101
+ assert_equal job.thread_id, Thread.current.object_id
102
+ end
103
+
104
+ class IdentifiableJob
105
+ def initialize(id)
106
+ @id = id
107
+ end
108
+
109
+ def ==(other)
110
+ other.same_id?(@id)
111
+ end
112
+
113
+ def same_id?(other_id)
114
+ other_id == @id
115
+ end
116
+
117
+ def run
118
+ end
119
+ end
120
+
121
+ def test_queue_can_be_observed
122
+ jobs = (1..10).map do |id|
123
+ IdentifiableJob.new(id)
124
+ end
125
+
126
+ jobs.each do |job|
127
+ @queue.push job
128
+ end
129
+
130
+ assert_equal jobs, @queue.jobs
131
+ end
132
+
133
+ def test_adding_an_unmarshallable_job
134
+ anonymous_class_instance = Struct.new(:run).new
135
+
136
+ assert_raises TypeError do
137
+ @queue.push anonymous_class_instance
138
+ end
139
+ end
140
+
141
+ def test_attempting_to_add_a_reference_to_itself
142
+ job = {reference: @queue}
143
+ assert_raises TypeError do
144
+ @queue.push job
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,111 @@
1
+ # Original File: RAILS_ROOT/activesupport/test/queueing/threaded_consumer_test.rb
2
+ require "support/active_support_abstract_unit"
3
+ require "rails-queue"
4
+ require "active_support/log_subscriber/test_helper"
5
+
6
+ class TestThreadConsumer < ActiveSupport::TestCase
7
+ class Job
8
+ attr_reader :id
9
+ def initialize(id = 1, &block)
10
+ @id = id
11
+ @block = block
12
+ end
13
+
14
+ def run
15
+ @block.call if @block
16
+ end
17
+ end
18
+
19
+ def setup
20
+ @logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
21
+ @queue = Rails::Queue::Queue.new(logger: @logger)
22
+ end
23
+
24
+ def teardown
25
+ @queue.drain
26
+ end
27
+
28
+ test "the jobs are executed" do
29
+ ran = false
30
+ job = Job.new { ran = true }
31
+
32
+ @queue.push job
33
+ @queue.drain
34
+
35
+ assert_equal true, ran
36
+ end
37
+
38
+ test "the jobs are not executed synchronously" do
39
+ run, ran = Queue.new, Queue.new
40
+ job = Job.new { ran.push run.pop }
41
+
42
+ @queue.consumer.start
43
+ @queue.push job
44
+ assert ran.empty?
45
+
46
+ run.push true
47
+ assert_equal true, ran.pop
48
+ end
49
+
50
+ test "shutting down the queue synchronously drains the jobs" do
51
+ ran = false
52
+ job = Job.new do
53
+ sleep 0.1
54
+ ran = true
55
+ end
56
+
57
+ @queue.consumer.start
58
+ @queue.push job
59
+ assert_equal false, ran
60
+
61
+ @queue.consumer.shutdown
62
+ assert_equal true, ran
63
+ end
64
+
65
+ test "log job that raises an exception" do
66
+ job = Job.new { raise "RuntimeError: Error!" }
67
+
68
+ @queue.push job
69
+ consume_queue @queue
70
+
71
+ assert_equal 1, @logger.logged(:error).size
72
+ assert_match "Job Error: #{job.inspect}\nRuntimeError: Error!", @logger.logged(:error).last
73
+ end
74
+
75
+ test "logger defaults to stderr" do
76
+ begin
77
+ $stderr, old_stderr = StringIO.new, $stderr
78
+ queue = Rails::Queue::Queue.new
79
+ queue.push Job.new { raise "RuntimeError: Error!" }
80
+ consume_queue queue
81
+ assert_match 'Job Error', $stderr.string
82
+ ensure
83
+ $stderr = old_stderr
84
+ end
85
+ end
86
+
87
+ test "test overriding exception handling" do
88
+ @queue.consumer.instance_eval do
89
+ def handle_exception(job, exception)
90
+ @last_error = exception.message
91
+ end
92
+
93
+ def last_error
94
+ @last_error
95
+ end
96
+ end
97
+
98
+ job = Job.new { raise "RuntimeError: Error!" }
99
+
100
+ @queue.push job
101
+ consume_queue @queue
102
+
103
+ assert_equal "RuntimeError: Error!", @queue.consumer.last_error
104
+ end
105
+
106
+ private
107
+ def consume_queue(queue)
108
+ queue.push nil
109
+ queue.consumer.consume
110
+ end
111
+ end
@@ -0,0 +1,152 @@
1
+ require "support/isolation_abstract_unit"
2
+
3
+ class QueueTest < ActiveSupport::TestCase
4
+ include ActiveSupport::Testing::Isolation
5
+
6
+ def setup
7
+ build_app
8
+ boot_rails
9
+ end
10
+
11
+ def teardown
12
+ teardown_app
13
+ end
14
+
15
+ def app_const
16
+ @app_const ||= Class.new(Rails::Application)
17
+ end
18
+
19
+ test "the queue is a SynchronousQueue in test mode" do
20
+ app("test")
21
+ assert_kind_of Rails::Queue::SynchronousQueue, Rails.application.queue
22
+ assert_kind_of Rails::Queue::SynchronousQueue, Rails.queue
23
+ end
24
+
25
+ test "the queue is a SynchronousQueue in development mode" do
26
+ app("development")
27
+ assert_kind_of Rails::Queue::SynchronousQueue, Rails.application.queue
28
+ assert_kind_of Rails::Queue::SynchronousQueue, Rails.queue
29
+ end
30
+
31
+ class ThreadTrackingJob
32
+ def initialize
33
+ @origin = Thread.current.object_id
34
+ end
35
+
36
+ def run
37
+ @target = Thread.current.object_id
38
+ end
39
+
40
+ def ran_in_different_thread?
41
+ @origin != @target
42
+ end
43
+
44
+ def ran?
45
+ @target
46
+ end
47
+ end
48
+
49
+ test "in development mode, an enqueued job will be processed in the same thread" do
50
+ app("development")
51
+
52
+ job = ThreadTrackingJob.new
53
+ Rails.queue.push job
54
+ sleep 0.1
55
+
56
+ assert job.ran?, "Expected job to be run"
57
+ refute job.ran_in_different_thread?, "Expected job to run in the same thread"
58
+ end
59
+
60
+ test "in test mode, an enqueued job will be processed in the same thread" do
61
+ app("test")
62
+
63
+ job = ThreadTrackingJob.new
64
+ Rails.queue.push job
65
+ sleep 0.1
66
+
67
+ assert job.ran?, "Expected job to be run"
68
+ refute job.ran_in_different_thread?, "Expected job to run in the same thread"
69
+ end
70
+
71
+ test "in production, automatically spawn a queue consumer in a background thread" do
72
+ add_to_env_config "production", <<-RUBY
73
+ config.queue = Rails::Queue::Queue.new
74
+ RUBY
75
+
76
+ app("production")
77
+
78
+ assert_nil Rails.application.config.queue_consumer
79
+ assert_kind_of Rails::Queue::ThreadedQueueConsumer, Rails.application.queue_consumer
80
+ assert_equal Rails.logger, Rails.application.queue_consumer.logger
81
+ end
82
+
83
+ test "attempting to marshal a queue will raise an exception" do
84
+ app("test")
85
+ assert_raises TypeError do
86
+ Marshal.dump Rails.queue
87
+ end
88
+ end
89
+
90
+ def setup_custom_queue
91
+ add_to_env_config "production", <<-RUBY
92
+ require "my_queue"
93
+ config.queue = MyQueue.new
94
+ RUBY
95
+
96
+ app_file "lib/my_queue.rb", <<-RUBY
97
+ class MyQueue
98
+ def push(job)
99
+ job.run
100
+ end
101
+ end
102
+ RUBY
103
+
104
+ app("production")
105
+ end
106
+
107
+ test "a custom queue implementation can be provided" do
108
+ setup_custom_queue
109
+
110
+ assert_kind_of MyQueue, Rails.queue
111
+
112
+ job = Struct.new(:id, :ran) do
113
+ def run
114
+ self.ran = true
115
+ end
116
+ end
117
+
118
+ job1 = job.new(1)
119
+ Rails.queue.push job1
120
+
121
+ assert_equal true, job1.ran
122
+ end
123
+
124
+ test "a custom consumer implementation can be provided" do
125
+ add_to_env_config "production", <<-RUBY
126
+ require "my_queue_consumer"
127
+ config.queue = Rails::Queue::Queue.new
128
+ config.queue_consumer = MyQueueConsumer.new
129
+ RUBY
130
+
131
+ app_file "lib/my_queue_consumer.rb", <<-RUBY
132
+ class MyQueueConsumer
133
+ attr_reader :started
134
+
135
+ def start
136
+ @started = true
137
+ end
138
+ end
139
+ RUBY
140
+
141
+ app("production")
142
+
143
+ assert_kind_of MyQueueConsumer, Rails.application.queue_consumer
144
+ assert Rails.application.queue_consumer.started
145
+ end
146
+
147
+ test "default consumer is not used with custom queue implementation" do
148
+ setup_custom_queue
149
+
150
+ assert_nil Rails.application.queue_consumer
151
+ end
152
+ end