zss 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/zss/socket.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'ffi-rzmq'
2
+ require 'timeout'
2
3
 
3
4
  module ZSS
4
5
  class Socket
@@ -39,30 +40,32 @@ module ZSS
39
40
  private
40
41
 
41
42
  def context
42
- context = ZMQ::Context.create(1)
43
- fail Socket::Error, 'failed to create context' unless context
44
- yield context
43
+ ctx = ZMQ::Context.create(1)
44
+ fail Socket::Error, 'failed to create create_context' unless ctx
45
+ yield ctx
45
46
  ensure
46
- check!(context.terminate) if context
47
+ check!(ctx.terminate) if ctx
47
48
  end
48
49
 
49
- def socket context
50
+ def socket(context)
50
51
  socket = context.socket ZMQ::DEALER
51
52
  fail Socket::Error, 'failed to create socket' unless socket
52
53
  socket.identity = "#{identity}##{SecureRandom.uuid}"
53
54
  socket.setsockopt(ZMQ::LINGER, 0)
54
- socket.bind(socket_address)
55
+ socket.connect(socket_address)
55
56
  yield socket
56
57
  ensure
57
- check! socket.close if socket
58
+ check!(socket.close) if socket
58
59
  end
59
60
 
60
61
  def send_message socket, message
61
62
  frames = message.to_frames
62
- first = frames.shift
63
+
64
+ # if it's a reply should send identity
65
+ frames.shift if message.req?
63
66
  last = frames.pop
64
67
  frames.each { |f| check! socket.send_string f.to_s, ZMQ::SNDMORE }
65
- check! socket.send_string last
68
+ check! socket.send_string last.to_s
66
69
  end
67
70
 
68
71
  def receive_message socket
data/lib/zss/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ZSS
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
@@ -16,7 +16,8 @@ describe ZSS::Client do
16
16
  end
17
17
 
18
18
  after :each do
19
- @broker.terminate if @broker
19
+ return unless @broker
20
+ @broker.join
20
21
  @broker = nil
21
22
  end
22
23
 
@@ -25,7 +26,7 @@ describe ZSS::Client do
25
26
  end
26
27
 
27
28
  it('returns service response') do
28
- @broker = run_broker(broker_frontend) do |msg|
29
+ @broker = run_broker_for_client(broker_frontend) do |msg|
29
30
  expect(msg.payload).to eq("ping")
30
31
  msg.status = 200
31
32
  msg.payload = "PONG"
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'spec_broker_helper'
3
+ require 'em-zeromq'
4
+ require 'zss/service'
5
+
6
+ describe ZSS::Service do
7
+ include BrokerHelper
8
+
9
+ class DummyService
10
+ def ping payload, headers
11
+ headers[:took] = "0s"
12
+ return "PONG"
13
+ end
14
+ end
15
+
16
+ let(:broker_backend) { "ipc://socket_test" }
17
+
18
+ let(:config) do
19
+ Hashie::Mash.new(
20
+ backend: broker_backend,
21
+ heartbeat: 2000
22
+ )
23
+ end
24
+
25
+ let(:address) { ZSS::Message::Address.new(sid: "pong", verb: "ping") }
26
+
27
+ let(:message) { ZSS::Message.new(address: address, payload: "PING") }
28
+
29
+ subject { described_class.new(:pong, config) }
30
+
31
+ it('handles request') do
32
+ EM.run do
33
+ run_broker_for_service(broker_backend, message) do |msg|
34
+ expect(msg.payload).to eq("PONG")
35
+ expect(msg.status).to eq(200)
36
+ expect(msg.headers).to eq({ "took" => "0s" })
37
+ subject.stop
38
+ EM.stop
39
+ end
40
+
41
+ service = DummyService.new
42
+ subject.add_route(service, :ping)
43
+ subject.run
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -14,7 +14,8 @@ describe ZSS::Socket do
14
14
  let(:message) { ZSS::Message.new(address: address, payload: "PING") }
15
15
 
16
16
  after :each do
17
- @broker.terminate if @broker
17
+ return unless @broker
18
+ @broker.join
18
19
  @broker = nil
19
20
  end
20
21
 
@@ -23,7 +24,7 @@ describe ZSS::Socket do
23
24
  subject { ZSS::Socket.new(config) }
24
25
 
25
26
  it('returns service response') do
26
- @broker = run_broker(broker_frontend) do |msg|
27
+ @broker = run_broker_for_client(broker_frontend) do |msg|
27
28
  msg.status = 200
