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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +3 -2
- data.tar.gz.sig +0 -0
- data/Authentication.md +30 -0
- data/History.md +36 -0
- data/README.md +70 -6
- data/ext/zyre_ext/cert.c +504 -0
- data/ext/zyre_ext/event.c +105 -33
- data/ext/zyre_ext/extconf.rb +5 -0
- data/ext/zyre_ext/node.c +283 -38
- data/ext/zyre_ext/poller.c +6 -7
- data/ext/zyre_ext/zyre_ext.c +104 -1
- data/ext/zyre_ext/zyre_ext.h +25 -2
- data/lib/observability/instrumentation/zyre.rb +4 -4
- data/lib/zyre.rb +38 -14
- data/lib/zyre/cert.rb +75 -0
- data/lib/zyre/event.rb +9 -0
- data/lib/zyre/event/leader.rb +18 -0
- data/lib/zyre/event/stop.rb +5 -0
- data/lib/zyre/testing.rb +36 -3
- data/spec/observability/instrumentation/zyre_spec.rb +2 -2
- data/spec/spec_helper.rb +4 -0
- data/spec/zyre/cert_spec.rb +218 -0
- data/spec/zyre/event_spec.rb +62 -0
- data/spec/zyre/node_spec.rb +195 -8
- data/spec/zyre/testing_spec.rb +72 -0
- data/spec/zyre_spec.rb +12 -0
- metadata +22 -17
- metadata.gz.sig +0 -0
data/lib/zyre/event/stop.rb
CHANGED
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
|
-
|
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[:
|
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[:
|
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
|
data/spec/zyre/event_spec.rb
CHANGED
@@ -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
|
data/spec/zyre/node_spec.rb
CHANGED
@@ -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.
|
166
|
-
expect( ev
|
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
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
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
|
194
|
-
expect( ev.msg ).to eq(
|
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
|
|