amqp 0.7.0.pre → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/.gitignore +4 -0
  2. data/.rspec +2 -0
  3. data/CHANGELOG +8 -2
  4. data/CONTRIBUTORS +22 -0
  5. data/Gemfile +3 -3
  6. data/README.md +20 -11
  7. data/Rakefile +30 -6
  8. data/amqp.gemspec +1 -1
  9. data/bin/cleanify.rb +50 -0
  10. data/examples/amqp/simple.rb +6 -4
  11. data/examples/mq/ack.rb +8 -6
  12. data/examples/mq/automatic_binding_for_default_direct_exchange.rb +65 -0
  13. data/examples/mq/callbacks.rb +9 -1
  14. data/examples/mq/clock.rb +17 -17
  15. data/examples/mq/hashtable.rb +19 -10
  16. data/examples/mq/internal.rb +13 -11
  17. data/examples/mq/logger.rb +38 -36
  18. data/examples/mq/multiclock.rb +16 -7
  19. data/examples/mq/pingpong.rb +16 -7
  20. data/examples/mq/pop.rb +8 -6
  21. data/examples/mq/primes-simple.rb +2 -0
  22. data/examples/mq/primes.rb +7 -5
  23. data/examples/mq/stocks.rb +14 -5
  24. data/lib/amqp.rb +12 -8
  25. data/lib/amqp/buffer.rb +35 -158
  26. data/lib/amqp/client.rb +34 -22
  27. data/lib/amqp/frame.rb +8 -64
  28. data/lib/amqp/protocol.rb +21 -70
  29. data/lib/amqp/server.rb +11 -9
  30. data/lib/amqp/spec.rb +8 -6
  31. data/lib/amqp/version.rb +2 -0
  32. data/lib/ext/blankslate.rb +3 -1
  33. data/lib/ext/em.rb +2 -0
  34. data/lib/ext/emfork.rb +13 -11
  35. data/lib/mq.rb +253 -156
  36. data/lib/mq/collection.rb +6 -88
  37. data/lib/mq/exchange.rb +70 -13
  38. data/lib/mq/header.rb +12 -6
  39. data/lib/mq/logger.rb +9 -7
  40. data/lib/mq/queue.rb +42 -30
  41. data/lib/mq/rpc.rb +6 -4
  42. data/protocol/codegen.rb +20 -18
  43. data/research/api.rb +10 -46
  44. data/research/primes-forked.rb +9 -7
  45. data/research/primes-processes.rb +74 -72
  46. data/research/primes-threaded.rb +9 -7
  47. data/spec/integration/automatic_binding_for_default_direct_exchange_spec.rb +61 -0
  48. data/spec/mq_helper.rb +70 -0
  49. data/spec/spec_helper.rb +84 -29
  50. data/spec/unit/amqp/buffer_spec.rb +178 -0
  51. data/spec/unit/amqp/client_spec.rb +472 -0
  52. data/spec/unit/amqp/frame_spec.rb +60 -0
  53. data/spec/unit/amqp/misc_spec.rb +123 -0
  54. data/spec/unit/amqp/protocol_spec.rb +53 -0
  55. data/spec/unit/mq/channel_close_spec.rb +15 -0
  56. data/spec/unit/mq/collection_spec.rb +129 -0
  57. data/spec/unit/mq/exchange_declaration_spec.rb +524 -0
  58. data/spec/unit/mq/misc_spec.rb +228 -0
  59. data/spec/unit/mq/mq_basic_spec.rb +39 -0
  60. data/spec/unit/mq/queue_declaration_spec.rb +97 -0
  61. data/spec/unit/mq/queue_spec.rb +71 -0
  62. metadata +33 -21
  63. data/Gemfile.lock +0 -16
  64. data/old/README +0 -30
  65. data/old/Rakefile +0 -12
  66. data/old/amqp-0.8.json +0 -606
  67. data/old/amqp_spec.rb +0 -796
  68. data/old/amqpc.rb +0 -695
  69. data/old/codegen.rb +0 -148
  70. data/spec/channel_close_spec.rb +0 -13
  71. data/spec/sync_async_spec.rb +0 -52
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+
3
+
4
+ require "spec_helper"
5
+ require "amqp/frame"
6
+
7
+ describe AMQP::Frame do
8
+ include AMQP
9
+
10
+ it 'should handle basic frame types' do
11
+ Frame::Method.new.id.should == 1
12
+ Frame::Header.new.id.should == 2
13
+ Frame::Body.new.id.should == 3
14
+ end
15
+
16
+ it 'should convert method frames to binary' do
17
+ meth = Protocol::Connection::Secure.new :challenge => 'secret'
18
+
19
+ frame = Frame::Method.new(meth)
20
+ frame.to_binary.should be_kind_of Buffer
21
+ frame.to_s.should == [1, 0, meth.to_s.length, meth.to_s, 206].pack('CnNa*C')
22
+ end
23
+
24
+ it 'should convert binary to method frames' do
25
+ orig = Frame::Method.new Protocol::Connection::Secure.new(:challenge => 'secret')
26
+
27
+ copy = Frame.parse(orig.to_binary)
28
+ copy.should == orig
29
+ end
30
+
31
+ it 'should ignore partial frames until ready' do
32
+ frame = Frame::Method.new Protocol::Connection::Secure.new(:challenge => 'secret')
33
+ data = frame.to_s
34
+
35
+ buf = Buffer.new
36
+ Frame.parse(buf).should == nil
37
+
38
+ buf << data[0..5]
39
+ Frame.parse(buf).should == nil
40
+
41
+ buf << data[6..-1]
42
+ Frame.parse(buf).should == frame
43
+
44
+ Frame.parse(buf).should == nil
45
+ end
46
+
47
+ it 'should convert header frames to binary' do
48
+ head = Protocol::Header.new(Protocol::Basic, :priority => 1)
49
+
50
+ frame = Frame::Header.new(head)
51
+ frame.to_s.should == [2, 0, head.to_s.length, head.to_s, 206].pack('CnNa*C')
52
+ end
53
+
54
+ it 'should convert binary to header frame' do
55
+ orig = Frame::Header.new Protocol::Header.new(Protocol::Basic, :priority => 1)
56
+
57
+ copy = Frame.parse(orig.to_binary)
58
+ copy.should == orig
59
+ end
60
+ end
@@ -0,0 +1,123 @@
1
+ require 'spec_helper'
2
+
3
+ require 'amqp'
4
+
5
+ module MockClientModule
6
+ def my_mock_method
7
+ end
8
+ end
9
+
10
+ class MockClient
11
+ include AMQP::Client
12
+ end
13
+
14
+ describe AMQP, 'class object' do
15
+ context 'has class accessors, with default values' do
16
+ subject { AMQP }
17
+
18
+ its(:logging) { should be_false }
19
+ its(:connection) { should be_nil }
20
+ its(:conn) { should be_nil } # Alias for #connection
21
+ its(:closing) { should be_false }
22
+ its(:settings) { should == {:host => "127.0.0.1",
23
+ :port => 5672,
24
+ :user => "guest",
25
+ :pass => "guest",
26
+ :vhost => "/",
27
+ :timeout => nil,
28
+ :logging => false,
29
+ :ssl => false} }
30
+
31
+ its(:client) { should == AMQP::BasicClient }
32
+ end
33
+
34
+
35
+
36
+ describe '.client=' do
37
+ after(:all) { AMQP.client = AMQP::BasicClient }
38
+
39
+ it 'is used to change default client module' do
40
+ AMQP.client = MockClientModule
41
+ AMQP.client.should == MockClientModule
42
+ MockClientModule.ancestors.should include AMQP
43
+ end
44
+
45
+ describe 'new default client module' do
46
+ it 'sticks around after being assigned' do
47
+ AMQP.client.should == MockClientModule
48
+ end
49
+
50
+ it 'extends any object that includes AMQP::Client' do
51
+ @client = MockClient.new
52
+ @client.should respond_to :my_mock_method
53
+ end
54
+ end
55
+ end # .client
56
+
57
+
58
+
59
+
60
+ describe 'logging' do
61
+ after(:all) do
62
+ AMQP.logging = false
63
+ end
64
+
65
+ it 'is silent by default' do
66
+ AMQP.logging.should be_false
67
+ end
68
+ end # .logging=
69
+
70
+
71
+
72
+
73
+ describe '.start' do
74
+ context 'inside EM loop' do
75
+ include AMQP::SpecHelper
76
+ em_after { AMQP.cleanup_state }
77
+
78
+ it 'yields to given block AFTER connection is established' do
79
+ em do
80
+ AMQP.start AMQP_OPTS do
81
+ @block_fired = true
82
+ AMQP.connection.should be_connected
83
+ end
84
+ done(0.1) { @block_fired.should be_true }
85
+ end
86
+ end
87
+ end # context 'inside EM loop'
88
+ end # .start
89
+
90
+
91
+
92
+
93
+ describe '.stop' do
94
+ it 'is noop if connection is not established' do
95
+ expect { @res = AMQP.stop }.to_not raise_error
96
+ @res.should be_nil
97
+ end
98
+
99
+ context 'with established AMQP connection' do
100
+ include AMQP::Spec
101
+ after { AMQP.cleanup_state; done }
102
+ default_options AMQP_OPTS
103
+
104
+ it 'unsets AMQP.connection property. Mind the delay!' do
105
+ AMQP.start(AMQP_OPTS)
106
+ AMQP.connection.should be_connected
107
+
108
+ AMQP.stop
109
+ AMQP.connection.should_not be_nil
110
+ done(0.1) { AMQP.connection.should be_nil }
111
+ end
112
+
113
+ it 'yields to given block AFTER disconnect (BUT before AMQP.conn is cleared!)' do
114
+ AMQP.stop do
115
+ @block_fired = true
116
+ AMQP.connection.should_not be_nil
117
+ AMQP.instance_variable_get(:@closing).should be_true
118
+ end
119
+ done(0.1) { @block_fired.should be_true }
120
+ end
121
+ end # context 'with established AMQP connection'
122
+ end # .stop
123
+ end
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+ require "amqp/protocol"
5
+
6
+ describe AMQP::Protocol do
7
+ include AMQP
8
+
9
+ it 'should instantiate methods with arguments' do
10
+ meth = Protocol::Connection::StartOk.new nil, 'PLAIN', nil, 'en_US'
11
+ meth.locale.should == 'en_US'
12
+ end
13
+
14
+ it 'should instantiate methods with named parameters' do
15
+ meth = Protocol::Connection::StartOk.new :locale => 'en_US',
16
+ :mechanism => 'PLAIN'
17
+ meth.locale.should == 'en_US'
18
+ end
19
+
20
+ it 'should convert methods to binary' do
21
+ meth = Protocol::Connection::Secure.new :challenge => 'secret'
22
+ meth.to_binary.should be_kind_of Buffer
23
+
24
+ meth.to_s.should == [10, 20, 6, 'secret'].pack('nnNa*')
25
+ end
26
+
27
+ it 'should convert binary to method' do
28
+ orig = Protocol::Connection::Secure.new :challenge => 'secret'
29
+ copy = Protocol.parse orig.to_binary
30
+ orig.should == copy
31
+ end
32
+
33
+ it 'should convert headers to binary' do
34
+ head = Protocol::Header.new Protocol::Basic,
35
+ size = 5,
36
+ weight = 0,
37
+ :content_type => 'text/json',
38
+ :delivery_mode => 1,
39
+ :priority => 1
40
+ head.to_s.should ==
41
+ [60, weight, 0, size, 0b1001_1000_0000_0000, 9, 'text/json', 1, 1].pack('nnNNnCa*CC')
42
+ end
43
+
44
+ it 'should convert binary to header' do
45
+ orig = Protocol::Header.new Protocol::Basic,
46
+ size = 5,
47
+ weight = 0,
48
+ :content_type => 'text/json',
49
+ :delivery_mode => 1,
50
+ :priority => 1
51
+ Protocol::Header.new(orig.to_binary).should == orig
52
+ end
53
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe MQ, "#close(&callback)" do
6
+ include AMQP::EMSpec
7
+
8
+ default_timeout 5
9
+
10
+ it "takes a callback which will run when we get back Channel.Close-Ok" do
11
+ MQ.new.close do |amq|
12
+ done
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,129 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+ require "mq/collection"
5
+
6
+ Item = Struct.new(:name)
7
+
8
+ describe MQ::Collection do
9
+ before do
10
+ @items = 3.times.map { |int| Item.new("name-#{int}") }
11
+ @collection = MQ::Collection.new(@items)
12
+ end
13
+
14
+ it "provides access to items by name" do
15
+ @collection["name-1"].should_not be_nil
16
+ @collection["name-1"].should eql(@items[1])
17
+ end
18
+
19
+ it "DOES NOT allow modification of existing items" do
20
+ lambda { @collection["name-1"] = Item.new("test") }.should raise_error(NoMethodError)
21
+ end
22
+
23
+
24
+ describe "#<<" do
25
+ it "should raise IncompatibleItemError if the argument doesn't have method :name" do
26
+ lambda { @collection << nil }.should raise_error(MQ::Collection::IncompatibleItemError)
27
+ end
28
+
29
+ it "should add an item into the collection" do
30
+ length = @collection.length
31
+ @collection << Item.new("test")
32
+ @collection.length.should eql(length + 1)
33
+ end
34
+
35
+ context "when another item with given name already exists and the name IS NOT nil" do
36
+ it "should not add an item to the collection" do
37
+ @collection << Item.new("test")
38
+ length = @collection.length
39
+ @collection << Item.new("test")
40
+ @collection.length.should eql(length)
41
+ end # it
42
+ end # context
43
+
44
+
45
+ context "when another item with given name already exists and the name IS nil" do
46
+ it "should add an item to the collection" do
47
+ @collection << Item.new(nil)
48
+ length = @collection.length
49
+ @collection << Item.new(nil)
50
+ @collection.length.should eql(length + 1)
51
+ end # it
52
+ end # context
53
+
54
+ it "should return the item" do
55
+ item = Item.new("test")
56
+ (@collection << item).should eql item
57
+ end
58
+
59
+ context "when adding new item with duplicate name" do
60
+ before do
61
+ @original_item = Item.new("test")
62
+ @new_item = Item.new("test")
63
+ @collection << @original_item
64
+ end
65
+
66
+ it "keeps item already in collection" do
67
+ @collection << @new_item
68
+ @collection['test'].should eql @original_item
69
+ end
70
+
71
+ it "returns item already in collection" do
72
+ (@collection << @new_item).should eql @original_item
73
+ end
74
+ end
75
+
76
+ context "when item we add already exists in the collection" do
77
+ it "should return the item" do
78
+ item = Item.new("test")
79
+ @collection << item
80
+ (@collection << item).should eql item
81
+ end # it
82
+ end # context
83
+ end # describe
84
+
85
+
86
+
87
+
88
+ describe "#add!" do
89
+ context "when the argument doesn't respond to :name" do
90
+ it "should raise IncompatibleItemError " do
91
+ lambda { @collection.add!(nil) }.should raise_error(MQ::Collection::IncompatibleItemError)
92
+ end # it
93
+ end
94
+
95
+ context "when another item with given name already exists" do
96
+ it "should add an item to the collection" do
97
+ @collection.add! Item.new("test")
98
+ lambda do
99
+ @collection.add!(Item.new("test"))
100
+ end.should change(@collection, :length).by(1)
101
+ end # it
102
+ end # context
103
+
104
+ it "should return the item" do
105
+ item = Item.new("test")
106
+ (@collection.add! item).should eql(item)
107
+ end # it
108
+ end # describe
109
+
110
+
111
+
112
+
113
+
114
+ describe "#<<" do
115
+ context "when the argument doesn't respond to :name" do
116
+ it "should raise IncompatibleItemError " do
117
+ lambda { @collection << nil }.should raise_error(MQ::Collection::IncompatibleItemError)
118
+ end # it
119
+ end # context
120
+
121
+ context "when the argument DOES respond to :name" do
122
+ it "should add an item into the collection" do
123
+ lambda do
124
+ @collection << Item.new("test")
125
+ end.should change(@collection, :length).by(1)
126
+ end
127
+ end # context
128
+ end # describe
129
+ end # describe MQ::Collection
@@ -0,0 +1,524 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe MQ do
6
+
7
+ #
8
+ # Environment
9
+ #
10
+
11
+ include AMQP::Spec
12
+
13
+ default_timeout 10
14
+
15
+ amqp_before do
16
+ @channel = MQ.new
17
+ end
18
+
19
+
20
+ #
21
+ # Examples
22
+ #
23
+
24
+ describe "#direct" do
25
+ context "when exchange name is specified" do
26
+ it 'declares a new direct exchange with that name' do
27
+ @channel.direct('name').name.should == 'name'
28
+
29
+ done
30
+ end
31
+
32
+ it "declares direct exchange as transient (non-durable)" do
33
+ exchange = @channel.direct('name')
34
+
35
+ exchange.should_not be_durable
36
+ exchange.should be_transient
37
+
38
+ done
39
+ end
40
+
41
+ it "declares direct exchange as non-auto-deleted" do
42
+ exchange = @channel.direct('name')
43
+
44
+ exchange.should_not be_auto_deleted
45
+
46
+ done
47
+ end
48
+ end
49
+
50
+
51
+ context "when exchange name is omitted" do
52
+ it 'uses amq.direct' do
53
+ @channel.direct.name.should == 'amq.direct'
54
+ done
55
+ end # it
56
+ end # context
57
+
58
+
59
+ context "when exchange name was specified as a blank string" do
60
+ it 'returns direct exchange with server-generated name' do
61
+ pending <<-EOF
62
+ This has to be fixed in RabbitMQ first
63
+ https://bugzilla.rabbitmq.com/show_bug.cgi?id=23509
64
+ EOF
65
+ @channel.direct("") do |exchange|
66
+ exchange.name.should_not be_empty
67
+ done
68
+ end
69
+ end
70
+ end # context
71
+
72
+
73
+ context "when passive option is used" do
74
+ context "and exchange with given name already exists" do
75
+ it "silently returns" do
76
+ name = "a_new_direct_exchange declared at #{Time.now.to_i}"
77
+
78
+ original_exchange = @channel.direct(name)
79
+ exchange = @channel.direct(name, :passive => true)
80
+
81
+ exchange.should == original_exchange
82
+
83
+ done
84
+ end # it
85
+ end
86
+
87
+ context "and exchange with given name DOES NOT exist" do
88
+ it "raises an exception" do
89
+ pending "Not yet supported"
90
+
91
+ expect {
92
+ exchange = @channel.direct("direct exchange declared at #{Time.now.to_i}", :passive => true)
93
+ }.to raise_error
94
+
95
+ done
96
+ end # it
97
+ end # context
98
+ end # context
99
+
100
+
101
+ context "when exchange is declared as durable" do
102
+ it "returns a new durable direct exchange" do
103
+ exchange = @channel.direct("a_new_durable_direct_exchange", :durable => true)
104
+ exchange.should be_durable
105
+ exchange.should_not be_transient
106
+
107
+ done
108
+ end # it
109
+ end # context
110
+
111
+
112
+ context "when exchange is declared as non-durable" do
113
+ it "returns a new NON-durable direct exchange" do
114
+ exchange = @channel.direct("a_new_non_durable_direct_exchange", :durable => false)
115
+ exchange.should_not be_durable
116
+ exchange.should be_transient
117
+
118
+ done
119
+ end # it
120
+ end # context
121
+
122
+
123
+ context "when exchange is declared as auto-deleted" do
124
+ it "returns a new auto-deleted direct exchange" do
125
+ exchange = @channel.direct("a new auto-deleted direct exchange", :auto_delete => true)
126
+
127
+ exchange.should be_auto_deleted
128
+ done
129
+ end # it
130
+ end # context
131
+
132
+
133
+ context "when exchange is declared as auto-deleted" do
134
+ it "returns a new auto-deleted direct exchange" do
135
+ exchange = @channel.direct("a new non-auto-deleted direct exchange", :auto_delete => false)
136
+
137
+ exchange.should_not be_auto_deleted
138
+ done
139
+ end # it
140
+ end # context
141
+
142
+
143
+ context "when exchange is declared without explicit :nowait parameter" do
144
+ it "is declared with :nowait by default" do
145
+ exchange = @channel.direct("a new non-auto-deleted direct exchange", :auto_delete => false)
146
+
147
+ exchange.should_not be_auto_deleted
148
+ done
149
+ end # it
150
+ end # context
151
+
152
+
153
+ context "when exchange is re-declared with parameters different from original declaration" do
154
+ it "raises an exception" do
155
+ @channel.direct("previously.declared.durable.direct.exchange", :durable => true)
156
+
157
+ expect {
158
+ @channel.direct("previously.declared.durable.direct.exchange", :durable => false)
159
+ }.to raise_error(MQ::IncompatibleOptionsError)
160
+
161
+ done
162
+ end # it
163
+ end # context
164
+ end # describe
165
+
166
+
167
+
168
+
169
+ describe "#fanout" do
170
+ context "when exchange name is specified" do
171
+ let(:name) { "new.fanout.exchange" }
172
+
173
+ it "declares a new fanout exchange with that name" do
174
+ exchange = @channel.fanout(name)
175
+
176
+ exchange.name.should == name
177
+
178
+ done
179
+ end
180
+ end # context
181
+
182
+ context "when exchange name is omitted" do
183
+ it "uses amq.fanout" do
184
+ exchange = @channel.fanout
185
+ exchange.name.should == "amq.fanout"
186
+ exchange.name.should_not == "amq.fanout2"
187
+
188
+ done
189
+ end
190
+ end # context
191
+
192
+ context "when passive option is used" do
193
+ context "and exchange with given name already exists" do
194
+ it "silently returns" do
195
+ name = "a_new_fanout_exchange declared at #{Time.now.to_i}"
196
+
197
+ original_exchange = @channel.fanout(name)
198
+ exchange = @channel.fanout(name, :passive => true)
199
+
200
+ exchange.should == original_exchange
201
+
202
+ done
203
+ end # it
204
+ end
205
+
206
+ context "and exchange with given name DOES NOT exist" do
207
+ it "raises an exception" do
208
+ pending "Not yet supported"
209
+
210
+ expect {
211
+ exchange = @channel.fanout("fanout exchange declared at #{Time.now.to_i}", :passive => true)
212
+ }.to raise_error
213
+
214
+ done
215
+ end # it
216
+ end # context
217
+ end # context
218
+
219
+
220
+ context "when exchange is declared as durable" do
221
+ it "returns a new durable fanout exchange" do
222
+ exchange = @channel.fanout("a_new_durable_fanout_exchange", :durable => true)
223
+ exchange.should be_durable
224
+ exchange.should_not be_transient
225
+
226
+ done
227
+ end # it
228
+ end # context
229
+
230
+
231
+ context "when exchange is declared as non-durable" do
232
+ it "returns a new NON-durable fanout exchange" do
233
+ exchange = @channel.fanout("a_new_non_durable_fanout_exchange", :durable => false)
234
+ exchange.should_not be_durable
235
+ exchange.should be_transient
236
+
237
+ done
238
+ end # it
239
+ end # context
240
+
241
+
242
+ context "when exchange is declared as auto-deleted" do
243
+ it "returns a new auto-deleted fanout exchange" do
244
+ exchange = @channel.fanout("a new auto-deleted fanout exchange", :auto_delete => true)
245
+
246
+ exchange.should be_auto_deleted
247
+ done
248
+ end # it
249
+ end # context
250
+
251
+
252
+ context "when exchange is declared as auto-deleted" do
253
+ it "returns a new auto-deleted fanout exchange" do
254
+ exchange = @channel.fanout("a new non-auto-deleted fanout exchange", :auto_delete => false)
255
+
256
+ exchange.should_not be_auto_deleted
257
+ done
258
+ end # it
259
+ end # context
260
+
261
+
262
+ context "when exchange is declared without explicit :nowait parameter" do
263
+ it "is declared with :nowait by default" do
264
+ exchange = @channel.fanout("a new non-auto-deleted fanout exchange", :auto_delete => false)
265
+
266
+ exchange.should_not be_auto_deleted
267
+ done
268
+ end # it
269
+ end # context
270
+
271
+
272
+ context "when exchange is re-declared with parameters different from original declaration" do
273
+ it "raises an exception" do
274
+ @channel.fanout("previously.declared.durable.topic.exchange", :durable => true)
275
+
276
+ expect {
277
+ @channel.fanout("previously.declared.durable.topic.exchange", :durable => false)
278
+ }.to raise_error(MQ::IncompatibleOptionsError)
279
+
280
+ done
281
+ end # it
282
+ end # context
283
+ end # describe
284
+
285
+
286
+
287
+
288
+ describe "#topic" do
289
+ context "when exchange name is specified" do
290
+ let(:name) { "a.topic.exchange" }
291
+
292
+ it "declares a new topic exchange with that name" do
293
+ exchange = @channel.topic(name)
294
+ exchange.name.should == name
295
+
296
+ done
297
+ end
298
+ end # context
299
+
300
+ context "when exchange name is omitted" do
301
+ it "uses amq.topic" do
302
+ exchange = @channel.topic
303
+ exchange.name.should == "amq.topic"
304
+ exchange.name.should_not == "amq.topic2"
305
+
306
+ done
307
+ end
308
+ end # context
309
+
310
+ context "when passive option is used" do
311
+ context "and exchange with given name already exists" do
312
+ it "silently returns" do
313
+ name = "a_new_topic_exchange declared at #{Time.now.to_i}"
314
+
315
+ original_exchange = @channel.topic(name)
316
+ exchange = @channel.topic(name, :passive => true)
317
+
318
+ exchange.should == original_exchange
319
+
320
+ done
321
+ end # it
322
+ end
323
+
324
+ context "and exchange with given name DOES NOT exist" do
325
+ it "raises an exception" do
326
+ pending "Not yet supported"
327
+
328
+ expect {
329
+ exchange = @channel.topic("topic exchange declared at #{Time.now.to_i}", :passive => true)
330
+ }.to raise_error
331
+
332
+ done
333
+ end # it
334
+ end # context
335
+ end # context
336
+
337
+
338
+ context "when exchange is declared as durable" do
339
+ it "returns a new durable topic exchange" do
340
+ exchange = @channel.topic("a_new_durable_topic_exchange", :durable => true)
341
+ exchange.should be_durable
342
+ exchange.should_not be_transient
343
+
344
+ done
345
+ end # it
346
+ end # context
347
+
348
+
349
+ context "when exchange is declared as non-durable" do
350
+ it "returns a new NON-durable topic exchange" do
351
+ exchange = @channel.topic("a_new_non_durable_topic_exchange", :durable => false)
352
+ exchange.should_not be_durable
353
+ exchange.should be_transient
354
+
355
+ done
356
+ end # it
357
+ end # context
358
+
359
+
360
+ context "when exchange is declared as auto-deleted" do
361
+ it "returns a new auto-deleted topic exchange" do
362
+ exchange = @channel.topic("a new auto-deleted topic exchange", :auto_delete => true)
363
+
364
+ exchange.should be_auto_deleted
365
+ done
366
+ end # it
367
+ end # context
368
+
369
+
370
+ context "when exchange is declared as auto-deleted" do
371
+ it "returns a new auto-deleted topic exchange" do
372
+ exchange = @channel.topic("a new non-auto-deleted topic exchange", :auto_delete => false)
373
+
374
+ exchange.should_not be_auto_deleted
375
+ done
376
+ end # it
377
+ end # context
378
+
379
+
380
+ context "when exchange is declared without explicit :nowait parameter" do
381
+ it "is declared with :nowait by default" do
382
+ exchange = @channel.topic("a new non-auto-deleted topic exchange", :auto_delete => false)
383
+
384
+ exchange.should_not be_auto_deleted
385
+ done
386
+ end # it
387
+ end # context
388
+
389
+
390
+ context "when exchange is re-declared with parameters different from original declaration" do
391
+ xit "raises an exception" do
392
+ @channel.topic("previously.declared.durable.topic.exchange", :durable => true)
393
+
394
+ expect {
395
+ @channel.topic("previously.declared.durable.topic.exchange", :durable => false)
396
+ }.to raise_error(MQ::IncompatibleOptionsError)
397
+
398
+ done
399
+ end # it
400
+ end # context
401
+ end # describe
402
+
403
+
404
+
405
+
406
+ describe "#headers" do
407
+ context "when exchange name is specified" do
408
+ let(:name) { "new.headers.exchange" }
409
+
410
+ it "declares a new headers exchange with that name" do
411
+ exchange = @channel.headers(name)
412
+
413
+ exchange.name.should == name
414
+
415
+ done
416
+ end
417
+ end # context
418
+
419
+ context "when exchange name is omitted" do
420
+ xit "uses amq.match" do
421
+ pending "Times out. MK."
422
+
423
+ exchange = @channel.headers
424
+ exchange.name.should == "amq.match"
425
+ exchange.name.should_not == "amq.headers"
426
+
427
+ done
428
+ end
429
+ end # context
430
+
431
+ context "when passive option is used" do
432
+ context "and exchange with given name already exists" do
433
+ it "silently returns" do
434
+ name = "a_new_headers_exchange declared at #{Time.now.to_i}"
435
+
436
+ original_exchange = @channel.headers(name)
437
+ exchange = @channel.headers(name, :passive => true)
438
+
439
+ exchange.should == original_exchange
440
+
441
+ done
442
+ end # it
443
+ end
444
+
445
+ context "and exchange with given name DOES NOT exist" do
446
+ it "raises an exception" do
447
+ pending "Not yet supported"
448
+
449
+ expect {
450
+ exchange = @channel.headers("headers exchange declared at #{Time.now.to_i}", :passive => true)
451
+ }.to raise_error
452
+
453
+ done
454
+ end # it
455
+ end # context
456
+ end # context
457
+
458
+
459
+ context "when exchange is declared as durable" do
460
+ it "returns a new durable headers exchange" do
461
+ exchange = @channel.headers("a_new_durable_headers_exchange", :durable => true)
462
+ exchange.should be_durable
463
+ exchange.should_not be_transient
464
+
465
+ done
466
+ end # it
467
+ end # context
468
+
469
+
470
+ context "when exchange is declared as non-durable" do
471
+ it "returns a new NON-durable headers exchange" do
472
+ exchange = @channel.headers("a_new_non_durable_headers_exchange", :durable => false)
473
+ exchange.should_not be_durable
474
+ exchange.should be_transient
475
+
476
+ done
477
+ end # it
478
+ end # context
479
+
480
+
481
+ context "when exchange is declared as auto-deleted" do
482
+ it "returns a new auto-deleted headers exchange" do
483
+ exchange = @channel.headers("a new auto-deleted headers exchange", :auto_delete => true)
484
+
485
+ exchange.should be_auto_deleted
486
+ done
487
+ end # it
488
+ end # context
489
+
490
+
491
+ context "when exchange is declared as auto-deleted" do
492
+ it "returns a new auto-deleted headers exchange" do
493
+ exchange = @channel.headers("a new non-auto-deleted headers exchange", :auto_delete => false)
494
+
495
+ exchange.should_not be_auto_deleted
496
+ done
497
+ end # it
498
+ end # context
499
+
500
+
501
+ context "when exchange is declared without explicit :nowait parameter" do
502
+ it "is declared with :nowait by default" do
503
+ exchange = @channel.headers("a new non-auto-deleted headers exchange", :auto_delete => false)
504
+
505
+ exchange.should_not be_auto_deleted
506
+ done
507
+ end # it
508
+ end # context
509
+
510
+
511
+ context "when exchange is re-declared with parameters different from original declaration" do
512
+ xit "raises an exception" do
513
+ pending "Times out. MK."
514
+ @channel.headers("previously.declared.durable.topic.exchange", :durable => true)
515
+
516
+ expect {
517
+ @channel.headers("previously.declared.durable.topic.exchange", :durable => false)
518
+ }.to raise_error(MQ::IncompatibleOptionsError)
519
+
520
+ done
521
+ end # it
522
+ end # context
523
+ end # describe
524
+ end # describe MQ