qpid_proton 0.21.0 → 0.22.0

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.
@@ -20,26 +20,26 @@ require 'test_tools'
20
20
  require 'minitest/unit'
21
21
  require 'socket'
22
22
 
23
- # Container that listens on a random port
24
- class TestContainer < Qpid::Proton::Container
25
-
26
- def initialize(handler, lopts=nil, id=nil)
27
- super handler, id
28
- @listener = listen_io(TCPServer.open(0), ListenOnceHandler.new(lopts))
29
- end
30
-
31
- def port() @listener.to_io.addr[1]; end
32
- def url() "amqp://:#{port}"; end#
23
+ # MessagingHandler that raises in on_error to catch unexpected errors
24
+ class ExceptionMessagingHandler
25
+ def on_error(e) raise e; end
33
26
  end
34
27
 
35
28
  class ContainerTest < MiniTest::Test
36
29
  include Qpid::Proton
37
30
 
38
31
  def test_simple()
39
- send_handler = Class.new(MessagingHandler) do
32
+ send_handler = Class.new(ExceptionMessagingHandler) do
40
33
  attr_reader :accepted, :sent
34
+
35
+ def initialize() @sent, @accepted = nil; end
36
+
41
37
  def on_sendable(sender)
42
- sender.send Message.new("foo") unless @sent
38
+ unless @sent
39
+ m = Message.new("hello")
40
+ m[:foo] = :bar
41
+ sender.send m
42
+ end
43
43
  @sent = true
44
44
  end
45
45
 
@@ -49,7 +49,7 @@ class ContainerTest < MiniTest::Test
49
49
  end
50
50
  end.new
51
51
 
52
- receive_handler = Class.new(MessagingHandler) do
52
+ receive_handler = Class.new(ExceptionMessagingHandler) do
53
53
  attr_reader :message, :link
54
54
  def on_receiver_open(link)
55
55
  @link = link
@@ -64,13 +64,14 @@ class ContainerTest < MiniTest::Test
64
64
  end
65
65
  end.new
66
66
 
67
- c = TestContainer.new(receive_handler, {}, __method__)
67
+ c = ServerContainer.new(__method__, {:handler => receive_handler})
68
68
  c.connect(c.url, {:handler => send_handler}).open_sender({:name => "testlink"})
69
69
  c.run
70
70
 
71
71
  assert send_handler.accepted
72
72
  assert_equal "testlink", receive_handler.link.name
73
- assert_equal "foo", receive_handler.message.body
73
+ assert_equal "hello", receive_handler.message.body
74
+ assert_equal :bar, receive_handler.message[:foo]
74
75
  assert_equal "test_simple", receive_handler.link.connection.container_id
75
76
  end
76
77
 
@@ -153,48 +154,48 @@ class ContainerTest < MiniTest::Test
153
154
  def test_bad_host
154
155
  cont = Container.new(__method__)
155
156
  assert_raises (SocketError) { cont.listen("badlisten.example.com:999") }
156
- assert_raises (SocketError) { c = cont.connect("badconnect.example.com:999") }
157
+ assert_raises (SocketError) { cont.connect("badconnect.example.com:999") }
157
158
  end
158
159
 
159
- # Verify that connection options are sent to the peer and available as Connection methods
160
+ # Verify that connection options are sent to the peer
160
161
  def test_connection_options
161
162
  # Note: user, password and sasl_xxx options are tested by ContainerSASLTest below
162
- server_handler = Class.new(MessagingHandler) do
163
- def on_error(e) raise e.inspect; end
163
+ server_handler = Class.new(ExceptionMessagingHandler) do
164
164
  def on_connection_open(c)
165
165
  @connection = c
166
166
  c.open({
167
- :virtual_host => "server.to.client",
168
- :properties => { :server => :client },
169
- :offered_capabilities => [ :s1 ],
170
- :desired_capabilities => [ :s2 ],
171
- :container_id => "box",
172
- })
167
+ :virtual_host => "server.to.client",
168
+ :properties => { :server => :client },
169
+ :offered_capabilities => [ :s1 ],
170
+ :desired_capabilities => [ :s2 ],
171
+ :container_id => "box",
172
+ })
173
173
  c.close
174
174
  end
175
175
  attr_reader :connection
176
176
  end.new
