vx-common-amqp 0.2.6
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/.travis.yml +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +276 -0
- data/README.md +29 -0
- data/Rakefile +6 -0
- data/bin/vx-consumers +12 -0
- data/lib/vx/common/amqp/cli.rb +88 -0
- data/lib/vx/common/amqp/config.rb +74 -0
- data/lib/vx/common/amqp/consumer/ack.rb +19 -0
- data/lib/vx/common/amqp/consumer/configuration.rb +119 -0
- data/lib/vx/common/amqp/consumer/publish.rb +32 -0
- data/lib/vx/common/amqp/consumer/subscribe.rb +67 -0
- data/lib/vx/common/amqp/consumer.rb +70 -0
- data/lib/vx/common/amqp/formatter.rb +105 -0
- data/lib/vx/common/amqp/mixins/callbacks.rb +35 -0
- data/lib/vx/common/amqp/mixins/logger.rb +17 -0
- data/lib/vx/common/amqp/session.rb +154 -0
- data/lib/vx/common/amqp/supervisor/threaded.rb +171 -0
- data/lib/vx/common/amqp/testing.rb +54 -0
- data/lib/vx/common/amqp/version.rb +7 -0
- data/lib/vx/common/amqp.rb +68 -0
- data/spec/integration/multi_threaded_spec.rb +89 -0
- data/spec/integration/threaded_supervisor_spec.rb +85 -0
- data/spec/lib/amqp/config_spec.rb +32 -0
- data/spec/lib/amqp/consumer_spec.rb +316 -0
- data/spec/lib/amqp/formatter_spec.rb +47 -0
- data/spec/lib/amqp/mixins/callbacks_spec.rb +26 -0
- data/spec/lib/amqp/session_spec.rb +144 -0
- data/spec/lib/amqp/supervisor/threaded_spec.rb +124 -0
- data/spec/lib/amqp_spec.rb +9 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/amqp.rb +15 -0
- data/spec/support/ignore_me_error.rb +1 -0
- data/vx-common-amqp.gemspec +30 -0
- metadata +178 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.expand_path("../amqp/version", __FILE__)
|
2
|
+
|
3
|
+
module Vx
|
4
|
+
module Common
|
5
|
+
module AMQP
|
6
|
+
|
7
|
+
autoload :Config, File.expand_path("../amqp/config", __FILE__)
|
8
|
+
autoload :Session, File.expand_path("../amqp/session", __FILE__)
|
9
|
+
autoload :Consumer, File.expand_path("../amqp/consumer", __FILE__)
|
10
|
+
autoload :CLI, File.expand_path("../amqp/cli", __FILE__)
|
11
|
+
autoload :Formatter, File.expand_path("../amqp/formatter", __FILE__)
|
12
|
+
|
13
|
+
module Supervisor
|
14
|
+
autoload :Threaded, File.expand_path("../amqp/supervisor/threaded", __FILE__)
|
15
|
+
end
|
16
|
+
|
17
|
+
autoload :Logger, File.expand_path("../amqp/mixins/logger", __FILE__)
|
18
|
+
autoload :Callbacks, File.expand_path("../amqp/mixins/callbacks", __FILE__)
|
19
|
+
|
20
|
+
extend self
|
21
|
+
|
22
|
+
@@config = Common::AMQP::Config.new
|
23
|
+
@@session = Common::AMQP::Session.new
|
24
|
+
|
25
|
+
def configure
|
26
|
+
yield config
|
27
|
+
end
|
28
|
+
|
29
|
+
def config
|
30
|
+
@@config
|
31
|
+
end
|
32
|
+
|
33
|
+
def session
|
34
|
+
@@session
|
35
|
+
end
|
36
|
+
|
37
|
+
def open
|
38
|
+
session.open
|
39
|
+
end
|
40
|
+
|
41
|
+
def open?
|
42
|
+
session.open?
|
43
|
+
end
|
44
|
+
|
45
|
+
def close
|
46
|
+
session.close
|
47
|
+
end
|
48
|
+
|
49
|
+
def logger
|
50
|
+
config.logger
|
51
|
+
end
|
52
|
+
|
53
|
+
def logger=(val)
|
54
|
+
config.logger = val
|
55
|
+
end
|
56
|
+
|
57
|
+
def shutdown
|
58
|
+
Common::AMQP::Session.shutdown
|
59
|
+
Vx::Common::AMQP::Supervisor::Threaded.shutdown
|
60
|
+
end
|
61
|
+
|
62
|
+
def shutdown?
|
63
|
+
Common::AMQP::Session.shutdown?
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'thread'
|
3
|
+
require 'timeout'
|
4
|
+
|
5
|
+
class Vx::BobThread
|
6
|
+
include Vx::Common::AMQP::Consumer
|
7
|
+
|
8
|
+
queue exclusive: true, durable: false
|
9
|
+
exchange auto_delete: true, durable: false
|
10
|
+
ack true
|
11
|
+
|
12
|
+
def perform(payload)
|
13
|
+
$mtest_mutex.synchronize do
|
14
|
+
$mtest_collected << payload
|
15
|
+
ack!
|
16
|
+
sleep 0.1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Vx::AliceThread
|
22
|
+
include Vx::Common::AMQP::Consumer
|
23
|
+
|
24
|
+
queue exclusive: true, durable: false
|
25
|
+
exchange auto_delete: true, durable: false
|
26
|
+
ack true
|
27
|
+
|
28
|
+
def perform(payload)
|
29
|
+
Vx::BobThread.publish payload, content_type: properties[:content_type]
|
30
|
+
ack!
|
31
|
+
sleep 0.1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "Run in multithread environment", slow: true, jruby: true do
|
36
|
+
let(:num_messages) { 100 }
|
37
|
+
let(:alice) { Vx::AliceThread }
|
38
|
+
let(:bob) { Vx::BobThread }
|
39
|
+
let(:sess) { Vx::Common::AMQP.open }
|
40
|
+
let(:ch) { sess.conn.create_channel }
|
41
|
+
|
42
|
+
before do
|
43
|
+
$mtest_mutex = Mutex.new
|
44
|
+
$mtest_collected = []
|
45
|
+
end
|
46
|
+
|
47
|
+
after do
|
48
|
+
sess.close
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should be successfuly" do
|
52
|
+
ths = (0..12).map do |i|
|
53
|
+
klass = (i % 2 == 0) ? alice : bob
|
54
|
+
Thread.new do
|
55
|
+
begin
|
56
|
+
klass.subscribe
|
57
|
+
rescue Exception => e
|
58
|
+
puts "ERROR: #{e.inspect}"
|
59
|
+
raise e
|
60
|
+
end
|
61
|
+
end.tap do |th|
|
62
|
+
th.abort_on_exception = true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
sleep 0.5
|
66
|
+
|
67
|
+
num_messages.times do |n|
|
68
|
+
alice.publish "n#{n}", content_type: "text/plain"
|
69
|
+
end
|
70
|
+
|
71
|
+
Timeout.timeout(60) do
|
72
|
+
loop do
|
73
|
+
stop = false
|
74
|
+
$mtest_mutex.synchronize do
|
75
|
+
puts $mtest_collected.size
|
76
|
+
stop = true if $mtest_collected.size >= num_messages
|
77
|
+
end
|
78
|
+
break if stop
|
79
|
+
sleep 2
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
Vx::Common::AMQP.shutdown
|
84
|
+
Timeout.timeout(10) { ths.map{|i| i.join } }
|
85
|
+
|
86
|
+
expect($mtest_collected.sort).to eq (0...num_messages).map{|i| "n#{i}" }.sort
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'thread'
|
3
|
+
require 'timeout'
|
4
|
+
|
5
|
+
class Vx::BobThreadWithSupervisor
|
6
|
+
include Vx::Common::AMQP::Consumer
|
7
|
+
|
8
|
+
class ErrorSimulation < ::Exception ; end
|
9
|
+
|
10
|
+
queue exclusive: true, durable: false
|
11
|
+
exchange auto_delete: true, durable: false
|
12
|
+
ack true
|
13
|
+
|
14
|
+
def perform(payload)
|
15
|
+
$mtest_mutex.synchronize do
|
16
|
+
raise IgnoreMeError if Random.new(delivery_info.delivery_tag.to_i).rand < 0.2
|
17
|
+
$mtest_collected << payload
|
18
|
+
ack!
|
19
|
+
sleep 0.1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Vx::AliceThreadWithSupervisor
|
25
|
+
include Vx::Common::AMQP::Consumer
|
26
|
+
|
27
|
+
queue exclusive: true, durable: false
|
28
|
+
exchange auto_delete: true, durable: false
|
29
|
+
ack true
|
30
|
+
|
31
|
+
def perform(payload)
|
32
|
+
Vx::BobThreadWithSupervisor.publish payload, content_type: properties[:content_type]
|
33
|
+
ack!
|
34
|
+
sleep 0.1
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "Run in multithread environment", slow: true, jruby: true do
|
39
|
+
let(:num_messages) { 100 }
|
40
|
+
let(:alice) { Vx::AliceThreadWithSupervisor }
|
41
|
+
let(:bob) { Vx::BobThreadWithSupervisor }
|
42
|
+
let(:sess) { Vx::Common::AMQP.open }
|
43
|
+
let(:ch) { sess.conn.create_channel }
|
44
|
+
|
45
|
+
before do
|
46
|
+
$mtest_mutex = Mutex.new
|
47
|
+
$mtest_collected = []
|
48
|
+
end
|
49
|
+
|
50
|
+
after do
|
51
|
+
sess.close
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should be successfuly" do
|
55
|
+
|
56
|
+
supervisor = Vx::Common::AMQP::Supervisor::Threaded.build alice => 6, bob => 6
|
57
|
+
|
58
|
+
supervisor_thread = supervisor.run_async
|
59
|
+
|
60
|
+
sleep 0.5
|
61
|
+
|
62
|
+
num_messages.times do |n|
|
63
|
+
alice.publish "n#{n}", content_type: "text/plain"
|
64
|
+
end
|
65
|
+
|
66
|
+
Timeout.timeout(60) do
|
67
|
+
loop do
|
68
|
+
stop = false
|
69
|
+
$mtest_mutex.synchronize do
|
70
|
+
puts $mtest_collected.size
|
71
|
+
stop = true if $mtest_collected.size >= num_messages
|
72
|
+
end
|
73
|
+
break if stop
|
74
|
+
sleep 2
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
Vx::Common::AMQP.shutdown
|
79
|
+
supervisor.shutdown
|
80
|
+
Timeout.timeout(10) { supervisor_thread.join }
|
81
|
+
|
82
|
+
expect($mtest_collected.sort).to eq (0...num_messages).map{|i| "n#{i}" }.sort
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Vx::Common::AMQP::Config do
|
4
|
+
let(:config) { described_class.new }
|
5
|
+
|
6
|
+
context '(callbacks)' do
|
7
|
+
%w{ before after }.each do |p|
|
8
|
+
%w{ subscribe recieve publish }.each do |m|
|
9
|
+
name = "#{p}_#{m}"
|
10
|
+
it name do
|
11
|
+
config.public_send name do |value|
|
12
|
+
value
|
13
|
+
end
|
14
|
+
|
15
|
+
val = config.callbacks[name.to_sym].call("value")
|
16
|
+
expect(val).to eq 'value'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "on_error" do
|
22
|
+
it "should be success" do
|
23
|
+
config.on_error do |e|
|
24
|
+
e
|
25
|
+
end
|
26
|
+
val = config.callbacks[:on_error].call "value"
|
27
|
+
expect(val).to eq 'value'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,316 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'timeout'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
class Vx::TestConsumer
|
6
|
+
include Vx::Common::AMQP::Consumer
|
7
|
+
|
8
|
+
ack true
|
9
|
+
|
10
|
+
def perform(payload)
|
11
|
+
Thread.current[:collected] ||= []
|
12
|
+
Thread.current[:collected] << payload
|
13
|
+
ack!
|
14
|
+
|
15
|
+
:shutdown if Thread.current[:collected].size == 3
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe Vx::Common::AMQP::Consumer do
|
20
|
+
|
21
|
+
let(:consumer) { Vx::TestConsumer.new }
|
22
|
+
let(:consumer_class) { consumer.class }
|
23
|
+
|
24
|
+
subject { consumer }
|
25
|
+
|
26
|
+
before { consumer_class.reset_consumer_configuration! }
|
27
|
+
|
28
|
+
context '(configuration)' do
|
29
|
+
|
30
|
+
subject { consumer_class }
|
31
|
+
|
32
|
+
its(:config) { should be_an_instance_of(Vx::Common::AMQP::Config) }
|
33
|
+
its(:consumer_name) { should eq 'test_consumer' }
|
34
|
+
|
35
|
+
context "consumer_id" do
|
36
|
+
|
37
|
+
it "should be success" do
|
38
|
+
expect(consumer_class.consumer_id).to eq 'test_consumer'
|
39
|
+
th = Thread.new do
|
40
|
+
Thread.current[:vx_amqp_consumer_id] = '99'
|
41
|
+
consumer_class.consumer_id
|
42
|
+
end
|
43
|
+
expect(th.value).to eq 'test_consumer.99'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
context "model" do
|
49
|
+
subject { consumer_class.model }
|
50
|
+
|
51
|
+
it "by default should be nil" do
|
52
|
+
expect(subject).to be_nil
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'when set model should be' do
|
56
|
+
consumer_class.model Hash
|
57
|
+
expect(subject).to eq Hash
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "content_type" do
|
62
|
+
|
63
|
+
subject { consumer_class.content_type }
|
64
|
+
|
65
|
+
it "by default should be nil" do
|
66
|
+
expect(subject).to be_nil
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'when set content type should be' do
|
70
|
+
consumer_class.content_type 'foo'
|
71
|
+
expect(subject).to eq 'foo'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "bind_options" do
|
76
|
+
subject { consumer_class.bind_options }
|
77
|
+
|
78
|
+
context "by default should eq {}" do
|
79
|
+
it { should eq ({}) }
|
80
|
+
end
|
81
|
+
|
82
|
+
context "set routing_key" do
|
83
|
+
before { consumer_class.routing_key 'key' }
|
84
|
+
it { should eq(routing_key: 'key') }
|
85
|
+
end
|
86
|
+
|
87
|
+
context "set routing_key by block" do
|
88
|
+
before do
|
89
|
+
consumer_class.routing_key { 'key.block' }
|
90
|
+
end
|
91
|
+
it { should eq(routing_key: 'key.block') }
|
92
|
+
end
|
93
|
+
|
94
|
+
context "set headers" do
|
95
|
+
before { consumer_class.headers 'key' }
|
96
|
+
it { should eq({headers: 'key'}) }
|
97
|
+
end
|
98
|
+
|
99
|
+
context "set headers by block" do
|
100
|
+
before { consumer_class.headers { 'key.block' } }
|
101
|
+
it { should eq(headers: 'key.block') }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "ack" do
|
106
|
+
subject { consumer_class.ack }
|
107
|
+
|
108
|
+
it "by default should be false" do
|
109
|
+
expect(subject).to be_false
|
110
|
+
end
|
111
|
+
|
112
|
+
it "when set to true should be true" do
|
113
|
+
consumer_class.ack true
|
114
|
+
expect(subject).to be_true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "exchange_name" do
|
119
|
+
subject { consumer_class.exchange_name }
|
120
|
+
|
121
|
+
it 'by default should eq consumer_name' do
|
122
|
+
expect(subject).to eq consumer_class.consumer_name
|
123
|
+
end
|
124
|
+
|
125
|
+
it "when set name should be" do
|
126
|
+
consumer_class.exchange :foo
|
127
|
+
expect(subject).to eq :foo
|
128
|
+
end
|
129
|
+
|
130
|
+
it "when set by block should be" do
|
131
|
+
consumer_class.exchange { 'name.block' }
|
132
|
+
expect(subject).to eq 'name.block'
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context "queue_name" do
|
137
|
+
subject{ consumer_class.queue_name }
|
138
|
+
it 'by default should eq consumer_name' do
|
139
|
+
expect(subject).to eq consumer_class.consumer_name
|
140
|
+
end
|
141
|
+
|
142
|
+
it "when set name should be" do
|
143
|
+
consumer_class.queue :bar
|
144
|
+
expect(subject).to eq :bar
|
145
|
+
end
|
146
|
+
|
147
|
+
it "when set by block should be" do
|
148
|
+
consumer_class.queue { 'name.block' }
|
149
|
+
expect(subject).to eq 'name.block'
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
%w{ queue exchange }.each do |m|
|
154
|
+
context "#{m}_options" do
|
155
|
+
subject { consumer_class.send "#{m}_options" }
|
156
|
+
it 'by default should eq {}' do
|
157
|
+
expect(subject).to eq({})
|
158
|
+
end
|
159
|
+
|
160
|
+
it "when set #{m} options should be" do
|
161
|
+
consumer_class.send(m, durable: true)
|
162
|
+
expect(subject).to eq(durable: true)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
%w{ routing_key headers }.each do |m|
|
168
|
+
context m do
|
169
|
+
subject { consumer_class.send m }
|
170
|
+
|
171
|
+
it 'by default should be nil' do
|
172
|
+
expect(subject).to be_nil
|
173
|
+
end
|
174
|
+
|
175
|
+
it "when set #{m} should be" do
|
176
|
+
consumer_class.send(m, key: :value)
|
177
|
+
expect(subject).to eq(key: :value)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
context "(publish)" do
|
184
|
+
|
185
|
+
context "options" do
|
186
|
+
let(:message) { {"foo" => 1, "bar" => 2} }
|
187
|
+
let(:expected_options) { {} }
|
188
|
+
let(:options) { {} }
|
189
|
+
let(:x) { OpenStruct.new name: "name" }
|
190
|
+
|
191
|
+
subject{ consumer_class.publish message, options }
|
192
|
+
|
193
|
+
before do
|
194
|
+
mock(consumer_class).declare_exchange { x }
|
195
|
+
mock(x).publish(message.to_json, expected_options)
|
196
|
+
end
|
197
|
+
|
198
|
+
context "routing_key" do
|
199
|
+
context "by default" do
|
200
|
+
it { should be }
|
201
|
+
end
|
202
|
+
|
203
|
+
context "when exists in configuration" do
|
204
|
+
let(:expected_options) { { routing_key: 'routing.key' } }
|
205
|
+
before do
|
206
|
+
consumer_class.routing_key 'routing.key'
|
207
|
+
end
|
208
|
+
it { should be }
|
209
|
+
end
|
210
|
+
|
211
|
+
context "when exists in options" do
|
212
|
+
let(:expected_options) { { routing_key: 'routing.key' } }
|
213
|
+
let(:options) { { routing_key: 'routing.key' } }
|
214
|
+
it { should be }
|
215
|
+
end
|
216
|
+
|
217
|
+
context "when exists in options and configuration" do
|
218
|
+
let(:expected_options) { { routing_key: 'options.key' } }
|
219
|
+
let(:options) { { routing_key: 'options.key' } }
|
220
|
+
before do
|
221
|
+
consumer_class.routing_key 'configuration.key'
|
222
|
+
end
|
223
|
+
it { should be }
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
context "headers" do
|
228
|
+
context "by default" do
|
229
|
+
it { should be }
|
230
|
+
end
|
231
|
+
|
232
|
+
context "when exists in configuration" do
|
233
|
+
let(:expected_options) { { headers: 'key' } }
|
234
|
+
before do
|
235
|
+
consumer_class.headers 'key'
|
236
|
+
end
|
237
|
+
it { should be }
|
238
|
+
end
|
239
|
+
|
240
|
+
context "when exists in options" do
|
241
|
+
let(:expected_options) { { headers: 'key' } }
|
242
|
+
let(:options) { { headers: 'key' } }
|
243
|
+
it { should be }
|
244
|
+
end
|
245
|
+
|
246
|
+
context "when exists in options and configuration" do
|
247
|
+
let(:expected_options) { { headers: 'options' } }
|
248
|
+
let(:options) { { headers: 'options' } }
|
249
|
+
before do
|
250
|
+
consumer_class.headers 'configuration'
|
251
|
+
end
|
252
|
+
it { should be }
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context "real run" do
|
258
|
+
let(:x_name) { consumer_class.exchange_name }
|
259
|
+
let(:q_name) { consumer_class.queue_name }
|
260
|
+
let(:sess) { consumer_class.session.open }
|
261
|
+
let(:ch) { sess.conn.create_channel }
|
262
|
+
let(:q) { sess.declare_queue q_name, channel: ch }
|
263
|
+
let(:x) { sess.declare_exchange x_name, channel: ch }
|
264
|
+
let(:message) { { 'key' => 'value' } }
|
265
|
+
|
266
|
+
after do
|
267
|
+
delete_queue q
|
268
|
+
delete_exchange x
|
269
|
+
sess.close
|
270
|
+
end
|
271
|
+
|
272
|
+
before do
|
273
|
+
q.bind x
|
274
|
+
end
|
275
|
+
|
276
|
+
it "should publish message to exchange using settings from consumer" do
|
277
|
+
consumer_class.publish message
|
278
|
+
sleep 0.25
|
279
|
+
expect(q.message_count).to eq 1
|
280
|
+
_, _, expected = q.pop
|
281
|
+
expect(expected).to eq message.to_json
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
context '(subscribe)' do
|
287
|
+
let(:x_name) { consumer_class.exchange_name }
|
288
|
+
let(:q_name) { consumer_class.queue_name }
|
289
|
+
let(:sess) { consumer_class.session.open }
|
290
|
+
let(:ch) { sess.conn.create_channel }
|
291
|
+
let(:q) { sess.declare_queue q_name, channel: ch }
|
292
|
+
let(:x) { sess.declare_exchange x_name, channel: ch }
|
293
|
+
|
294
|
+
after do
|
295
|
+
delete_queue q
|
296
|
+
delete_exchange x
|
297
|
+
sess.close
|
298
|
+
end
|
299
|
+
|
300
|
+
before do
|
301
|
+
consumer_class.ack true
|
302
|
+
q.bind(x)
|
303
|
+
3.times { |n| x.publish({"n" => n}.to_json, content_type: "application/json") }
|
304
|
+
end
|
305
|
+
|
306
|
+
subject { Thread.current[:collected] }
|
307
|
+
|
308
|
+
it "should receive messages" do
|
309
|
+
Timeout.timeout(3) do
|
310
|
+
consumer_class.subscribe
|
311
|
+
end
|
312
|
+
expect(subject).to have(3).items
|
313
|
+
expect(subject.map(&:values).flatten).to eq [0,1,2]
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class FormatterConsumerTest
|
5
|
+
include Vx::Common::AMQP::Consumer
|
6
|
+
|
7
|
+
model Hash
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Vx::Common::AMQP::Formatter do
|
11
|
+
let(:consumer) { FormatterConsumerTest.new }
|
12
|
+
subject { described_class }
|
13
|
+
|
14
|
+
context "pack" do
|
15
|
+
let(:body) { { "a" => 1, "b" => 2 } }
|
16
|
+
|
17
|
+
it "should pack message" do
|
18
|
+
expect(subject.pack 'application/json', body).to eq body.to_json
|
19
|
+
expect(subject.pack :foo, body).to be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
context "unpack" do
|
25
|
+
let(:body) { { "a" => 1, "b" => 2 } }
|
26
|
+
|
27
|
+
before do
|
28
|
+
mock(Hash).from_json(body.to_json) { body }
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should unpack message" do
|
32
|
+
expect(subject.unpack 'application/json', Hash, body.to_json).to eq body
|
33
|
+
expect(subject.unpack :foo, Hash, body.to_json).to be_nil
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
context "lookup" do
|
39
|
+
|
40
|
+
it "should find format by content type" do
|
41
|
+
expect(subject.lookup('application/json').content_type).to eq 'application/json'
|
42
|
+
expect(subject.lookup(:foo)).to be_nil
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Vx::Common::AMQP::Callbacks do
|
4
|
+
let(:output) { [] }
|
5
|
+
let(:object) { Object.new.extend described_class }
|
6
|
+
|
7
|
+
before do
|
8
|
+
Vx::Common::AMQP.config.reset!
|
9
|
+
Vx::Common::AMQP.configure do |c|
|
10
|
+
c.before_publish { |v| output << "before:#{v}" }
|
11
|
+
c.after_publish { |v| output << "after:#{v}" }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
after { Vx::Common::AMQP.config.reset! }
|
16
|
+
|
17
|
+
context 'run_callbacks' do
|
18
|
+
it "should be success" do
|
19
|
+
object.run_callbacks :publish, "call" do
|
20
|
+
output << "call"
|
21
|
+
end
|
22
|
+
expect(output).to eq %w{ before:call call after:call }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|