zyre 0.1.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,4 +6,9 @@ require 'zyre/event' unless defined?( Zyre::Event )
6
6
 
7
7
  class Zyre::Event::Stop < Zyre::Event
8
8
 
9
+ ### Provide the details of the inspect message.
10
+ def inspect_details
11
+ return " node is stopping"
12
+ end
13
+
9
14
  end # class Zyre::Event::Stop
data/lib/zyre/testing.rb CHANGED
@@ -154,8 +154,12 @@ module Zyre::Testing
154
154
 
155
155
 
156
156
  ### Generate a SHOUT event.
157
- def shout( **overrides )
157
+ def shout( group=nil, *msg, **overrides )
158
158
  uuid = overrides.delete( :peer_uuid ) || self.peer_uuid
159
+
160
+ overrides[:group] = group if group
161
+ overrides[:msg] = msg if !msg.empty?
162
+
159
163
  config = {
160
164
  peer_name: self.peer_name,
161
165
  group: self.group,
@@ -166,9 +170,14 @@ module Zyre::Testing
166
170
  end
167
171
 
168
172
 
169
- ### Generate a WHISPER event.
170
- def whisper( **overrides )
173
+ ### Generate a WHISPER event. The first positional argument, which would
174
+ ### normally be the UUID of the peer to WHISPER to is ignored, since the
175
+ ### generated event's +peer_uuid+ is the sending node's not the receiving one's.
176
+ def whisper( _ignored=nil, *msg, **overrides )
171
177
  uuid = overrides.delete( :peer_uuid ) || self.peer_uuid
178
+
179
+ overrides[:msg] = msg if !msg.empty?
180
+
172
181
  config = {
173
182
  peer_name: self.peer_name,
174
183
  msg: self.msg
@@ -212,6 +221,18 @@ module Zyre::Testing
212
221
  end
213
222
 
214
223
 
224
+ ### Generate a LEADER event.
225
+ def leader( **overrides )
226
+ uuid = overrides.delete( :peer_uuid ) || self.peer_uuid
227
+ config = {
228
+ peer_name: self.peer_name,
229
+ group: self.group
230
+ }.merge( overrides )
231
+
232
+ return Zyre::Event.synthesize( :leader, uuid, **config )
233
+ end
234
+
235
+
215
236
  ### Generate an EXIT event.
216
237
  def exit( **overrides )
217
238
  uuid = overrides.delete( :peer_uuid ) || self.peer_uuid
@@ -222,6 +243,17 @@ module Zyre::Testing
222
243
  return Zyre::Event.synthesize( :exit, uuid, **config )
223
244
  end
224
245
 
246
+
247
+ ### Generate a STOP event.
248
+ def stop( **overrides )
249
+ uuid = overrides.delete( :peer_uuid ) || self.peer_uuid
250
+ config = {
251
+ peer_name: self.peer_name
252
+ }.merge( overrides )
253
+
254
+ return Zyre::Event.synthesize( :stop, uuid, **config )
255
+ end
256
+
225
257
  end # class EventFactory
226
258
 
227
259
 
@@ -251,6 +283,7 @@ module Zyre::Testing
251
283
  def started_node( name=nil )
252
284
  node = Zyre::Node.new( name )
253
285
  node.endpoint = 'inproc://node-test-%s' % [ SecureRandom.hex(16) ]
286
+
254
287
  yield( node ) if block_given?
255
288
 
256
289
  if @gossip_endpoint
@@ -31,7 +31,7 @@ RSpec.describe 'Observability::Instrumentation::Zyre', :observability do
31
31
  events = Observability.observer.sender.find_events( 'zyre.node.whisper' )
32
32
  expect( events.length ).to eq( 1 )
33
33
  expect( events.first[:peer_uuid] ).to eq( node2.uuid )
34
- expect( events.first[:message] ).to eq( 'a peer-to-peer message' )
34
+ expect( events.first[:messages] ).to eq( ['a peer-to-peer message'] )
35
35
 
36
36
  end
37
37
 
@@ -49,7 +49,7 @@ RSpec.describe 'Observability::Instrumentation::Zyre', :observability do
49
49
  events = Observability.observer.sender.find_events( 'zyre.node.shout' )
50
50
  expect( events.length ).to eq( 1 )
51
51
  expect( events.first[:group] ).to eq( 'observer-testing' )
52
- expect( events.first[:message] ).to eq( 'a peer-to-peer message' )
52
+ expect( events.first[:messages] ).to eq( ['a peer-to-peer message'] )
53
53
  end
54
54
 
55
55
  end
data/spec/spec_helper.rb CHANGED
@@ -55,6 +55,10 @@ RSpec.configure do |config|
55
55
  Zyre::Testing.check_fdmax
56
56
  end
57
57
 
58
+ config.filter_run_excluding( :draft_api ) unless Zyre.has_draft_apis?
59
+ config.filter_run_excluding( :czmq_draft_api ) unless Zyre.has_draft_czmq_apis?
60
+ config.filter_run_excluding( :no_czmq_draft_api ) if Zyre.has_draft_czmq_apis?
61
+
58
62
  config.include( Zyre::Testing )
59
63
  config.include( Loggability::SpecHelpers )
60
64
  end
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env rspec -cfd
2
+
3
+ require_relative '../spec_helper'
4
+
5
+ require 'tempfile'
6
+
7
+ require 'zyre/cert'
8
+
9
+
10
+ RSpec.describe( Zyre::Cert ) do
11
+
12
+ let( :cert_file ) { Tempfile.new('zyre_cert_test') }
13
+
14
+
15
+ it "can be created in-memory" do
16
+ expect( described_class.new ).to be_a( described_class )
17
+ end
18
+
19
+
20
+ it "can be created from a keypair" do
21
+ cert = described_class.new
22
+
23
+ result = described_class.from( cert.public_key, cert.private_key )
24
+
25
+ expect( result ).to be_a( described_class )
26
+ expect( result ).to eq( cert )
27
+ end
28
+
29
+
30
+ it "can be saved to and loaded from a file" do
31
+ cert = described_class.new
32
+ cert.save( cert_file.path )
33
+
34
+ result = described_class.load( cert_file.path )
35
+
36
+ expect( result ).to be_a( described_class )
37
+ expect( result ).to eq( cert )
38
+ end
39
+
40
+
41
+ it "can be saved as a public cert to and loaded from a file" do
42
+ cert = described_class.new
43
+ cert.save_public( cert_file.path )
44
+
45
+ result = described_class.load( cert_file.path )
46
+
47
+ expect( result ).to be_a( described_class )
48
+ expect( result.public_key ).to eq( cert.public_key )
49
+ expect( result.secret_key ).to eq( Zyre::Cert::EMPTY_KEY )
50
+ end
51
+
52
+
53
+ it "can be saved as a secret cert to and loaded from a file" do
54
+ cert = described_class.new
55
+ cert.save_secret( cert_file.path )
56
+
57
+ result = described_class.load( cert_file.path )
58
+
59
+ expect( result ).to be_a( described_class )
60
+ expect( result ).to eq( cert )
61
+ end
62
+
63
+
64
+ it "knows what its public key is" do
65
+ cert = described_class.new
66
+
67
+ key = cert.public_key
68
+
69
+ expect( key.encoding ).to eq( Encoding::ASCII_8BIT )
70
+ expect( key.bytesize ).to eq( 32 )
71
+ end
72
+
73
+
74
+ it "knows what its secret key is" do
75
+ cert = described_class.new
76
+
77
+ key = cert.secret_key
78
+
79
+ expect( key.encoding ).to eq( Encoding::ASCII_8BIT )
80
+ expect( key.bytesize ).to eq( 32 )
81
+ end
82
+
83
+
84
+ it "knows what its Z85-armored public key is" do
85
+ cert = described_class.new
86
+
87
+ key = cert.public_txt
88
+
89
+ expect( key.encoding ).to eq( Encoding::US_ASCII )
90
+ expect( key.length ).to eq( 40 )
91
+ end
92
+
93
+
94
+ it "knows what its Z85-armored secret key is" do
95
+ cert = described_class.new
96
+
97
+ key = cert.secret_txt
98
+
99
+ expect( key.encoding ).to eq( Encoding::US_ASCII )
100
+ expect( key.length ).to eq( 40 )
101
+ end
102
+
103
+
104
+ it "can set metadata on the cert" do
105
+ cert = described_class.new
106
+
107
+ cert[ :username ] = 'timmy'
108
+
109
+ expect( cert['username'] ).to eq( 'timmy' )
110
+ end
111
+
112
+
113
+ it "stringifies non-string metadata on the cert" do
114
+ cert = described_class.new
115
+
116
+ cert[ :euid ] = 12
117
+
118
+ expect( cert[:euid] ).to eq( '12' )
119
+ end
120
+
121
+
122
+ it "fetches non-existant values as nil" do
123
+ cert = described_class.new
124
+
125
+ expect( cert[:nonexistant_key] ).to be_nil
126
+ end
127
+
128
+
129
+ it "silently ignores overwrites of a metadata value if not built with Drafts", :no_czmq_draft_api do
130
+ cert = described_class.new
131
+
132
+ cert.set_meta( 'foo', 'bar' )
133
+ cert.set_meta( 'foo', 'baz' )
134
+
135
+ expect( cert.meta('foo') ).to eq( 'bar' )
136
+ end
137
+
138
+
139
+ it "can overwrite a metadata value if built with Drafts", :czmq_draft_api do
140
+ cert = described_class.new
141
+
142
+ cert.set_meta( 'foo', 'bar' )
143
+ cert.set_meta( 'foo', 'baz' )
144
+
145
+ expect( cert.meta('foo') ).to eq( 'baz' )
146
+ end
147
+
148
+
149
+ it "knows what metadata has been set on the cert" do
150
+ cert = described_class.new
151
+
152
+ cert[ :euid ] = 0
153
+ cert[ :username ] = 'jrandom'
154
+ cert[ 'firstname' ] = 'James'
155
+ cert[ :lastname ] = 'Random'
156
+ cert[ 'key 2' ] = 'cf67c750-c704-4ef7-ab83-ecb2cd2e326c'
157
+
158
+ expect( cert.meta_keys ).
159
+ to contain_exactly( 'euid', 'username', 'firstname', 'lastname', 'key 2' )
160
+ end
161
+
162
+
163
+ it "can delete one of its metadata key-value pairs", :czmq_draft_api do
164
+ cert = described_class.new
165
+
166
+ cert[ :euid ] = 0
167
+ cert[ :username ] = 'jrandom'
168
+ cert[ 'firstname' ] = 'James'
169
+ cert[ :lastname ] = 'Random'
170
+ cert[ 'key 2' ] = 'cf67c750-c704-4ef7-ab83-ecb2cd2e326c'
171
+
172
+ cert.delete( 'lastname' )
173
+
174
+ expect( cert.meta_keys ).
175
+ to contain_exactly( 'euid', 'username', 'firstname', 'key 2' )
176
+ end
177
+
178
+
179
+ it "can return all of its metadata as a Hash" do
180
+ cert = described_class.new
181
+
182
+ cert[ :euid ] = 0
183
+ cert[ :username ] = 'jrandom'
184
+ cert[ 'firstname' ] = 'James'
185
+ cert[ :lastname ] = 'Random'
186
+ cert[ 'key 2' ] = 'cf67c750-c704-4ef7-ab83-ecb2cd2e326c'
187
+
188
+ expect( cert.meta_hash ).to eq({
189
+ 'euid' => '0',
190
+ 'username' => 'jrandom',
191
+ 'firstname' => 'James',
192
+ 'lastname' => 'Random',
193
+ 'key 2' => 'cf67c750-c704-4ef7-ab83-ecb2cd2e326c',
194
+ })
195
+ end
196
+
197
+
198
+ it "can be applied to a Zyre node", :draft_apis do
199
+ node = instance_double( Zyre::Node )
200
+ cert = Zyre::Cert.new
201
+
202
+ expect( node ).to receive( :zcert= ).with( cert )
203
+ cert.apply( node )
204
+ end
205
+
206
+
207
+ it "can be duplicated" do
208
+ cert = described_class.new
209
+ cert[ :node_id ] = '05343500-f908-4903-9441-e648eb1754ec'
210
+ cert[ :node_order ] = 1
211
+
212
+ other = cert.dup
213
+
214
+ expect( other.object_id ).not_to eq( cert.object_id )
215
+ end
216
+
217
+
218
+ end
@@ -135,6 +135,68 @@ RSpec.describe( Zyre::Event ) do
135
135
  expect( result.peer_name ).to eq( 'S-' + peer_uuid[0, 6] )
136
136
  end
137
137
 
138
+
139
+ it "can generate a SHOUT event with binary data in its msg" do
140
+ data = "\xBF\x8D\x00\x9D\xDDg\x03_\xF2Nr\x01\xF0I8Au\x95\xC6L\xD37".b +
141
+ "\xFFt\xC7\xE1\xC0<l\x17\xA2N\xE3".b
142
+
143
+ result = described_class.synthesize( :SHOUT, peer_uuid, group: 'stream', msg: data )
144
+
145
+ expect( result ).to be_a( described_class::Shout )
146
+ expect( result.peer_uuid ).to eq( peer_uuid )
147
+ expect( result.group ).to eq( 'stream' )
148
+ expect( result.msg ).to eq( data )
149
+ end
150
+
151
+
152
+ it "raises when creating a WHISPER with no msg" do
153
+ expect {
154
+ described_class.synthesize( :WHISPER, peer_uuid )
155
+ }.to raise_error( ArgumentError, /missing required field :msg/i )
156
+ end
157
+
158
+
159
+ it "raises when creating a SHOUT with no msg" do
160
+ expect {
161
+ described_class.synthesize( :SHOUT, peer_uuid, group: 'agroup' )
162
+ }.to raise_error( ArgumentError, /missing required field :msg/i )
163
+ end
164
+
165
+
166
+ it "raises when creating a SHOUT with no group" do
167
+ expect {
168
+ described_class.synthesize( :SHOUT, peer_uuid, msg: 'amsg' )
169
+ }.to raise_error( ArgumentError, /missing required field :group/i )
170
+ end
171
+
172
+
173
+ it "raises when creating an ENTER with no peer_addr" do
174
+ expect {
175
+ described_class.synthesize( :ENTER, peer_uuid )
176
+ }.to raise_error( ArgumentError, /missing required field :peer_addr/i )
177
+ end
178
+
179
+
180
+ it "raises when creating a JOIN with no group" do
181
+ expect {
182
+ described_class.synthesize( :JOIN, peer_uuid )
183
+ }.to raise_error( ArgumentError, /missing required field :group/i )
184
+ end
185
+
186
+
187
+ it "raises when creating a LEAVE with no group" do
188
+ expect {
189
+ described_class.synthesize( :LEAVE, peer_uuid )
190
+ }.to raise_error( ArgumentError, /missing required field :group/i )
191
+ end
192
+
193
+
194
+ it "raises when creating an unknown type of event" do
195
+ expect {
196
+ described_class.synthesize( :BACKUP, peer_uuid )
197
+ }.to raise_error( ArgumentError, /don't know how to create :BACKUP events/i )
198
+ end
199
+
138
200
  end
139
201
 
140
202
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../spec_helper'
4
4
 
5
+ require 'securerandom'
5
6
  require 'zyre/node'
6
7
 
7
8
 
@@ -17,6 +18,13 @@ RSpec.describe( Zyre::Node ) do
17
18
  I sound my barbaric yawp over the roofs of the world.
18
19
  END_SHOUT
19
20
 
21
+ TEST_BINARY_STRING_PARTS = [
22
+ SecureRandom.bytes( 128 ),
23
+ "\x00".b,
24
+ SecureRandom.bytes( 128 )
25
+ ]
26
+ TEST_BINARY_STRING = TEST_BINARY_STRING_PARTS.join( '' )
27
+
20
28
 
21
29
  it "can be created anonymously" do
22
30
  node = described_class.new
@@ -162,13 +170,30 @@ RSpec.describe( Zyre::Node ) do
162
170
  node1.wait_for( :ENTER, peer_uuid: node2.uuid )
163
171
  node1.whisper( node2.uuid, TEST_WHISPER )
164
172
 
165
- ev = node2.recv
166
- expect( ev.type ).to eq( :ENTER )
173
+ ev = node2.wait_for( :WHISPER )
174
+ expect( ev ).to be_a( Zyre::Event::Whisper )
175
+ expect( ev ).to_not be_multipart
176
+ msg = ev.msg
177
+ expect( msg.encoding ).to eq( Encoding::ASCII_8BIT )
178
+ msg = msg.dup.force_encoding( 'utf-8' )
179
+ expect( msg ).to eq( TEST_WHISPER )
180
+ expect( ev.multipart_msg ).to eq( [TEST_WHISPER.b] )
181
+ end
167
182
 
168
- ev = node2.recv
169
- expect( ev.type ).to eq( :WHISPER )
170
- expect( ev.msg.encoding ).to eq( Encoding::UTF_8 )
171
- expect( ev.msg ).to eq( TEST_WHISPER )
183
+
184
+ it "can whisper a multipart message to another node" do
185
+ node1 = started_node()
186
+ node2 = started_node()
187
+
188
+ node1.wait_for( :ENTER, peer_uuid: node2.uuid )
189
+ node1.whisper( node2.uuid, 'poetry.snippet', TEST_WHISPER )
190
+
191
+ ev = node2.wait_for( :WHISPER, peer_uuid: node1.uuid )
192
+ expect( ev ).to be_a( Zyre::Event::Whisper )
193
+ expect( ev ).to be_multipart
194
+ expect( ev.msg.encoding ).to eq( Encoding::ASCII_8BIT )
195
+ expect( ev.msg ).to eq( 'poetry.snippet' )
196
+ expect( ev.multipart_msg ).to eq( ['poetry.snippet', TEST_WHISPER] )
172
197
  end
173
198
 
174
199
 
@@ -190,8 +215,96 @@ RSpec.describe( Zyre::Node ) do
190
215
 
191
216
  ev = node2.recv
192
217
  expect( ev ).to be_a( Zyre::Event::Shout )
193
- expect( ev.msg.encoding ).to eq( Encoding::UTF_8 )
194
- expect( ev.msg ).to eq( TEST_SHOUT )
218
+ expect( ev ).to_not be_multipart
219
+ expect( ev.msg.encoding ).to eq( Encoding::ASCII_8BIT )
220
+ expect( ev.msg ).to eq( TEST_SHOUT.b )
221
+ expect( ev.multipart_msg ).to eq( [TEST_SHOUT.b] )
222
+ end
223
+
224
+
225
+ it "can shout a multipart message to a group of nodes" do
226
+ node1 = started_node()
227
+ node2 = started_node()
228
+ node3 = started_node()
229
+
230
+ node1.join( 'ROOFTOP' )
231
+ node2.join( 'ROOFTOP' )
232
+ node3.join( 'ROOFTOP' )
233
+
234
+ 2.times do
235
+ node1.wait_for( :JOIN, group: 'ROOFTOP' )
236
+ end
237
+ node1.shout( 'ROOFTOP', "random.poetry", TEST_SHOUT )
238
+
239
+ ev = node2.wait_for( :SHOUT, group: 'ROOFTOP' )
240
+ expect( ev ).to be_a( Zyre::Event::Shout )
241
+ expect( ev ).to be_multipart
242
+ expect( ev.msg ).to eq( 'random.poetry'.b )
243
+ expect( ev.multipart_msg ).to eq( ['random.poetry'.b, TEST_SHOUT.b] )
244
+ end
245
+
246
+
247
+ it "can shout a binary message to a group of nodes" do
248
+ node1 = started_node()
249
+ node2 = started_node()
250
+ node3 = started_node()
251
+
252
+ node1.join( 'stream' )
253
+ node2.join( 'stream' )
254
+ node3.join( 'stream' )
255
+
256
+ 2.times do
257
+ node1.wait_for( :JOIN, group: 'stream' )
258
+ end
259
+ node1.shout( 'stream', TEST_BINARY_STRING )
260
+
261
+ ev = node2.wait_for( :SHOUT, group: 'stream' )
262
+ expect( ev ).to be_a( Zyre::Event::Shout )
263
+ expect( ev ).to_not be_multipart
264
+ expect( ev.msg ).to eq( TEST_BINARY_STRING )
265
+
266
+ ev = node3.wait_for( :SHOUT, group: 'stream' )
267
+ expect( ev ).to be_a( Zyre::Event::Shout )
268
+ expect( ev ).to_not be_multipart
269
+ expect( ev.msg ).to eq( TEST_BINARY_STRING )
270
+ end
271
+
272
+
273
+ it "can shout a multipart binary message to a group of nodes" do
274
+ node1 = started_node()
275
+ node2 = started_node()
276
+ node3 = started_node()
277
+
278
+ node1.join( 'stream' )
279
+ node2.join( 'stream' )
280
+ node3.join( 'stream' )
281
+
282
+ 2.times do
283
+ node1.wait_for( :JOIN, group: 'stream' )
284
+ end
285
+ node1.shout( 'stream', *TEST_BINARY_STRING_PARTS )
286
+
287
+ ev = node2.wait_for( :SHOUT, group: 'stream' )
288
+ expect( ev ).to be_a( Zyre::Event::Shout )
289
+ expect( ev ).to be_multipart
290
+ expect( ev.msg ).to eq( TEST_BINARY_STRING_PARTS[0] )
291
+ expect( ev.multipart_msg ).to eq( TEST_BINARY_STRING_PARTS )
292
+
293
+ ev = node3.wait_for( :SHOUT, group: 'stream' )
294
+ expect( ev ).to be_a( Zyre::Event::Shout )
295
+ expect( ev ).to be_multipart
296
+ expect( ev.msg ).to eq( TEST_BINARY_STRING_PARTS[0] )
297
+ expect( ev.multipart_msg ).to eq( TEST_BINARY_STRING_PARTS )
298
+ end
299
+
300
+
301
+ it "handles unstringifiable messages gracefully" do
302
+ node1 = started_node()
303
+ node1.join( 'ROOFTOP' )
304
+
305
+ expect {
306
+ node1.shout( 'ROOFTOP', nil )
307
+ }.to raise_error( TypeError, /nil/i )
195
308
  end
196
309
 
197
310
 
@@ -236,6 +349,22 @@ RSpec.describe( Zyre::Node ) do
236
349
  end
237
350
 
238
351
 
352
+ it "knows what the headers of its peers are" do
353
+ node1 = started_node()
354
+ node2 = started_node() do |node|
355
+ node.headers = {
356
+ protocol_version: 2,
357
+ content_type: 'application/javascript',
358
+ start_time: Time.now.to_f
359
+ }
360
+ end
361
+
362
+ node1.wait_for( :enter, peer_uuid: node2.uuid )
363
+
364
+ expect( node1.peer_header_value(node2.uuid, 'Protocol-version') ).to eq( '2' )
365
+ end
366
+
367
+
239
368
  it "has a blocking iterator" do
240
369
  node = described_class.new
241
370
 
@@ -352,5 +481,63 @@ RSpec.describe( Zyre::Node ) do
352
481
  end
353
482
 
354
483
 
484
+ context "draft APIs", :draft_api do
485
+
486
+ it "can specify a port for the ROUTER socket" do
487
+ node = described_class.new
488
+
489
+ expect {
490
+ node.beacon_peer_port = 16181
491
+ }.to_not raise_error()
492
+ end
493
+
494
+
495
+ it "can specify that a node should try to assume leadership of a particular group" do
496
+ node1 = started_node( 'node1' ) do |node|
497
+ # node.verbose!
498
+ node.set_contest_in_group( "GROUP_1" )
499
+ node.join( 'GROUP_1' )
500
+ node.join( 'GROUP_2' )
501
+ node.join( 'GROUP_3' )
502
+ end
503
+
504
+ node2 = started_node( 'node2' ) do |node|
505
+ # node.verbose!
506
+ node.set_contest_in_group( 'GROUP_1' )
507
+ node.set_contest_in_group( 'GROUP_2' )
508
+ node.join( 'GROUP_1' )
509
+ node.join( 'GROUP_2' )
510
+ node.join( 'GROUP_3' )
511
+ end
512
+
513
+ poller = Zyre::Poller.new( node1, node2 )
514
+
515
+ leaders = {}
516
+ leadership_message_count = 0
517
+ while ( node = poller.wait(0.5) )
518
+ event = node.recv
519
+
520
+ if event.type == :LEADER
521
+ leadership_message_count += 1
522
+ leaders[ event.group ] = event.peer_uuid
523
+ end
524
+ end
525
+
526
+ expect( leadership_message_count ).to eq( 4 )
527
+ expect( leaders.keys ).to contain_exactly( 'GROUP_1', 'GROUP_2' )
528
+ expect( leaders['GROUP_1'] ).to eq( node1.uuid ).or( eq node2.uuid )
529
+ expect( leaders['GROUP_2'] ).to eq( node2.uuid )
530
+ end
531
+
532
+
533
+ it "supports CURVE authentication"
534
+
535
+ it "supports advertised_endpoint="
536
+ it "supports gossip_connect_curve"
537
+ it "supports gossip_unpublish"
538
+ it "supports require_peer"
539
+
540
+ end
541
+
355
542
  end
356
543