28
29
  msg.payload = "PONG"
29
30
  end
@@ -1,11 +1,11 @@
1
- require 'ffi-rzmq'
1
+ require 'em-zeromq'
2
2
  require 'msgpack'
3
3
 
4
4
  module BrokerHelper
5
5
 
6
6
  LOG_BROKER_INFO = false
7
7
 
8
- def run_broker(endpoint)
8
+ def run_broker_for_client(endpoint)
9
9
 
10
10
  Thread.new do
11
11
  puts "broker running" if LOG_BROKER_INFO
@@ -16,7 +16,7 @@ module BrokerHelper
16
16
  context = ZMQ::Context.create
17
17
  socket = context.socket ZMQ::ROUTER
18
18
  socket.setsockopt(ZMQ::LINGER, 0)
19
- socket.connect(endpoint)
19
+ socket.bind(endpoint)
20
20
  puts "broker connected" if LOG_BROKER_INFO
21
21
 
22
22
  socket.recv_strings(frames = [])
@@ -31,11 +31,8 @@ module BrokerHelper
31
31
  frames = msg.to_frames
32
32
  socket.send_strings(frames)
33
33
 
34
- puts "broker sending #{frames}" if LOG_BROKER_INFO
34
+ puts "broker reply #{frames}" if LOG_BROKER_INFO
35
35
  end
36
-
37
- Thread.stop
38
-
39
36
  rescue => e
40
37
  puts "WTF => #{e}"
41
38
  ensure
@@ -49,4 +46,55 @@ module BrokerHelper
49
46
  end
50
47
 
51
48
  end
49
+
50
+ def run_broker_for_service(endpoint, send_message = nil)
51
+ context = EM::ZeroMQ::Context.new(1)
52
+ socket = context.socket(ZMQ::ROUTER)
53
+ socket.setsockopt(ZMQ::LINGER, 0)
54
+ socket.on(:message) do |*frames|
55
+ frames = get_frames_and_close_message(frames)
56
+ puts "broker received #{frames}" if LOG_BROKER_INFO
57
+ service_id = frames.shift if frames.length == 9
58
+ msg = ZSS::Message.parse(frames)
59
+
60
+ if msg.address.sid == 'SMI'
61
+ handle_smi_verbs(socket, msg, send_message)
62
+ else
63
+ puts "check service response" if LOG_BROKER_INFO
64
+ yield(msg) if block_given?
65
+ end
66
+
67
+ end
68
+
69
+ socket.bind(endpoint)
70
+ end
71
+
72
+ private
73
+
74
+ def handle_smi_verbs(socket, msg, send_message)
75
+
76
+ if msg.address.verb == 'UP'
77
+ msg.status = 200
78
+ msg.type = ZSS::Message::Type::REP
79
+ puts "broker reply to UP" if LOG_BROKER_INFO
80
+ socket.send_msg(*msg.to_frames)
81
+
82
+ if send_message
83
+ send_message.identity = msg.identity
84
+ puts "broker sending emulated client request" if LOG_BROKER_INFO
85
+ # emulate client request
86
+ socket.send_msg(*send_message.to_frames)
87
+ end
88
+
89
+ end
90
+ end
91
+
92
+ def get_frames_and_close_message(frames)
93
+ frames.map do |frame|
94
+ out_frame = frame.copy_out_string
95
+ frame.close
96
+ out_frame
97
+ end
98
+ end
99
+
52
100
  end
data/spec/spec_helper.rb CHANGED
@@ -15,5 +15,14 @@ $:.push '.'
15
15
  require 'rspec'
16
16
  require 'pry'
17
17
  require 'zss'
18
+ require 'timeout'
18
19
 
19
20
  Dir['spec/support/**/*.rb'].each &method(:require)
21
+
22
+ RSpec.configure do |c|
23
+ c.around(:each) do |example|
24
+ Timeout::timeout(2) {
25
+ example.run
26
+ }
27
+ end
28
+ end
@@ -52,7 +52,7 @@ describe ZSS::Client do
52
52
  timeout: 1000
53
53
  )
54
54
  )
55
- subject.call(:ping)
55
+ subject.call(:ping, nil)
56
56
  end
57
57
 
58
58
  context('on success') do
@@ -6,22 +6,35 @@ describe ZSS::Error do
6
6
  Hashie::Mash.new(userMessage: "user info", developerMessage: "dev info")
7
7
  end
8
8
 
9
- subject do
10
- described_class.new(500, response)
11
- end
9
+ describe('#ctor') do
12
10
 
