zyre 0.1.0 → 0.4.1

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.
@@ -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