larrytheliquid-moqueue 0.1.2

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,93 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require File.dirname(__FILE__) + '/example_helper'
3
+
4
+ # NOTE: moqueue currently does not mimic AMQP's behavior of:
5
+ # 1) requiring graceful shutdown for acks to be delivered
6
+ # 2) returning messages to the queue if not acked
7
+ # 3) not processing messages when AMQP isn't "running"
8
+ #
9
+ # This causes the result of this test to differ from the actual result when run
10
+ # with a real broker. The true behavior should be that the 3rd message
11
+ # published should be unacknowledged and returned to the queue. In this test,
12
+ # all messages get acknowleged
13
+ describe Moqueue, "when running the ack example" do
14
+ include ExampleHelper
15
+
16
+ def run_ack_example(&perform_ack)
17
+ AMQP.start(:host => 'localhost') do
18
+ MQ.queue('awesome').publish('Totally rad 1')
19
+ MQ.queue('awesome').publish('Totally rad 2')
20
+ MQ.queue('awesome').publish('Totally rad 3')
21
+
22
+ i = 0
23
+
24
+ # Stopping after the second item was acked will keep the 3rd item in the queue
25
+ MQ.queue('awesome').subscribe(:ack => true) do |h,m|
26
+ if (i+=1) == 3
27
+ #puts 'Shutting down...'
28
+ AMQP.stop{ EM.stop }
29
+ end
30
+
31
+ if AMQP.closing?
32
+ #puts "#{m} (ignored, redelivered later)"
33
+ else
34
+ #puts "received message: " + m
35
+ perform_ack.call(h)
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ before(:all) do
43
+ overload_amqp
44
+ end
45
+
46
+ before(:each) do
47
+ reset_broker
48
+ reset!
49
+ end
50
+
51
+ it "should get the correct result without errors" do
52
+ Timeout::timeout(2) do
53
+ run_ack_example {|h| h.ack }
54
+ end
55
+ q = MQ.queue('awesome')
56
+ q.should have(3).acked_messages
57
+ q.received_ack_for_message?('Totally rad 1').should be_true
58
+ q.received_ack_for_message?('Totally rad 2').should be_true
59
+ q.received_ack_for_message?('Totally rad 3').should be_true
60
+ end
61
+
62
+ it "should be able to ack in an EM.next_tick" do
63
+ Timeout::timeout(2) do
64
+ run_ack_example do |h|
65
+ EM.next_tick { h.ack }
66
+ end
67
+ end
68
+ q = MQ.queue('awesome')
69
+ q.should have(3).acked_messages
70
+ q.received_ack_for_message?('Totally rad 1').should be_true
71
+ q.received_ack_for_message?('Totally rad 2').should be_true
72
+ q.received_ack_for_message?('Totally rad 3').should be_true
73
+ end
74
+
75
+ it "should be able to ack in an EM.defer callback" do
76
+ Timeout::timeout(2) do
77
+ run_ack_example do |h|
78
+ EM.defer(proc {
79
+ 1337
80
+ },
81
+ proc { |result|
82
+ result.should == 1337
83
+ h.ack
84
+ })
85
+ end
86
+ end
87
+ q = MQ.queue('awesome')
88
+ q.should have(3).acked_messages
89
+ q.received_ack_for_message?('Totally rad 1').should be_true
90
+ q.received_ack_for_message?('Totally rad 2').should be_true
91
+ q.received_ack_for_message?('Totally rad 3').should be_true
92
+ end
93
+ end
@@ -0,0 +1,101 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe "AMQP", "when mocked out by Moqueue" do
4
+
5
+ before(:each) do
6
+ reset_broker
7
+ end
8
+
9
+ it "should have direct exchanges" do
10
+ queue = mock_queue("direct-exchanges")
11
+ queue.publish("you are correct, sir!")
12
+ queue.subscribe { |message| "do something with message" }
13
+ queue.received_message?("you are correct, sir!").should be_true
14
+ end
15
+
16
+ it "should have direct exchanges with acks" do
17
+ queue = mock_queue("direct-with-acks")
18
+ queue.publish("yessir!")
19
+ queue.subscribe(:ack => true) { |headers, message| headers.ack }
20
+ queue.received_ack_for_message?("yessir!").should be_true
21
+ end
22
+
23
+ it "should have topic exchanges" do
24
+ topic = mock_exchange(:topic => "TATFT")
25
+ queue = mock_queue("rspec-fiend")
26
+ queue.bind(topic, :key => "bdd.*").subscribe { |msg| "do something" }
27
+ topic.publish("TATFT FTW", :key=> "bdd.4life")
28
+ queue.received_message?("TATFT FTW").should be_true
29
+ end
30
+
31
+ it "should have topic exchanges with acks" do
32
+ topic = mock_exchange(:topic => "animals")
33
+ queue = mock_queue("cat-lover")
34
+ queue.bind(topic, :key => "cats.#").subscribe(:ack => true) do |header, msg|
35
+ header.ack
36
+ "do something with message"
37
+ end
38
+ topic.publish("OMG kittehs!", :key => "cats.lolz.kittehs")
39
+ topic.received_ack_for_message?("OMG kittehs!").should be_true
40
+ end
41
+
42
+ it "should have fanout exchanges with acks" do
43
+ film = mock_exchange(:fanout => "Godfather")
44
+ one_actor = mock_queue("Jack Woltz")
45
+ other_actor = mock_queue("Captain McCluskey")
46
+ one_actor.bind(film).subscribe(:ack =>true) { |h,msg| h.ack && "horse head" }
47
+ other_actor.bind(film).subscribe(:ack => true) { |h,msg| h.ack && "dirty cops" }
48
+ offer = "you can't refuse"
49
+ film.publish(offer)
50
+ one_actor.received_message?(offer).should be_true
51
+ other_actor.received_message?(offer).should be_true
52
+ film.should have(2).acked_messages
53
+ end
54
+
55
+ end
56
+
57
+ describe Moqueue, "with syntax sugar" do
58
+
59
+ before(:each) do
60
+ reset_broker
61
+ end
62
+
63
+ it "counts received messages" do
64
+ queue = mock_queue
65
+ queue.subscribe { |msg| msg.should_not be_nil }
66
+ 5.times {queue.publish("no moar beers kthxbye")}
67
+ queue.should have(5).received_messages
68
+ end
69
+
70
+ it "counts acked messages" do
71
+ queue = mock_queue
72
+ queue.subscribe(:ack=>true) { |headers,msg| headers.ack }
73
+ 5.times { queue.publish("time becomes a loop") }
74
+ queue.should have(5).acked_messages
75
+ end
76
+
77
+ it "makes the callback (#subscribe) block testable" do
78
+ emphasis = mock_queue
79
+ emphasis.subscribe { |msg| @emphasized = "**" + msg + "**" }
80
+ emphasis.run_callback("show emphasis").should == "**show emphasis**"
81
+ end
82
+
83
+ end
84
+
85
+ describe Moqueue, "when using custom rspec matchers" do
86
+
87
+ it "should accept syntax like queue.should have_received('a message')" do
88
+ queue = mock_queue("sugary")
89
+ queue.subscribe { |msg| "eat the message" }
90
+ queue.publish("a message")
91
+ queue.should have_received("a message")
92
+ end
93
+
94
+ it "should accept syntax like queue_or_exchange.should have_ack_for('a message')" do
95
+ queue = mock_queue("more sugar")
96
+ queue.subscribe(:ack => true) { |headers, msg| headers.ack }
97
+ queue.publish("another message")
98
+ queue.should have_ack_for("another message")
99
+ end
100
+
101
+ end
@@ -0,0 +1,16 @@
1
+ module ExampleHelper
2
+ def capture_output(*args)
3
+ @captured_output << args
4
+ end
5
+
6
+ def counter
7
+ @counter += 1
8
+ EM.stop {AMQP.stop} if @counter >= 2
9
+ @counter
10
+ end
11
+
12
+ def reset!
13
+ @counter, @captured_output = 0, []
14
+ end
15
+
16
+ end
@@ -0,0 +1,159 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Moqueue, "when running the logger example" do
4
+
5
+ class Logger
6
+ def initialize *args, &block
7
+ opts = args.pop if args.last.is_a? Hash
8
+ opts ||= {}
9
+
10
+ printer(block) if block
11
+
12
+ @prop = opts
13
+ @tags = ([:timestamp] + args).uniq
14
+ end
15
+
16
+ attr_reader :prop
17
+ alias :base :prop
18
+
19
+ def log severity, *args
20
+ opts = args.pop if args.last.is_a? Hash and args.size != 1
21
+ opts ||= {}
22
+ opts = @prop.clone.update(opts)
23
+
24
+ data = args.shift
25
+
26
+ data = {:type => :exception,
27
+ :name => data.class.to_s.intern,
28
+ :backtrace => data.backtrace,
29
+ :message => data.message} if data.is_a? Exception
30
+
31
+ (@tags + args).each do |tag|
32
+ tag = tag.to_sym
33
+ case tag
34
+ when :timestamp
35
+ opts.update :timestamp => Time.now
36
+ when :hostname
37
+ @hostname ||= { :hostname => `hostname`.strip }
38
+ opts.update @hostname
39
+ when :process
40
+ @process_id ||= { :process_id => Process.pid,
41
+ :process_name => $0,
42
+ :process_parent_id => Process.ppid,
43
+ :thread_id => Thread.current.object_id }
44
+ opts.update :process => @process_id
45
+ else
46
+ (opts[:tags] ||= []) << tag
47
+ end
48
+ end
49
+
50
+ opts.update(:severity => severity,
51
+ :msg => data)
52
+
53
+ print(opts)
54
+ unless Logger.disabled?
55
+ MQ.fanout('logging', :durable => true).publish Marshal.dump(opts)
56
+ end
57
+
58
+ opts
59
+ end
60
+ alias :method_missing :log
61
+
62
+ def print data = nil, &block
63
+ if block
64
+ @printer = block
65
+ elsif data.is_a? Proc
66
+ @printer = data
67
+ elsif data
68
+ (pr = @printer || self.class.printer) and pr.call(data)
69
+ else
70
+ @printer
71
+ end
72
+ end
73
+ alias :printer :print
74
+
75
+ def self.printer &block
76
+ @printer = block if block
77
+ @printer
78
+ end
79
+
80
+ def self.disabled?
81
+ !!@disabled
82
+ end
83
+
84
+ def self.enable
85
+ @disabled = false
86
+ end
87
+
88
+ def self.disable
89
+ @disabled = true
90
+ end
91
+ end
92
+
93
+
94
+ before(:all) do
95
+ overload_amqp
96
+ end
97
+
98
+
99
+ def run_client
100
+ AMQP.start do
101
+ log = Logger.new
102
+ log.debug 'its working!'
103
+
104
+ log = Logger.new do |msg|
105
+ #require 'pp'
106
+ #pp msg
107
+ #puts
108
+ end
109
+
110
+ log.info '123'
111
+ log.debug [1,2,3]
112
+ log.debug :one => 1, :two => 2
113
+ log.error Exception.new('123')
114
+
115
+ log.info '123', :process_id => Process.pid
116
+ log.info '123', :process
117
+ log.debug 'login', :session => 'abc', :user => 123
118
+
119
+ log = Logger.new(:webserver, :timestamp, :hostname, &log.printer)
120
+ log.info 'Request for /', :GET, :session => 'abc'
121
+
122
+ #AMQP.stop{ EM.stop }
123
+ end
124
+ end
125
+
126
+ def run_server
127
+ AMQP.start(:host => 'localhost') do
128
+
129
+ @server_queue = MQ.queue('logger')
130
+ @server_queue.bind(MQ.fanout('logging', :durable => true)).subscribe do |msg|
131
+ msg = Marshal.load(msg)
132
+ end
133
+ end
134
+ end
135
+
136
+ it "should get the expected results" do
137
+ EM.run do
138
+ threads = []
139
+ threads << Thread.new do
140
+ run_server
141
+ end
142
+ threads << Thread.new do
143
+ run_client
144
+ end
145
+
146
+ EM.add_timer(0.1) do
147
+ @server_queue.should have(9).received_messages
148
+ webserver_log = Marshal.load(@server_queue.received_messages.last)
149
+ webserver_log[:tags].should == [:webserver, :GET]
150
+ webserver_log[:msg].should == "Request for /"
151
+
152
+ EM.stop
153
+ threads.each { |t| t.join }
154
+ end
155
+
156
+ end
157
+ end
158
+
159
+ end
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require File.dirname(__FILE__) + '/example_helper'
3
+
4
+ describe Moqueue, "when testing the ping pong example" do
5
+ include ExampleHelper
6
+
7
+ def ping_pong
8
+ AMQP.start(:host => 'localhost') do
9
+
10
+ # AMQP.logging = true
11
+
12
+ amq = MQ.new
13
+ EM.add_periodic_timer(0.1){
14
+ @counter_val = counter
15
+ capture_output @counter_val, :sending, 'ping'
16
+ amq.queue('one').publish('ping')
17
+ }
18
+
19
+ amq = MQ.new
20
+ amq.queue('one').subscribe{ |msg|
21
+ capture_output @counter_val, 'one', :received, msg, :sending, 'pong'
22
+ amq.queue('two').publish('pong')
23
+ }
24
+
25
+ amq = MQ.new
26
+ amq.queue('two').subscribe{ |msg|
27
+ capture_output @counter_val, 'two', :received, msg
28
+ }
29
+
30
+ end
31
+
32
+ end
33
+
34
+ before(:all) do
35
+ overload_amqp
36
+ end
37
+
38
+ before(:each) do
39
+ reset!
40
+ end
41
+
42
+ it "should get the correct result without error" do
43
+ Timeout::timeout(5) do
44
+ ping_pong
45
+ end
46
+ expected = [[1, :sending, "ping"], [1, "one", :received, "ping", :sending, "pong"], [1, "two", :received, "pong"],
47
+ [2, :sending, "ping"], [2, "one", :received, "ping", :sending, "pong"], [2, "two", :received, "pong"]]
48
+ @captured_output.should == expected
49
+ end
50
+ end
@@ -0,0 +1,64 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require File.dirname(__FILE__) + '/example_helper'
3
+
4
+ describe Moqueue, "when running the stocks example" do
5
+ include ExampleHelper
6
+
7
+ def run_stocks
8
+ AMQP.start(:host => 'localhost') do
9
+
10
+ def log *args
11
+ #p [ Time.now, *args ]
12
+ end
13
+
14
+ def publish_stock_prices
15
+ mq = MQ.new
16
+ counter = 0
17
+ EM.add_periodic_timer(0.1){
18
+ counter += 1
19
+ EM.stop if counter > 5
20
+
21
+ {:appl => 170+rand(1000)/100.0, :msft => 22+rand(500)/100.0}.each do |stock, price|
22
+ stock = "usd.#{stock}"
23
+
24
+ log :publishing, stock, price
25
+ mq.topic('stocks').publish(price, :key => stock)
26
+ end
27
+ }
28
+ end
29
+
30
+ def watch_appl_stock
31
+ mq = MQ.new
32
+ @apple_queue = mq.queue('apple stock')
33
+ @apple_queue.bind(mq.topic('stocks'), :key => 'usd.appl').subscribe{ |price|
34
+ log 'apple stock', price
35
+ }
36
+ end
37
+
38
+ def watch_us_stocks
39
+ mq = MQ.new
40
+ @us_stocks = mq.queue('us stocks')
41
+ @us_stocks.bind(mq.topic('stocks'), :key => 'usd.*').subscribe{ |info, price|
42
+ log 'us stock', info.routing_key, price
43
+ }
44
+ end
45
+
46
+ publish_stock_prices
47
+ watch_appl_stock
48
+ watch_us_stocks
49
+
50
+ end
51
+ end
52
+
53
+ before(:each) do
54
+ overload_amqp
55
+ reset_broker
56
+ end
57
+
58
+ it "should get the correct results" do
59
+ run_stocks
60
+ @us_stocks.should have(12).received_messages
61
+ @apple_queue.should have(6).received_messages
62
+ end
63
+
64
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ -c -f specdoc
@@ -0,0 +1,21 @@
1
+ require "rubygems"
2
+ require "spec"
3
+
4
+ Spec::Runner.configure do |config|
5
+ config.mock_with :mocha
6
+ end
7
+
8
+ require File.dirname(__FILE__) + "/../lib/moqueue"
9
+
10
+ # Make sure tests fail if deferred blocks (for susbscribe and pop) don't get called
11
+ def ensure_deferred_block_called(opts={:times=>1})
12
+ @poke_me = mock("poke_me")
13
+ @poke_me.expects(:deferred_block_called).times(opts[:times])
14
+ end
15
+
16
+ def deferred_block_called
17
+ @poke_me.deferred_block_called
18
+ true
19
+ end
20
+
21
+ include Moqueue
@@ -0,0 +1,92 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Matchers do
4
+ class MatcherHarness
5
+ include Moqueue::Matchers
6
+ end
7
+
8
+ before(:each) do
9
+ @matchable = MatcherHarness.new
10
+ @mock_moqueue = mock("mock Moqueue::MockQueue")
11
+ @failure_exception = Spec::Expectations::ExpectationNotMetError
12
+ end
13
+
14
+ it "should include matchers in describe blocks automatically when using rspec" do
15
+ self.class.include?(Moqueue::Matchers).should be_true
16
+ end
17
+
18
+ it "should implement Object#should have_received_message" do
19
+ @mock_moqueue.expects(:received_message?).with("matchtacular").returns(true)
20
+ @mock_moqueue.should have_received_message("matchtacular")
21
+ end
22
+
23
+ it "should implement Object#should_not have_received_message" do
24
+ @mock_moqueue.expects(:received_message?).with("no match").returns(false)
25
+ @mock_moqueue.should_not have_received_message("no match")
26
+ end
27
+
28
+ it "should have a useful failure message" do
29
+ @mock_moqueue.expects(:received_message?).with("this fails").returns(false)
30
+ failing_example = lambda {@mock_moqueue.should have_received_message("this fails")}
31
+ error_message = "expected #{@mock_moqueue.inspect} to have received message ``this fails''"
32
+ failing_example.should raise_error(@failure_exception, error_message)
33
+ end
34
+
35
+ it "should have a useful negative failure message" do
36
+ @mock_moqueue.expects(:received_message?).with("FAIL").returns(true)
37
+ failing_example = lambda{@mock_moqueue.should_not have_received_message("FAIL")}
38
+ error_message = "expected #{@mock_moqueue.inspect} to not have received message ``FAIL''"
39
+ failing_example.should raise_error(@failure_exception, error_message)
40
+ end
41
+
42
+ it "should fail gracefully if object being tested for #have_received doesn't respond to #received_message?" do
43
+ begin
44
+ Object.new.should have_received_message("foo")
45
+ rescue => e
46
+ end
47
+ e.should be_a NoMethodError
48
+ e.message.should match /you can't use \`\`should have_received_message\'\' on #\<Object/
49
+ end
50
+
51
+ it "should alias #have_received_message as #have_received for less verbosity" do
52
+ @matchable.should respond_to(:have_received)
53
+ end
54
+
55
+ it "should alias #have_received_ack_for as #have_ack_for for less verbosity" do
56
+ @matchable.should respond_to(:have_ack_for)
57
+ end
58
+
59
+ it "should implement Object#should have_received_ack_for(msg_text)" do
60
+ @mock_moqueue.expects(:received_ack_for_message?).with("foo bar").returns(true)
61
+ @mock_moqueue.should have_received_ack_for("foo bar")
62
+ end
63
+
64
+ it "should implement Object#should_not have_received_ack_for(msg_text)" do
65
+ @mock_moqueue.expects(:received_ack_for_message?).with("bar baz").returns(false)
66
+ @mock_moqueue.should_not have_received_ack_for("bar baz")
67
+ end
68
+
69
+ it "should have a helpful failure message" do
70
+ @mock_moqueue.expects(:received_ack_for_message?).with("foo baz").returns(false)
71
+ failure = lambda {@mock_moqueue.should have_received_ack_for("foo baz")}
72
+ fail_msg = "expected #{@mock_moqueue.inspect} to have received an ack for the message ``foo baz''"
73
+ failure.should raise_error(@failure_exception, fail_msg)
74
+ end
75
+
76
+ it "should have a helpful negative failure message" do
77
+ @mock_moqueue.expects(:received_ack_for_message?).with("bar foo").returns(true)
78
+ failure = lambda {@mock_moqueue.should_not have_received_ack_for("bar foo")}
79
+ fail_msg = "expected #{@mock_moqueue.inspect} to not have received an ack for the message ``bar foo''"
80
+ failure.should raise_error(@failure_exception, fail_msg)
81
+ end
82
+
83
+ it "should fail gracefully if object being tested for #have_received_ack_for doesn't respond to #received_ack_for_message?" do
84
+ begin
85
+ Object.new.should have_received_message("foo")
86
+ rescue => e
87
+ end
88
+ e.should be_a NoMethodError
89
+ e.message.should match /you can't use \`\`should have_received_message\'\' on #\<Object/
90
+ end
91
+
92
+ end
@@ -0,0 +1,34 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe MockBroker do
4
+
5
+ before(:each) do
6
+ @broker = MockBroker.instance
7
+ @broker.reset!
8
+ end
9
+
10
+ it "should keep a list of named queues" do
11
+ queue = MockQueue.new("one-mocked-queue")
12
+ @broker.register_queue(queue)
13
+ @broker.find_queue("one-mocked-queue").should == queue
14
+ end
15
+
16
+ it "should reset itself" do
17
+ @broker.register_queue(MockQueue.new("throwaway"))
18
+ @broker.reset!
19
+ @broker.registered_queues.size.should == 0
20
+ end
21
+
22
+ it "should keep a list of topic exchanges" do
23
+ exchange = MockExchange.new(:topic => "lolcats")
24
+ @broker.register_topic_exchange(exchange)
25
+ @broker.find_topic_exchange("lolcats").should equal(exchange)
26
+ end
27
+
28
+ it "should keep a list of fanout queues" do
29
+ exchange = MockExchange.new(:fanout => "joinTheNaniteBorg")
30
+ @broker.register_fanout_exchange(exchange)
31
+ @broker.find_fanout_exchange("joinTheNaniteBorg").should equal(exchange)
32
+ end
33
+
34
+ end