13
- it('returns a fullfilled error.message') do
14
- expect(subject.code).to eq(500)
15
- expect(subject.developer_message).to eq("dev info")
16
- expect(subject.user_message).to eq("user info")
17
- end
11
+ subject do
12
+ described_class.new(500, response)
13
+ end
14
+
15
+ it('returns a fullfilled error.message') do
16
+ expect(subject.code).to eq(500)
17
+ expect(subject.developer_message).to eq("dev info")
18
+ expect(subject.user_message).to eq("user info")
19
+ end
20
+
21
+ it('returns error with dev info as error.message') do
22
+ expect(subject.message).to eq("dev info")
23
+ end
24
+
25
+ it('returns error with stacktrace') do
26
+ expect(subject.backtrace).not_to be_nil
27
+ end
18
28
 
19
- it('returns error with dev info as error.message') do
20
- expect(subject.message).to eq("dev info")
21
29
  end
22
30
 
23
- it('returns error with stacktrace') do
24
- expect(subject.backtrace).not_to be_nil
31
+ describe('.[]') do
32
+
33
+ it('returns the error') do
34
+ result = described_class[500]
35
+ expect(result.code).to eq(500)
36
+ end
37
+
25
38
  end
26
39
 
27
40
  end
@@ -183,4 +183,18 @@ describe ZSS::Message do
183
183
 
184
184
  end
185
185
 
186
+ describe('.req?') do
187
+
188
+ it('returns true on request message') do
189
+ message.type = ZSS::Message::Type::REQ
190
+ expect(message.req?).to eq(true)
191
+ end
192
+
193
+ it('returns false on response message') do
194
+ message.type = ZSS::Message::Type::REP
195
+ expect(message.req?).to eq(false)
196
+ end
197
+
198
+ end
199
+
186
200
  end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+ require 'zss/router'
