tom_queue 0.0.1.dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,225 @@
1
+ require 'tom_queue/helper'
2
+ require 'tom_queue/delayed_job'
3
+
4
+ describe "External consumers" do
5
+
6
+ let(:exchange_name) { "external-exchange-#{Time.now.to_f}" }
7
+ let(:trace) { [] }
8
+
9
+ let(:consumer_class) do
10
+ Class.new(Object) { include TomQueue::ExternalConsumer }.tap { |k| k.class_exec(trace) do |trace|
11
+ self::Trace = trace
12
+ def self.trace(*stuff); self::Trace << stuff; end
13
+ def trace(*stuff); self.class.trace(*stuff); end
14
+ end }
15
+ end
16
+
17
+ before do
18
+ # Hopefully break the core consumer loop out of DJ
19
+ TomQueue.default_prefix = "test-prefix-#{Time.now.to_f}"
20
+
21
+ TomQueue::DelayedJob.handlers.clear
22
+ TomQueue::DelayedJob.handlers << consumer_class
23
+ end
24
+
25
+ subject { Delayed::Worker.new.work_off(2) }
26
+
27
+ it "should be possible to make a consumer using the TomQueue::ExternalConsumer mixin" do
28
+ consumer_class
29
+ end
30
+
31
+ describe "when a consumer is bound to an exchange without a block" do
32
+
33
+ before do
34
+ consumer_class.class_exec(exchange_name) do |exchange_name|
35
+
36
+ bind_exchange(:fanout, exchange_name, :auto_delete => true, :durable => :false)
37
+
38
+ def initialize(payload, headers)
39
+ @payload = payload
40
+ trace(:init, payload, headers)
41
+ end
42
+
43
+ def perform
44
+ trace(:perform, @payload)
45
+ end
46
+
47
+ end
48
+
49
+ consumer_class.producer.publish('message')
50
+ subject
51
+ end
52
+
53
+ it "should call the init and perform methods the consumer" do
54
+ trace.map { |a| a[0] }.should == [:init, :perform]
55
+ end
56
+
57
+ it "should call the init method with the payload and headers" do
58
+ trace.first.tap do |method, payload, headers|
59
+ method.should == :init
60
+ payload.should == 'message'
61
+ headers.should be_a(Hash)
62
+ end
63
+ end
64
+
65
+ it "should have serialized the class so ivars from init are available during the perform call" do
66
+ trace.last.should == [:perform, 'message']
67
+ end
68
+ end
69
+
70
+ describe "when a block is attached to the bind_exchange call" do
71
+
72
+ before do
73
+ consumer_class.class_exec(exchange_name) do |exchange_name|
74
+
75
+ bind_exchange(:fanout, exchange_name, :auto_delete => true, :durable => :false) do |work|
76
+ trace(:bind_block, work)
77
+ self.block_method(work)
78
+ end
79
+
80
+ def self.block_method(work)
81
+ "something other than a delayed job"
82
+ end
83
+
84
+ end
85
+
86
+ consumer_class.producer.publish('message')
87
+ end
88
+
89
+ it "should call the block attached to the bind_exchange call" do
90
+ subject
91
+ trace.first.first.should == :bind_block
92
+ end
93
+
94
+ it "should pass the TomQueue::Work object to the block" do
95
+ subject
96
+ trace.first.last.tap do |work|
97
+ work.should be_a(TomQueue::Work)
98
+ work.payload.should == 'message'
99
+ end
100
+ end
101
+
102
+ describe "if something other than a Delayed::Job instance is returned" do
103
+ it "should ack the original AMQP message" do
104
+ pending('...')
105
+ end
106
+ end
107
+
108
+ describe "if the block returns a Delayed::Job instance" do
109
+
110
+ before do
111
+ consumer_class.class_eval do
112
+ def self.block_method(work)
113
+ new('custom-arg').delay.perform
114
+ end
115
+
116
+ def initialize(arg)
117
+ trace(:init, arg)
118
+ end
119
+ def perform
120
+ trace(:job_performed)
121
+ end
122
+ end
123
+ end
124
+
125
+ it "should call the bind block, which calls the init and defers the perform call" do
126
+ subject
127
+ trace.map { |a| a[0] }.should == [:bind_block, :init, :job_performed]
128
+ end
129
+
130
+ it "should be init'd directly with the custom arguments" do
131
+ subject
132
+ trace[1].last.should == 'custom-arg'
133
+ end
134
+
135
+ it "should perform the Delayed::Job" do
136
+ subject
137
+ trace.last.should == [:job_performed]
138
+ end
139
+
140
+ end
141
+
142
+ end
143
+
144
+
145
+ it "should use the encoder if specified"
146
+
147
+
148
+ # it "should not re-deliver the message once the delayed job has been created"
149
+
150
+
151
+
152
+
153
+
154
+ # it "should raise an exception if you publish a message without having bound the consumer"
155
+ # it "should raise an exception if you try to bind a consumer twice"
156
+
157
+
158
+ # it "should successfully round-trip a message" do
159
+ # consumer_class.producer.publish("a message")
160
+ # Delayed::Worker.new.work_off(1)
161
+ # consumer_class.messages.should == ["a message"]
162
+ # end
163
+
164
+ # it "should republish a message if an exception is raised" do
165
+ # consumer_class.producer.publish("asplode")
166
+ # consumer_class.asplode_count = 1
167
+ # Delayed::Worker.new.work_off(2)
168
+ # consumer_class.messages.should == ["asplode"]
169
+ # end
170
+
171
+ # it "should immediately run a job if one is returned out of the block"
172
+
173
+
174
+ # describe "if an exception is thrown by two workers" do
175
+ # it "should push the message to a dead-letter queue if an exception is raised twice"
176
+ # it "should trigger a log message"
177
+ # it "should notify the exception reporter"
178
+ # end
179
+
180
+ # end
181
+
182
+ # describe "temporary unit tests" do
183
+
184
+ # it "should clear any priority bindings just in case the priority changes"
185
+ # it "should not allow multiple bind_exchange calls to the consumer (for now)"
186
+
187
+ # describe "exchange options" do
188
+ # it "should set the auto-delete if specified"
189
+ # it "should default auto-delete to false"
190
+
191
+ # it "should set the durable flag if specified"
192
+ # it "should default durable to true"
193
+ # end
194
+
195
+ # describe "when a message is received" do
196
+ # it "should reject the message if an exception is thrown"
197
+ # it "should ack the message if the block succeeds"
198
+ # it "should re-deliver the message once"
199
+ # it "should post the message to a dead-letter queue if the redelivery attempt fails"
200
+ # end
201
+
202
+
203
+ # describe "when a block is provided" do
204
+
205
+ # it "should call the block with received message payload"
206
+
207
+ # end
208
+
209
+ # describe "when a block isn't provided" do
210
+
211
+ # it "should create an instance of the consumer class"
212
+ # it "should call #perform on the instance"
213
+ # it "should pass the payload as the first argument to the call"
214
+
215
+ # end
216
+
217
+
218
+ # describe "producer call" do
219
+
220
+ # it "should return an object that responds to :publish"
221
+ # it "should publish a message to the exchange when :publish is called"
222
+
223
+ # end
224
+
225
+ end
@@ -0,0 +1,91 @@
1
+ require 'helper'
2
+ require 'bunny'
3
+ require 'rest_client'
4
+ require 'tom_queue'
5
+ require 'tom_queue/delayed_job'
6
+
7
+ # Patch AR to allow Mock errors to escape after_commit callbacks
8
+ # There is a test to check this hook works in delayed_job_spec.rb
9
+ require 'active_record/connection_adapters/abstract/database_statements'
10
+ module ActiveRecord::ConnectionAdapters::DatabaseStatements
11
+ alias orig_commit_transaction_records commit_transaction_records
12
+ def commit_transaction_records
13
+ records = @_current_transaction_records.flatten
14
+ @_current_transaction_records.clear
15
+ unless records.blank?
16
+ records.uniq.each do |record|
17
+ begin
18
+ record.committed!
19
+ rescue Exception => e
20
+ if e.class.to_s =~ /^RSpec/
21
+ raise
22
+ else
23
+ record.logger.error(e) if record.respond_to?(:logger) && record.logger
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ begin
32
+ begin
33
+ RestClient.delete("http://guest:guest@localhost:15672/api/vhosts/test")
34
+ rescue RestClient::ResourceNotFound
35
+ end
36
+ RestClient.put("http://guest:guest@localhost:15672/api/vhosts/test", "{}", :content_type => :json, :accept => :json)
37
+ RestClient.put("http://guest:guest@localhost:15672/api/permissions/test/guest", '{"configure":".*","write":".*","read":".*"}', :content_type => :json, :accept => :json)
38
+ TheBunny = Bunny.new(:host => 'localhost', :vhost => 'test', :user => 'guest', :password => 'guest')
39
+ TheBunny.start
40
+ rescue Errno::ECONNREFUSED
41
+ $stderr.puts "\033[1;31mFailed to connect to RabbitMQ, is it running?\033[0m\n\n"
42
+ raise
43
+ end
44
+
45
+ RSpec.configure do |r|
46
+
47
+ r.before do
48
+ TomQueue.exception_reporter = Class.new do
49
+ def notify(exception)
50
+ puts "Exception reported: #{exception.inspect}"
51
+ puts exception.backtrace.join("\n")
52
+ end
53
+ end.new
54
+
55
+ TomQueue.logger = Logger.new($stdout) if ENV['DEBUG']
56
+ end
57
+
58
+ # Make sure all tests see the same Bunny instance
59
+ r.before do |test|
60
+ TomQueue.bunny = TheBunny
61
+ end
62
+
63
+ r.before do
64
+ TomQueue.logger ||= Logger.new("/dev/null")
65
+ TomQueue.default_prefix = "test-#{Time.now.to_f}"
66
+ TomQueue::DelayedJob.apply_hook!
67
+ Delayed::Job.class_variable_set(:@@tomqueue_manager, nil)
68
+ end
69
+
70
+ # All tests should take < 2 seconds !!
71
+ r.around do |test|
72
+ timeout = self.class.metadata[:timeout] || 2
73
+ if timeout == false
74
+ test.call
75
+ else
76
+ Timeout.timeout(timeout) { test.call }
77
+ end
78
+ end
79
+
80
+ r.around do |test|
81
+ begin
82
+ TomQueue::DeferredWorkManager.reset!
83
+
84
+ test.call
85
+
86
+ ensure
87
+ TomQueue::DeferredWorkManager.reset!
88
+ end
89
+ end
90
+
91
+ end
@@ -0,0 +1,152 @@
1
+ require 'tom_queue/helper'
2
+ require 'tempfile'
3
+
4
+ describe TomQueue::LoggingHelper do
5
+
6
+ include TomQueue::LoggingHelper
7
+
8
+ let(:file) { Tempfile.new("logfile") }
9
+ let(:logger) { Logger.new(file.path) }
10
+ let(:output) { file.flush; File.read(file.path) }
11
+
12
+ before do
13
+ logger.formatter = ::Logger::Formatter.new
14
+ TomQueue.logger = logger
15
+ end
16
+
17
+ describe "basic behaviour" do
18
+
19
+ it "should emit a debug message passed as an argument" do
20
+ debug "Simple to compute"
21
+ output.should =~ /^D.+Simple to compute$/
22
+ end
23
+
24
+ it "should emit an info message passed as an argument" do
25
+ info "Simple to compute"
26
+ output.should =~ /^I.+Simple to compute$/
27
+ end
28
+
29
+ it "should emit a warning message passed as an argument" do
30
+ warn "Simple to compute"
31
+ output.should =~ /^W.+Simple to compute$/
32
+ end
33
+
34
+ it "should emit an error message passed as an argument" do
35
+ error "Simple to compute"
36
+ output.should =~ /^E.+Simple to compute$/
37
+ end
38
+
39
+ it "should emit a debug message returned from the block" do
40
+ debug { "Expensive to compute" }
41
+ output.should =~ /^D.+Expensive to compute$/
42
+ end
43
+
44
+ it "should emit a info message returned from the block" do
45
+ info { "Expensive to compute" }
46
+ output.should =~ /^I.+Expensive to compute$/
47
+ end
48
+
49
+ it "should emit a warn message returned from the block" do
50
+ warn { "Expensive to compute" }
51
+ output.should =~ /^W.+Expensive to compute$/
52
+ end
53
+
54
+ it "should emit a error message returned from the block" do
55
+ error { "Expensive to compute" }
56
+ output.should =~ /^E.+Expensive to compute$/
57
+ end
58
+ end
59
+
60
+ describe "when the log level is info" do
61
+ before do
62
+ logger.level = Logger::INFO
63
+ end
64
+
65
+ it "should not yield the debug block" do
66
+ @called = false
67
+ debug { @called = true }
68
+ @called.should be_false
69
+ end
70
+ end
71
+
72
+ describe "when the log level is warn" do
73
+ before do
74
+ logger.level = Logger::WARN
75
+ end
76
+
77
+ it "should not yield the debug block" do
78
+ @called = false
79
+ debug { @called = true }
80
+ @called.should be_false
81
+ end
82
+ it "should not yield the info block" do
83
+ @called = false
84
+ info { @called = true }
85
+ @called.should be_false
86
+ end
87
+ end
88
+
89
+ describe "when the log level is error" do
90
+ before do
91
+ logger.level = Logger::ERROR
92
+ end
93
+
94
+ it "should not yield the debug block" do
95
+ @called = false
96
+ debug { @called = true }
97
+ @called.should be_false
98
+ end
99
+ it "should not yield the info block" do
100
+ @called = false
101
+ info { @called = true }
102
+ @called.should be_false
103
+ end
104
+ it "should not yield the warn block" do
105
+ @called = false
106
+ warn { @called = true }
107
+ @called.should be_false
108
+ end
109
+ end
110
+
111
+ describe "when TomQueue.logger is nil" do
112
+ before do
113
+ TomQueue.logger = nil
114
+ end
115
+ it "should not yield the debug block" do
116
+ @called = false
117
+ debug { @called = true }
118
+ @called.should be_false
119
+ end
120
+ it "should not yield the info block" do
121
+ @called = false
122
+ info { @called = true }
123
+ @called.should be_false
124
+ end
125
+ it "should not yield the warn block" do
126
+ @called = false
127
+ warn { @called = true }
128
+ @called.should be_false
129
+ end
130
+ it "should not yield the error block" do
131
+ @called = false
132
+ error { @called = true }
133
+ @called.should be_false
134
+ end
135
+ it "should drop debug messages silently" do
136
+ debug "Message"
137
+ end
138
+ it "should drop info messages silently" do
139
+ info "Message"
140
+ end
141
+ it "should drop warn messages silently" do
142
+ warn "Message"
143
+ end
144
+ it "should drop error messages silently" do
145
+ error "Message"
146
+ end
147
+ end
148
+
149
+
150
+
151
+ end
152
+