zss 0.0.2 → 0.0.3

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