pivotal-moqueue 0.1.0.200907241602

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ module Moqueue
2
+
3
+ module ObjectMethods
4
+ def mock_queue_and_exchange(name=nil)
5
+ queue = mock_queue(name)
6
+ exchange = mock_exchange
7
+ exchange.attached_queues << queue
8
+ [queue, exchange]
9
+ end
10
+
11
+ def mock_queue(name=nil)
12
+ MockQueue.new(name || "anonymous-#{rand(2**32).to_s(16)}")
13
+ end
14
+
15
+ def mock_exchange(opts={})
16
+ MockExchange.new(opts)
17
+ end
18
+
19
+ def overload_amqp
20
+ require MOQUEUE_ROOT + "moqueue/overloads"
21
+ end
22
+
23
+ def reset_broker
24
+ MockBroker.instance.reset!
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ Object.send(:include, Moqueue::ObjectMethods)
@@ -0,0 +1,46 @@
1
+ require "eventmachine"
2
+
3
+ class MQ
4
+
5
+ class << self
6
+ def queue(name)
7
+ Moqueue::MockQueue.new(name)
8
+ end
9
+
10
+ def fanout(name, opts={})
11
+ Moqueue::MockExchange.new(opts.merge(:fanout=>name))
12
+ end
13
+
14
+ end
15
+
16
+ def initialize(*args)
17
+ end
18
+
19
+ def queue(name, opts = {})
20
+ Moqueue::MockQueue.new(name)
21
+ end
22
+
23
+ def topic(topic_name)
24
+ Moqueue::MockExchange.new(:topic=>topic_name)
25
+ end
26
+
27
+ end
28
+
29
+ module AMQP
30
+
31
+ class << self
32
+ attr_reader :closing
33
+ alias :closing? :closing
34
+ end
35
+
36
+ def self.start(opts={},&block)
37
+ EM.run(&block)
38
+ end
39
+
40
+ def self.stop
41
+ @closing = true
42
+ yield if block_given?
43
+ @closing = false
44
+ end
45
+
46
+ end
data/moqueue.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{moqueue}
5
+ s.version = "0.1.0.200907241602"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Daniel DeLeo"]
9
+ s.date = %q{2009-07-09}
10
+ s.description = %q{Mocktacular Companion to AMQP Library. Happy TATFTing!}
11
+ s.email = %q{dan@kallistec.com}
12
+ s.extra_rdoc_files = ["README.rdoc"]
13
+ s.files = ["lib", "moqueue.gemspec", "Rakefile", "README.rdoc", "spec", "VERSION.yml", "lib/moqueue", "lib/moqueue/fibers18.rb", "lib/moqueue/matchers.rb", "lib/moqueue/mock_broker.rb", "lib/moqueue/mock_exchange.rb", "lib/moqueue/mock_headers.rb", "lib/moqueue/mock_queue.rb", "lib/moqueue/object_methods.rb", "lib/moqueue/overloads.rb", "lib/moqueue.rb", "spec/examples", "spec/examples/ack_spec.rb", "spec/examples/basic_usage_spec.rb", "spec/examples/example_helper.rb", "spec/examples/logger_spec.rb", "spec/examples/ping_pong_spec.rb", "spec/examples/stocks_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "spec/unit", "spec/unit/matchers_spec.rb", "spec/unit/mock_broker_spec.rb", "spec/unit/mock_exchange_spec.rb", "spec/unit/mock_headers_spec.rb", "spec/unit/mock_queue_spec.rb", "spec/unit/moqueue_spec.rb", "spec/unit/object_methods_spec.rb", "spec/unit/overloads_spec.rb"]
14
+ s.homepage = %q{http://github.com/danielsdeleo/moqueue}
15
+ s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
16
+ s.require_paths = ["lib"]
17
+ s.rubygems_version = %q{1.3.3}
18
+ s.summary = %q{Mocktacular Companion to AMQP Library. Happy TATFTing!}
19
+
20
+ if s.respond_to? :specification_version then
21
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
22
+ s.specification_version = 3
23
+
24
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
25
+ else
26
+ end
27
+ else
28
+ end
29
+ end
@@ -0,0 +1,60 @@
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
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
+ h.ack
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
54
+ end
55
+ q = MQ.queue('awesome')
56
+ q.received_ack_for_message?('Totally rad 1').should be_true
57
+ q.received_ack_for_message?('Totally rad 2').should be_true
58
+ q.received_ack_for_message?('Totally rad 3').should be_true
59
+ end
60
+ 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