177
- # Transport options must be provided to the listener, by Connection#open it is too late
178
- cont = TestContainer.new(nil, {
179
- :handler => server_handler,
180
- :idle_timeout => 88,
181
- :max_sessions =>1000,
182
- :max_frame_size => 8888,
183
- })
177
+ # Transport options set by listener, by Connection#open it is too late
178
+ cont = ServerContainer.new(__method__, {
179
+ :handler => server_handler,
180
+ :idle_timeout => 88,
181
+ :max_sessions =>1000,
182
+ :max_frame_size => 8888,
183
+ })
184
184
  client = cont.connect(cont.url,
185
- {:virtual_host => "client.to.server",
186
- :properties => { :foo => :bar, "str" => "str" },
187
- :offered_capabilities => [:c1 ],
188
- :desired_capabilities => ["c2" ],
189
- :idle_timeout => 42,
190
- :max_sessions =>100,
191
- :max_frame_size => 4096,
192
- :container_id => "bowl"
193
- })
185
+ {:virtual_host => "client.to.server",
186
+ :properties => { "foo" => :bar, "str" => "str" },
187
+ :offered_capabilities => [:c1 ],
188
+ :desired_capabilities => [:c2 ],
189
+ :idle_timeout => 42,
190
+ :max_sessions =>100,
191
+ :max_frame_size => 4096,
192
+ :container_id => "bowl"
193
+ })
194
194
  cont.run
195
+
195
196
  c = server_handler.connection
196
197
  assert_equal "client.to.server", c.virtual_host
197
- assert_equal({ :foo => :bar, :str => "str" }, c.properties)
198
+ assert_equal({ "foo" => :bar, "str" => "str" }, c.properties)
198
199
  assert_equal([:c1], c.offered_capabilities)
199
200
  assert_equal([:c2], c.desired_capabilities)
200
201
  assert_equal 21, c.idle_timeout # Proton divides by 2
@@ -212,108 +213,184 @@ class ContainerTest < MiniTest::Test
212
213
  assert_equal 44, c.idle_timeout # Proton divides by 2
213
214
  assert_equal 100, c.max_sessions
214
215
  end
215
- end
216
-
217
216
 
218
- class ContainerSASLTest < MiniTest::Test
219
- include Qpid::Proton
217
+ def test_link_options
218
+ server_handler = Class.new(ExceptionMessagingHandler) do
219
+ def initialize() @links = []; end
220
+ attr_reader :links
221
+ def on_sender_open(l) @links << l; end
222
+ def on_receiver_open(l) @links << l; end
223
+ end.new
220
224
 
221
- # Handler for test client/server that sets up server and client SASL options
222
- class SASLHandler < TestHandler
225
+ client_handler = Class.new(ExceptionMessagingHandler) do
226
+ def on_connection_open(c)
227
+ @links = [];
228
+ @links << c.open_sender("s1")
229
+ @links << c.open_sender({:name => "s2-n", :target => "s2-t", :source => "s2-s"})
230
+ @links << c.open_receiver("r1")
231
+ @links << c.open_receiver({:name => "r2-n", :target => "r2-t", :source => "r2-s"})
232
+ c.close
233
+ end
234
+ attr_reader :links
235
+ end.new
223
236
 
224
- def initialize(url="amqp://", opts=nil)
225
- super()
226
- @url, @opts = url, opts
227
- end
237
+ cont = ServerContainer.new(__method__, {:handler => server_handler }, 1)
238
+ cont.connect(cont.url, :handler => client_handler)
239
+ cont.run
228
240
 
229
- def on_container_start(container)
230
- @client = container.connect("#{@url}:#{container.port}", @opts)
231
- end
241
+ expect = ["test_link_options/1", "s2-n", "test_link_options/2", "r2-n"]
242
+ assert_equal expect, server_handler.links.map(&:name)
243
+ assert_equal expect, client_handler.links.map(&:name)
232
244
 
233
- attr_reader :auth_user
245
+ expect = [[nil,"s1"], ["s2-s","s2-t"], ["r1",nil], ["r2-s","r2-t"]]
246
+ assert_equal expect, server_handler.links.map { |l| [l.remote_source.address, l.remote_target.address] }
247
+ assert_equal expect, client_handler.links.map { |l| [l.source.address, l.target.address] }
248
+ end
234
249
 