3
+
4
+ describe ZSS::Router do
5
+
6
+ class Dummy
7
+
8
+ def print(payload); end
9
+
10
+ def print_with_headers(payload, header); end
11
+
12
+ end
13
+
14
+ subject { described_class.new }
15
+
16
+ let(:context_object) { Dummy.new }
17
+
18
+ describe('#ctor') do
19
+
20
+ end
21
+
22
+ describe('#add_route') do
23
+
24
+ it('register route successfully') do
25
+ subject.add(context_object, "print", :print)
26
+ end
27
+
28
+ it('register route successfully without handler') do
29
+ subject.add(context_object, :print)
30
+ end
31
+
32
+
33
+ context('on error') do
34
+
35
+ it('raises a Error on invalid context') do
36
+ expect { subject.add(nil, :route) }.
37
+ to raise_exception
38
+ end
39
+
40
+ it('raises a Error on invalid route') do
41
+ expect { subject.add(context_object, nil) }.
42
+ to raise_exception
43
+ end
44
+
45
+ it('raises a Error on invalid handler') do
46
+ expect { subject.add(context_object, :route) }.
47
+ to raise_exception
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+
54
+ describe('#get_route') do
55
+
56
+ before(:each) do
57
+ subject.add(context_object, "print", :print)
58
+ subject.add(context_object, "print_with_headers", :print_with_headers)
59
+ end
60
+
61
+ context('on error') do
62
+
63
+ it('raises a ZSS::Error on invalid verb') do
64
+ expect { subject.get("route") }.
65
+ to raise_exception(ZSS::Error)
66
+ end
67
+
68
+ end
69
+
70
+ context('on success') do
71
+
72
+ it('returns route handler') do
73
+ expect(subject.get("print")).to be_truthy
74
+ end
75
+
76
+ end
77
+
78
+ context('call route handler') do
79
+
80
+ it('successfully call with payload') do
81
+ expect(context_object).to receive(:print).with("print")
82
+ subject.get("print").call("print")
83
+ end
84
+
85
+ it('successfully call with payload and headers') do
86
+ expect(context_object).to receive(:print_with_headers)
87
+ .with("print_with_headers", { header: "x" })
88
+
89
+ subject.get("print_with_headers")
90
+ .call("print_with_headers", { header: "x" })
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+
97
+ end
@@ -0,0 +1,314 @@
1
+ require 'spec_helper'
2
+ require 'zss'
3
+ require 'zss/service'
4
+ require 'em-zeromq'
5
+
6
+ describe ZSS::Service do
7
+
8
+ let(:socket_address) { "ipc://socket_spec" }
9
+
10
+ let :config do
11
+ Hashie::Mash.new(
12
+ backend: socket_address,
13
+ heartbeat: 60000
14
+ )
15
+ end
16
+
17
+ class DummyService
18
+ def ping payload, headers
19
+ headers[:took] = "0s"
20
+ return "PONG"
21
+ end
22
+
23
+ def ping_fail payload, headers
24
+ fail 'should raise exception'
25
+ end
26
+ end
27
+
28
+ def done
29
+ subject.stop
30
+ EM.stop
31
+ end
32
+
33
+ let(:context) { double('EM::ZeroMQ::Context').as_null_object }
34
+ let(:socket) { double('EM::ZMQ::Socket').as_null_object }
35
+
36
+ before :each do
37
+ allow(EM::ZeroMQ::Context).to receive(:new) { context }
38
+ allow(context).to receive(:socket) { socket }
39
+ allow(SecureRandom).to receive(:uuid) { "uuid" }
40
+ config.heartbeat = 60000
41
+ end
42
+
43
+ after :each do
44
+ EM.stop if EM.reactor_running?
45
+ end
46
+
47
+ subject do
48
+ described_class.new(:pong, config)
49
+ end
50
+
51
+ describe('#ctor') do
52
+
53
+ it('returns a service with default config') do
54
+ subject = described_class.new(:pong)
55
+ expect(subject.sid).to eq("PONG")
56
+ expect(subject.backend).to eq(ZSS::Configuration.default.backend)
57
+ expect(subject.heartbeat).to eq(1000)
58
+ end
59
+
60
+ it('returns a service with config') do
61
+ config = Hashie::Mash.new(
62
+ backend: "socket",
63
+ heartbeat: 2000
64
+ )
65
+ subject = described_class.new(:pong, config)
66
+ expect(subject.sid).to eq("PONG")
67
+ expect(subject.backend).to eq("socket")
68
+ expect(subject.heartbeat).to eq(2000)
69
+ end
70
+
71
+ it('raises an error on invalid sid') do
72
+
73
+ expect do
74
+ described_class.new(nil, nil)
75
+ end.to raise_exception(ZSS::Error) do |error|
76
+ expect(error.code).to eq(500)
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
83
+ describe('#run') do
84
+
85
+ context('on error') do
86
+
87
+ it('raises RuntimeError on invalid context') do
88
+ expect(EM::ZeroMQ::Context).to receive(:new) { nil }
89
+ expect { subject.run }.to raise_exception(RuntimeError)
90
+ end
91
+
92
+ it('raises RuntimeError on invalid socket') do
93
+ expect(context).to receive(:socket) { nil }
94
+ expect { subject.run }.to raise_exception(RuntimeError)
95
+ end
96
+
97
+ end
98
+
99
+ context('open ZMQ Socket') do
100
+
101
+ it('with dealer type') do
102
+ expect(context).to receive(:socket).with(ZMQ::DEALER) do
103
+ done
104
+ socket
105
+ end
106
+ subject.run
107
+ end
108
+
109
+ it('with identity set') do
110
+ expect(socket).to receive(:identity=).with("pong#uuid") { done }
111
+ subject.run
112
+ end
113
+
114
+ it('with linger set to 0') do
115
+ expect(socket).to receive(:setsockopt).with(ZMQ::LINGER, 0) { done }
116
+ subject.run
117
+ end
118
+
119
+ it('connect to socket address') do
120
+ expect(socket).to receive(:connect).with(socket_address) { done }
121
+ subject.run
122
+ end
123
+
124
+ end
125
+
126
+ it('register service on broker') do
127
+ expect(socket).to receive(:send_msg) do |*frames|
128
+ message = ZSS::Message.parse(frames)
129
+ expect(message.address.sid).to eq('SMI')
130
+ expect(message.address.verb).to eq('UP')
131
+ expect(message.payload).to eq('PONG')
132
+ done
133
+ true
134
+ end
135
+
136
+ subject.run
137
+ end
138
+
139
+ context('handling requests') do
140
+
141
+ let(:address) { ZSS::Message::Address.new(sid: "PONG", verb: "PING") }
142
+
143
+ let(:message) { ZSS::Message.new(address: address, payload: "PING") }
144
+
145
+ let :message_parts do
146
+ message.to_frames.map { |f| ZMQ::Message.new(f) }
147
+ end
148
+
149
+ it('returns payload and headers') do
150
+ service = DummyService.new
151
+ subject.add_route(service, :ping)
152
+
153
+ EM.run do
154
+ allow(socket).to receive(:on) do |event, &block|
155
+ EM.add_timer { block.call *message_parts }
156
+ end
157
+
158
+ subject.run
159
+
160
+ expect(socket).to receive(:send_msg) do |*frames|
161
+ message = ZSS::Message.parse(frames)
162
+
163
+ expect(message.type).to eq(ZSS::Message::Type::REP)
164
+ expect(message.status).to eq(200)
165
+ expect(message.headers).to eq({ "took" => "0s" })
166
+
167
+ done
168
+
169
+ true
170
+ end
171
+ end
172
+
173
+ end
174
+
175
+ context('on error') do
176
+
177
+ it('returns 404 on invalid sid') do
178
+ message.address.sid = "something"
179
+
180
+ EM.run do
181
+ allow(socket).to receive(:on) do |event, &block|
182
+ EM.add_timer { block.call *message_parts }
183
+ end
184
+
185
+ subject.run
186
+
187
+ expect(socket).to receive(:send_msg) do |*frames|
188
+ message = ZSS::Message.parse(frames)
189
+
190
+ expect(message.type).to eq(ZSS::Message::Type::REP)
191
+ expect(message.status).to eq(404)
192
+
193
+ done
194
+
195
+ true
196
+ end
197
+ end
198
+
199
+ end
200
+
201
+ it('returns 404 on invalid verb') do
202
+
203
+ message.address.verb = "something"
204
+
205
+ EM.run do
206
+ allow(socket).to receive(:on) do |event, &block|
207
+ EM.add_timer { block.call *message_parts }
208
+ end
209
+
210
+ subject.run
211
+
212
+ expect(socket).to receive(:send_msg) do |*frames|
213
+ message = ZSS::Message.parse(frames)
214
+
215
+ expect(message.type).to eq(ZSS::Message::Type::REP)
216
+ expect(message.status).to eq(404)
217
+
218
+ done
219
+
220
+ true
221
+ end
222
+ end
223
+
224
+ end
225
+
226
+ it('returns 500 when an error occurred while handling request') do
227
+
228
+ service = DummyService.new
229
+ subject.add_route(service, "PING/FAIL", :ping_fail)
230
+ message.address.verb = "PING/FAIL"
231
+
232
+ EM.run do
233
+ allow(socket).to receive(:on) do |event, &block|
234
+ EM.add_timer { block.call *message_parts }
235
+ end
236
+
237
+ subject.run
238
+
239
+ expect(socket).to receive(:send_msg) do |*frames|
240
+ message = ZSS::Message.parse(frames)
241
+
242
+ expect(message.type).to eq(ZSS::Message::Type::REP)
243
+ expect(message.status).to eq(500)
244
+
245
+ done
246
+
247
+ true
248
+ end
249
+ end
250
+
251
+ end
252
+
253
+ end
254
+
255
+ end
256
+
257
+ it('sends heartbeat message') do
258
+ config.heartbeat = 500
259
+ subject = described_class.new(:pong, config)
260
+
261
+ EM.run do
262
+
263
+ subject.run
264
+
265
+ expect(socket).to receive(:send_msg) do |*frames|
266
+ message = ZSS::Message.parse(frames)
267
+
268
+ expect(message.type).to eq(ZSS::Message::Type::REQ)
269
+ expect(message.address.sid).to eq('SMI')
270
+ expect(message.address.verb).to eq('HEARTBEAT')
271
+ expect(message.payload).to eq('PONG')
272
+
273
+ done
274
+
275
+ true
276
+ end
277
+ end
278
+ end
279
+
280
+ end
281
+
282
+ describe('#stop') do
283
+
284
+ it('disconnects the socket') do
285
+ EM.run do
286
+ subject.run
287
+
288
+ expect(socket).to receive(:disconnect) { done }
289
+
290
+ subject.stop
291
+ end
292
+ end
293
+
294
+ it('unregister service on broker') do
295
+ EM.run do
296
+
297
+ subject.run
298
+
299
+ expect(socket).to receive(:send_msg) do |*frames|
300
+ message = ZSS::Message.parse(frames)
301
+ expect(message.address.sid).to eq('SMI')
302
+ expect(message.address.verb).to eq('DOWN')
303
+ expect(message.payload).to eq('PONG')
304
+ done
305
+ 1
306
+ end
307
+
308
+ subject.stop
309
+ end
310
+ end
311
+
312
+ end
313
+
314
+ end