235
- def on_connection_open(connection)
236
- super
237
- if connection == @client
238
- connection.close
239
- else
240
- @auth_user = connection.user
241
- end
242
- end
250
+ def extract_terminus_options(t)
251
+ opts = Hash[[:address, :distribution_mode, :durability_mode, :timeout, :expiry_policy].map { |m| [m, t.send(m)] }]
252
+ opts[:filter] = t.filter.map
253
+ opts[:capabilities] = t.capabilities.map
254
+ opts[:dynamic] = t.dynamic?
255
+ opts
243
256
  end
244
257
 
245
- # Generate SASL server configuration files and database, initialize proton SASL
246
- class SASLConfig
247
- include Qpid::Proton
248
- attr_reader :conf_dir, :conf_file, :conf_name, :database
249
-
250
- def initialize()
251
- if SASL.extended? # Configure cyrus SASL
252
- @conf_dir = File.expand_path('sasl_conf')
253
- @conf_name = "proton-server"
254
- @database = File.join(@conf_dir, "proton.sasldb")
255
- @conf_file = File.join(conf_dir,"#{@conf_name}.conf")
256
- Dir::mkdir(@conf_dir) unless File.directory?(@conf_dir)
257
- # Same user name in different realms
258
- make_user("user", "password", "proton") # proton realm
259
- make_user("user", "default_password") # Default realm
260
- File.open(@conf_file, 'w') do |f|
261
- f.write("
262
- sasldb_path: #{database}
263
- mech_list: EXTERNAL DIGEST-MD5 SCRAM-SHA-1 CRAM-MD5 PLAIN ANONYMOUS
264
- ")
265
- end
266
- # Tell proton library to use the new configuration
267
- SASL.config_path = conf_dir
268
- SASL.config_name = conf_name
269
- end
270
- end
258
+ def test_terminus_options
259
+ opts = {
260
+ :distribution_mode => Terminus::DIST_MODE_COPY,
261
+ :durability_mode => Terminus::DELIVERIES,
262
+ :timeout => 5,
263
+ :expiry_policy => Terminus::EXPIRE_WITH_LINK,
264
+ :filter => { :try => 'me' },
265
+ :capabilities => { :cap => 'len' },
266
+ }
267
+ src_opts = { :address => "src", :dynamic => true }.update(opts)
268
+ tgt_opts = { :address => "tgt", :dynamic => false }.update(opts)
269
+
270
+ cont = ServerContainer.new(__method__, {}, 1)
271
+ c = cont.connect(cont.url)
272
+ s = c.open_sender({:target => tgt_opts, :source => src_opts })
273
+ assert_equal src_opts, extract_terminus_options(s.source)
274
+ assert_equal tgt_opts, extract_terminus_options(s.target)
275
+ assert s.source.dynamic?
276
+ assert !s.target.dynamic?
277
+ end
271
278
 
272
- private
279
+ # Test for time out on connecting to an unresponsive server
280
+ def test_idle_timeout_server_no_open
281
+ s = TCPServer.new(0)
282
+ cont = Container.new(__method__)
283
+ cont.connect(":#{s.addr[1]}", {:idle_timeout => 0.1, :handler => ExceptionMessagingHandler.new })
284
+ ex = assert_raises(Qpid::Proton::Condition) { cont.run }
285
+ assert_match(/resource-limit-exceeded/, ex.to_s)
286
+ ensure
287
+ s.close if s
288
+ end
273
289
 
274
- SASLPASSWD = (ENV['SASLPASSWD'] or 'saslpasswd2')
290
+ # Test for time out on unresponsive client
291
+ def test_idle_timeout_client
292
+ server = ServerContainerThread.new("#{__method__}.server", {:idle_timeout => 0.1})
293
+ client_handler = Class.new(ExceptionMessagingHandler) do
294
+ def initialize() @ready, @block = Queue.new, Queue.new; end
295
+ attr_reader :ready, :block
296
+ def on_connection_open(c)
297
+ @ready.push nil # Tell the main thread we are now open
298
+ @block.pop # Block the client so the server will time it out
299
+ end
300
+ end.new
275
301
 
276
- def make_user(user, password, realm=nil)
277
- realm_opt = (realm ? "-u #{realm}" : "")
278
- cmd = "echo '#{password}' | #{SASLPASSWD} -c -p -f #{database} #{realm_opt} #{user}"
279
- system(cmd) or raise RuntimeError.new("saslpasswd2 failed: #{makepw_cmd}")
280
- end
281
- DEFAULT = SASLConfig.new
302
+ client = Container.new(nil, "#{__method__}.client")
303
+ client.connect(server.url, {:handler => client_handler})
304
+ client_thread = Thread.new { client.run }
305
+ client_handler.ready.pop # Wait till the client has connected
306
+ server.join # Exits when the connection closes from idle-timeout
307
+ client_handler.block.push nil # Unblock the client
308
+ ex = assert_raises(Qpid::Proton::Condition) { client_thread.join }
309
+ assert_match(/resource-limit-exceeded/, ex.to_s)
282
310
  end
283
311
 
284
- def test_sasl_anonymous()
285
- s = SASLHandler.new("amqp://", {:sasl_allowed_mechs => "ANONYMOUS"})
286
- TestContainer.new(s, {:sasl_allowed_mechs => "ANONYMOUS"}, __method__).run
287
- assert_equal "anonymous", s.connections[0].user
312
+ # Make sure we stop and clean up if an aborted connection causes a handler to raise.
313
+ # https://issues.apache.org/jira/browse/PROTON-1791
314
+ def test_handler_raise
315
+ cont = ServerContainer.new(__method__, {}, 0) # Don't auto-close the listener
316
+ client_handler = Class.new(MessagingHandler) do
317
+ # TestException is < Exception so not handled by default rescue clause
318
+ def on_connection_open(c) raise TestException.new("Bad Dog"); end
319
+ end.new
320
+ threads = 3.times.collect { Thread.new { cont.run } }
321
+ sleep 0.01 while cont.running < 3 # Wait for all threads to be running
322
+ sockets = 2.times.collect { TCPSocket.new("", cont.port) }
323
+ cont.connect_io(sockets[1]) # No exception
324
+ cont.connect_io(sockets[0], {:handler => client_handler}) # Should stop container
325
+
326
+ threads.each { |t| assert_equal("Bad Dog", assert_raises(TestException) {t.join}.message) }
327
+ sockets.each { |s| assert s.closed? }
328
+ assert cont.listener.to_io.closed?
329
+ assert_raises(Container::StoppedError) { cont.run }
330
+ assert_raises(Container::StoppedError) { cont.listen "" }
288
331
  end
289
332
 
290
- def test_sasl_plain_url()
291
- skip unless SASL.extended?
292
- # Use default realm with URL, should authenticate with "default_password"
293
- opts = {:sasl_allowed_mechs => "PLAIN", :sasl_allow_insecure_mechs => true}
294
- s = SASLHandler.new("amqp://user:default_password@", opts)
295
- TestContainer.new(s, opts, __method__).run
296
- assert_equal(2, s.connections.size)
297
- assert_equal("user", s.auth_user)
333
+ # Make sure Container::Scheduler puts tasks in proper order.
334
+ def test_scheduler
335
+ a = []
336
+ s = Schedule.new
337
+
338
+ assert_equal true, s.add(Time.at 3) { a << 3 }
339
+ assert_equal false, s.process(Time.at 2) # Should not run
340
+ assert_equal [], a
341
+ assert_equal true, s.process(Time.at 3) # Should run
342
+ assert_equal [3], a
343
+
344
+ a = []
345
+ assert_equal true, s.add(Time.at 3) { a << 3 }
346
+ assert_equal false, s.add(Time.at 5) { a << 5 }
347
+ assert_equal false, s.add(Time.at 1) { a << 1 }
348
+ assert_equal false, s.add(Time.at 4) { a << 4 }
349
+ assert_equal false, s.add(Time.at 4) { a << 4.1 }
350
+ assert_equal false, s.add(Time.at 4) { a << 4.2 }
351
+ assert_equal false, s.process(Time.at 4)
352
+ assert_equal [1, 3, 4, 4.1, 4.2], a
353
+ a = []
354
+ assert_equal true, s.process(Time.at 5)
355
+ assert_equal [5], a
298
356
  end
299
357
 
300
- def test_sasl_plain_options()
301
- skip unless SASL.extended?
302
- # Use default realm with connection options, should authenticate with "default_password"
303
- opts = {:sasl_allowed_mechs => "PLAIN",:sasl_allow_insecure_mechs => true,
304
- :user => 'user', :password => 'default_password' }
305
- s = SASLHandler.new("amqp://", opts)
306
- TestContainer.new(s, {:sasl_allowed_mechs => "PLAIN",:sasl_allow_insecure_mechs => true}, __method__).run
307
- assert_equal(2, s.connections.size)
308
- assert_equal("user", s.auth_user)
358
+ def test_container_schedule
359
+ c = Container.new __method__
360
+ delays = [0.1, 0.03, 0.02, 0.04]
361
+ a = []
362
+ delays.each { |d| c.schedule(d) { a << [d, Time.now] } }
363
+ start = Time.now
364
+ c.run
365
+ delays.sort.each do |d|
366
+ x = a.shift
367
+ assert_equal d, x[0]
368
+ assert_in_delta start + d, x[1], 0.01
369
+ end
309
370
  end
310
371
 
311
- # Ensure we don't allow PLAIN if allow_insecure_mechs = true is not explicitly set
312
- def test_disallow_insecure()
313
- # Don't set allow_insecure_mechs, but try to use PLAIN
314
- s = SASLHandler.new("amqp://user:password@", {:sasl_allowed_mechs => "PLAIN", :sasl_allow_insecure_mechs => true})
315
- e = assert_raises(TestError) { TestContainer.new(s, {:sasl_allowed_mechs => "PLAIN"}, __method__).run }
316
- assert_match(/amqp:unauthorized-access.*Authentication failed/, e.to_s)
372
+ def test_work_queue
373
+ cont = ServerContainer.new(__method__, {}, 1)
374
+ c = cont.connect(cont.url)
375
+ t = Thread.new { cont.run }
376
+ q = Queue.new
377
+
378
+ start = Time.now
379
+ c.work_queue.schedule(0.02) { q << [3, Thread.current] }
380
+ c.work_queue.add { q << [1, Thread.current] }
381
+ c.work_queue.schedule(0.04) { q << [4, Thread.current] }
382
+ c.work_queue.add { q << [2, Thread.current] }
383
+
384
+ assert_equal [1, t], q.pop
385
+ assert_equal [2, t], q.pop
386
+ assert_in_delta 0.0, Time.now - start, 0.01
387
+ assert_equal [3, t], q.pop
388
+ assert_in_delta 0.02, Time.now - start, 0.01
389
+ assert_equal [4, t], q.pop
390
+ assert_in_delta 0.04, Time.now - start, 0.01
391
+
392
+ c.work_queue.add { c.close }
393
+ t.join
394
+ assert_raises(EOFError) { c.work_queue.add { } }
317
395
  end
318
396
  end
319
-
@@ -0,0 +1,141 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+
19
+ require 'test_tools'
20
+ require 'minitest/unit'
21
+ require 'socket'
22
+
23
+ # Container that listens on a random port
24
+ class TestContainer < Qpid::Proton::Container
25
+
26
+ def initialize(handler, listener_opts, id)
27
+ super handler, id
28
+ @listener = listen_io(TCPServer.open(0), ListenOnceHandler.new(listener_opts))
29
+ end
30
+ attr_reader :listener
31
+ end
32
+
33
+ class ContainerSASLTest < MiniTest::Test
34
+ include Qpid::Proton
35
+
36
+ # Handler for test client/server that sets up server and client SASL options
37
+ class SASLHandler < TestHandler
38
+
39
+ def initialize(url="amqp://", opts=nil)
40
+ super()
41
+ @url, @opts = url, opts
42
+ end
43
+
44
+ def on_container_start(container)
45
+ @client = container.connect("#{@url}:#{container.listener.port}", @opts)
46
+ end
47
+
48
+ attr_reader :auth_user
49
+
50
+ def on_connection_open(connection)
51
+ super
52
+ if connection == @client
53
+ connection.close
54
+ else
55
+ @auth_user = connection.user
56
+ end
57
+ end
58
+ end
59
+
60
+ # Generate SASL server configuration files and database, initialize proton SASL
61
+ class SASLConfig
62
+ include Qpid::Proton
63
+ attr_reader :conf_dir, :conf_file, :conf_name, :database, :error
64
+
65
+ def initialize()
66
+ if SASL.extended? # Configure cyrus SASL
67
+ @conf_dir = File.expand_path('sasl_conf')
68
+ @conf_name = "proton-server"
69
+ @database = File.join(@conf_dir, "proton.sasldb")
70
+ @conf_file = File.join(conf_dir,"#{@conf_name}.conf")
71
+ Dir::mkdir(@conf_dir) unless File.directory?(@conf_dir)
72
+ # Same user name in different realms
73
+ make_user("user", "password", "proton") # proton realm
74
+ make_user("user", "default_password") # Default realm
75
+ File.open(@conf_file, 'w') do |f|
76
+ f.write("
77
+ sasldb_path: #{database}
78
+ mech_list: EXTERNAL DIGEST-MD5 SCRAM-SHA-1 CRAM-MD5 PLAIN ANONYMOUS
79
+ ")
80
+ end
81
+ # Tell proton library to use the new configuration
82
+ SASL.config_path = conf_dir
83
+ SASL.config_name = conf_name
84
+ end
85
+ rescue => e
86
+ @error = e
87
+ end
88
+
89
+ private
90
+
91
+ SASLPASSWD = (ENV['SASLPASSWD'] or 'saslpasswd2')
92
+
93
+ def make_user(user, password, realm=nil)
94
+ realm_opt = (realm ? "-u #{realm}" : "")
95
+ cmd = "echo '#{password}' | #{SASLPASSWD} -c -p -f #{database} #{realm_opt} #{user}"
96
+ system(cmd) or raise RuntimeError.new("saslpasswd2 failed: #{cmd}")
97
+ end
98
+
99
+ INSTANCE = SASLConfig.new
100
+ end
101
+
102
+ def begin_extended_test
103
+ skip("Extended SASL not enabled") unless SASL.extended?
104
+ skip("Extended SASL setup error: #{SASLConfig::INSTANCE.error}") if SASLConfig::INSTANCE.error
105
+ end
106
+
107
+ def test_sasl_anonymous()
108
+ s = SASLHandler.new("amqp://", {:sasl_allowed_mechs => "ANONYMOUS"})
109
+ TestContainer.new(s, {:sasl_allowed_mechs => "ANONYMOUS"}, __method__).run
110
+ assert_equal "anonymous", s.connections[0].user
111
+ end
112
+
113
+ def test_sasl_plain_url()
114
+ begin_extended_test
115
+ # Use default realm with URL, should authenticate with "default_password"
116
+ opts = {:sasl_allowed_mechs => "PLAIN", :sasl_allow_insecure_mechs => true}
117
+ s = SASLHandler.new("amqp://user:default_password@", opts)
118
+ TestContainer.new(s, opts, __method__).run
119
+ assert_equal(2, s.connections.size)
120
+ assert_equal("user", s.auth_user)
121
+ end
122
+
123
+ def test_sasl_plain_options()
124
+ begin_extended_test
125
+ # Use default realm with connection options, should authenticate with "default_password"
126
+ opts = {:sasl_allowed_mechs => "PLAIN",:sasl_allow_insecure_mechs => true,
127
+ :user => 'user', :password => 'default_password' }
128
+ s = SASLHandler.new("amqp://", opts)
129
+ TestContainer.new(s, {:sasl_allowed_mechs => "PLAIN",:sasl_allow_insecure_mechs => true}, __method__).run
130
+ assert_equal(2, s.connections.size)
131
+ assert_equal("user", s.auth_user)
132
+ end
133
+
134
+ # Ensure we don't allow PLAIN if allow_insecure_mechs = true is not explicitly set
135
+ def test_disallow_insecure()
136
+ # Don't set allow_insecure_mechs, but try to use PLAIN
137
+ s = SASLHandler.new("amqp://user:password@", {:sasl_allowed_mechs => "PLAIN", :sasl_allow_insecure_mechs => true})
138
+ e = assert_raises(TestError) { TestContainer.new(s, {:sasl_allowed_mechs => "PLAIN"}, __method__).run }
139
+ assert_match(/amqp:unauthorized-access.*Authentication failed/, e.to_s)
140
+ end
141
+ end
@@ -23,7 +23,7 @@ class InteropTest < MiniTest::Test
23
23
  # Walk up the directory tree to find the tests directory.
24
24
  def get_data(name)
25
25
  path = File.join(File.dirname(__FILE__), "../../../../tests/interop/#{name}.amqp")
26
- raise "Can't find test/interop directory from #{__FILE__}" unless File.exists?(path)
26
+ raise "Can't find test/interop directory from #{__FILE__}" unless File.exist?(path)
27
27
  File.open(path, "rb") { |f| f.read }
28
28
  end
29
29
 
@@ -112,7 +112,7 @@ class TestMessagingHandler < MiniTest::Test
112
112
  def test_session_error
113
113
  d = DriverPair.new(RecordingHandler.new, RecordingHandler.new)
114
114
  d.client.connection.open
115
- s = d.client.connection.session; s.open; d.run
115
+ s = d.client.connection.default_session; s.open; d.run
116
116
  assert_equal [:on_connection_open, :on_session_open], d.client.handler.names
117
117
  assert_equal [:on_connection_open, :on_session_open], d.server.handler.names
118
118
  d.clear
data/tests/test_tools.rb CHANGED
@@ -28,7 +28,8 @@ rescue NameError # For older versions of MiniTest
28
28
  MiniTest::Test = MiniTest::Unit::TestCase
29
29
  end
30
30
 
31
- class TestError < Exception; end
31
+ class TestError < RuntimeError; end # Normal error
32
+ class TestException < Exception; end # Not caught by default rescue
32
33
 
33
34
  def wait_port(port, timeout=5)
34
35
  deadline = Time.now + timeout
@@ -95,15 +96,15 @@ class TestHandler < Qpid::Proton::MessagingHandler
95
96
  end
96
97
  end
97
98
 
98
- # ListenHandler that closes the Listener after first accept
99
+ # ListenHandler that closes the Listener after first (or n) accepts
99
100
  class ListenOnceHandler < Qpid::Proton::Listener::Handler
100
- def on_error(l, e) raise TestError, e.inspect; end
101
- def on_accept(l) l.close; super; end
101
+ def initialize(opts, n=1) super(opts); @n=n; end
102
+ def on_error(l, e) raise e; end
103
+ def on_accept(l) l.close if (@n -= 1).zero?; super; end
102
104
  end
103
105
 
104
106
  # Add port/url to Listener, assuming a TCP socket
105
107
  class Qpid::Proton::Listener
106
- def port() to_io.addr[1]; end
107
108
  def url() "amqp://:#{port}"; end
108
109
  end
109
110
 
@@ -145,3 +146,27 @@ DriverPair = Struct.new(:client, :server) do
145
146
  end
146
147
  end
147
148
 
149
+ # Container that listens on a random port
150
+ class ServerContainer < Qpid::Proton::Container
151
+ include Qpid::Proton
152
+
153
+ def initialize(id=nil, listener_opts=nil, n=1)
154
+ super id
155
+ @listener = listen_io(TCPServer.open(0), ListenOnceHandler.new(listener_opts, n))
156
+ end
157
+
158
+ attr_reader :listener
159
+
160
+ def port() @listener.port; end
161
+ def url() "amqp://:#{port}"; end
162
+ end
163
+
164
+ class ServerContainerThread < ServerContainer
165
+ def initialize(id=nil, listener_opts=nil, n=1)
166
+ super
167
+ @thread = Thread.new { run }
168
+ end
169
+
170
+ attr_reader :thread
171
+ def join() @thread.join; end
172
+ end
data/tests/test_uri.rb CHANGED
@@ -67,6 +67,8 @@ class TestURI < MiniTest::Test
67
67
  assert_equal URI("amqp://h:1"), uri("h:1")
68
68
  assert_equal URI("amqp://h"), uri("h")
69
69
  assert_equal URI("amqp://h"), uri("h:")
70
+ assert_equal ["amqp", nil, "", 5672, ""], uri_parts("")
71
+ assert_equal ["amqp", nil, "", 5672, ""], uri_parts(":")
70
72
  assert_equal ["amqp", nil, "", 1, ""], uri_parts(":1")
71
73
  assert_equal ["amqp", nil, "", 1, ""], uri_parts("amqp://:1")
72
74
  assert_equal URI("amqp://[::1:2]:1"), uri("[::1:2]:1")