alchemy-flux 0.0.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.
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe "performance of AlchemyFlux" do
4
+
5
+ after(:each) do
6
+ # This will stop EventMachine and disconnect RabbitMQ, reseting each test
7
+ AlchemyFlux::Service.stop
8
+ end
9
+
10
+ it 'should handle multiple messages at the same time' do
11
+ service_a = AlchemyFlux::Service.new("fluxa.service", {threadpool_size: 500}) do |message|
12
+ {'body' => "hola #{message['body']['name']}"}
13
+ end
14
+
15
+ service_b = AlchemyFlux::Service.new("fluxb.service")
16
+
17
+ service_a.start
18
+ service_b.start
19
+
20
+ calls = 400
21
+
22
+ responses = Queue.new
23
+ st = Time.now()
24
+ (1..calls).each do
25
+ service_b.send_message_to_service("fluxa.service", {'body' => {'name' => "Bob"}}) do |response|
26
+ responses << response
27
+ end
28
+ end
29
+
30
+ (1..calls).each do
31
+ resp = responses.pop
32
+ expect(resp['body']).to eq "hola Bob"
33
+ end
34
+ et = Time.now()
35
+ puts "Time for #{calls} async calls is #{(et-st)*1000}ms total; #{((et-st)*1000)/calls}ms per call"
36
+ service_a.stop
37
+ service_b.stop
38
+ end
39
+
40
+ it 'should handle multiple messages at the same time' do
41
+ service_a = AlchemyFlux::Service.new("fluxa.service", {threadpool_size: 500}) do |message|
42
+ {'body' => "hola #{message['body']['name']}"}
43
+ end
44
+
45
+ service_b = AlchemyFlux::Service.new("fluxb.service")
46
+
47
+ service_a.start
48
+ service_b.start
49
+
50
+ calls = 400
51
+
52
+ st = Time.now()
53
+ (1..calls).each do
54
+ resp = service_b.send_message_to_service("fluxa.service", {'body' => {'name' => "Bob"}})
55
+ expect(resp['body']).to eq "hola Bob"
56
+ end
57
+
58
+ et = Time.now()
59
+ puts "Time for #{calls} sync calls is #{(et-st)*1000}ms total; #{((et-st)*1000)/calls}ms per call"
60
+ service_a.stop
61
+ service_b.stop
62
+ end
63
+
64
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rack::Handler::AlchemyFlux do
4
+
5
+ after(:each) do
6
+ # This will stop EventMachine and disconnect RabbitMQ, reseting each test
7
+ AlchemyFlux::Service.stop
8
+ end
9
+
10
+ describe "#start" do
11
+
12
+ it 'should be able to start with a simple rack app' do
13
+ ENV['SERVICE_NAME'] = 'rack.service'
14
+ app = Proc.new do |env|
15
+ ['200', {}, ['hi Bob']]
16
+ end
17
+
18
+ service_a = AlchemyFlux::Service.new("fluxa.service")
19
+ Rack::Handler::AlchemyFlux.start app
20
+ service_a.start
21
+
22
+ response = service_a.send_message_to_service("rack.service", {})
23
+ expect(response['body']).to eq "hi Bob"
24
+ service_a.stop
25
+ Rack::Handler::AlchemyFlux.stop
26
+ end
27
+
28
+ it 'should register resources with RESOURCE_PATHS env variable' do
29
+ ENV['SERVICE_NAME'] = 'rack.service'
30
+ ENV['RESOURCE_PATHS'] = '/alice,/bob'
31
+ app = Proc.new do |env|
32
+ ['200', {}, ["hi #{env['PATH_INFO']}"]]
33
+ end
34
+
35
+ service_a = AlchemyFlux::Service.new("fluxa.service", :timeout => 100)
36
+ Rack::Handler::AlchemyFlux.start app
37
+
38
+ service_a.start
39
+
40
+ response = service_a.send_message_to_resource({'path' => '/alice'})
41
+ expect(response['body']).to eq "hi /alice"
42
+
43
+ response = service_a.send_message_to_resource({'path' => '/bob'})
44
+ expect(response['body']).to eq "hi /bob"
45
+
46
+ service_a.stop
47
+ Rack::Handler::AlchemyFlux.stop
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe AlchemyFlux::Service do
4
+
5
+ after(:each) do
6
+ # This will stop EventMachine and disconnect RabbitMQ, reseting each test
7
+ AlchemyFlux::Service.stop
8
+ end
9
+
10
+ describe "#send_message_to_resource" do
11
+
12
+ it 'should be able to send messages to resource via path' do
13
+ resource_path = "/v1/fluxy_#{AlchemyFlux::Service.generateUUID()}"
14
+ service_a = AlchemyFlux::Service.new("fluxa.service", resource_paths: [resource_path]) do |message|
15
+ {'body' => "hi #{message['body']['name']}"}
16
+ end
17
+
18
+ service_b = AlchemyFlux::Service.new("fluxb.service")
19
+
20
+ service_a.start
21
+ service_b.start
22
+
23
+ response = service_b.send_message_to_resource({'path' => resource_path, 'body' => {'name' => "Bob"}})
24
+ expect(response['body']).to eq "hi Bob"
25
+
26
+ response = service_b.send_message_to_resource({'path' => "#{resource_path}/id", 'body' => {'name' => "Alice"}})
27
+ expect(response['body']).to eq "hi Alice"
28
+ service_a.stop
29
+ service_b.stop
30
+ end
31
+
32
+ it 'should be able to register multiple resources' do
33
+ resource_path1 = "/v1/fluxy_#{AlchemyFlux::Service.generateUUID()}"
34
+ resource_path2 = "/v1/fluxy_#{AlchemyFlux::Service.generateUUID()}"
35
+ service_a = AlchemyFlux::Service.new("fluxa.service", resource_paths: [resource_path1, resource_path2]) do |message|
36
+ {'body' => "hi #{message['body']['name']}"}
37
+ end
38
+
39
+ service_b = AlchemyFlux::Service.new("fluxb.service")
40
+
41
+ service_a.start
42
+ service_b.start
43
+
44
+ response = service_b.send_message_to_resource({'path' => resource_path1, 'body' => {'name' => "Bob"}})
45
+ expect(response['body']).to eq "hi Bob"
46
+
47
+ response = service_b.send_message_to_resource({'path' => resource_path2, 'body' => {'name' => "Alice"}})
48
+ expect(response['body']).to eq "hi Alice"
49
+ service_a.stop
50
+ service_b.stop
51
+ end
52
+
53
+
54
+ describe 'unhappy path' do
55
+ it 'should return error on a message to non existant service' do
56
+ service_b = AlchemyFlux::Service.new("fluxb.service")
57
+
58
+ service_b.start
59
+
60
+ expect(service_b.send_message_to_resource({'path' => '/v1/unregistered_resource'})).to eq AlchemyFlux::MessageNotDeliveredError
61
+ expect(service_b.transactions.length).to eq 0
62
+
63
+ service_b.stop
64
+ end
65
+
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,448 @@
1
+ require 'spec_helper'
2
+
3
+ describe AlchemyFlux::Service do
4
+ def thread_count
5
+ Thread.list.count
6
+ end
7
+
8
+ after(:each) do
9
+ # This will stop EventMachine and disconnect RabbitMQ, reseting each test
10
+ AlchemyFlux::Service.stop
11
+ end
12
+
13
+ describe '#initialize' do
14
+ it 'be initializable' do
15
+ AlchemyFlux::Service.new("test.service")
16
+ end
17
+ end
18
+
19
+ describe '#Service.start' do
20
+ it 'should create the EM thread' do
21
+ init_thread_count = thread_count
22
+ AlchemyFlux::Service.start
23
+ expect(thread_count).to eq init_thread_count + 1
24
+ AlchemyFlux::Service.stop
25
+ expect(thread_count).to eq init_thread_count
26
+ end
27
+
28
+ it 'should raise an error if amqp uri is broken' do
29
+ expect{AlchemyFlux::Service.start('bad_uri')}.to raise_error(ArgumentError)
30
+ expect{AlchemyFlux::Service.start('amqp://localhosty')}.to raise_error(EventMachine::ConnectionError)
31
+ end
32
+
33
+ it 'should start the Service connection on instance start' do
34
+ init_thread_count = thread_count
35
+ service = AlchemyFlux::Service.new("testflux.service")
36
+ service.start
37
+ expect(thread_count).to eq init_thread_count + 1
38
+ AlchemyFlux::Service.stop
39
+ expect(thread_count).to eq init_thread_count
40
+ end
41
+
42
+ it 'should raise an error if amqp uri is broken on instance start' do
43
+ service_bad_uri = AlchemyFlux::Service.new("testflux.service", ampq_uri: 'bad_uri')
44
+ service_bad_server = AlchemyFlux::Service.new("testflux.service", ampq_uri: 'amqp://localhosty')
45
+ expect{service_bad_uri.start}.to raise_error(ArgumentError)
46
+ expect{service_bad_server.start}.to raise_error(EventMachine::ConnectionError)
47
+ end
48
+ end
49
+
50
+ describe '#start' do
51
+
52
+ it 'should start a service and increase thread count by 1' do
53
+ init_thread_count = thread_count
54
+ service = AlchemyFlux::Service.new("testflux.service")
55
+ expect(service.state).to eq :stopped
56
+ service.start
57
+ expect(thread_count).to eq init_thread_count + 1
58
+ expect(service.state).to eq :started
59
+ #stop should not decrease because EM might still be running
60
+ service.stop
61
+ expect(service.state).to eq :stopped
62
+ expect(thread_count).to eq init_thread_count + 1
63
+ end
64
+
65
+ it 'start and stop multiple services with no extra threads' do
66
+ init_thread_count = thread_count
67
+
68
+ service_a = AlchemyFlux::Service.new("testfluxa.service")
69
+ service_b = AlchemyFlux::Service.new("testfluxb.service")
70
+
71
+ service_a.start
72
+ service_b.start
73
+
74
+ expect(thread_count).to eq init_thread_count + 1
75
+
76
+ service_a.stop
77
+ service_b.stop
78
+
79
+ expect(thread_count).to eq init_thread_count + 1
80
+ end
81
+
82
+ it 'should stop receiving messages after it has stopped' do
83
+ received_message = false
84
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
85
+ received_message = true
86
+ {}
87
+ end
88
+
89
+ service_b = AlchemyFlux::Service.new("fluxb.service", timeout: 100)
90
+
91
+ service_a.start
92
+ service_b.start
93
+
94
+ service_a.stop
95
+ expect(service_b.send_message_to_service("fluxa.service", {})).to eq AlchemyFlux::TimeoutError
96
+ service_b.stop
97
+ end
98
+
99
+ it 'should process incoming messages then stop' do
100
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
101
+ sleep(0.05)
102
+ {'body' => "hello"}
103
+ end
104
+
105
+ service_b = AlchemyFlux::Service.new("fluxb.service", timeout: 200)
106
+
107
+ service_a.start
108
+ service_b.start
109
+
110
+ response_queue = Queue.new
111
+ service_b.send_message_to_service("fluxa.service", {}) do |response|
112
+ response_queue << response
113
+ end
114
+ sleep(0.05)
115
+ service_a.stop
116
+
117
+ response = response_queue.pop
118
+
119
+ expect(response['body']).to eq "hello"
120
+ service_b.stop
121
+ end
122
+
123
+ it 'should process outgoing messages then stop' do
124
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
125
+ sleep(0.05)
126
+ {'body' => "hello"}
127
+ end
128
+
129
+ service_b = AlchemyFlux::Service.new("fluxb.service", timeout: 200)
130
+
131
+ service_a.start
132
+ service_b.start
133
+
134
+ response_queue = Queue.new
135
+ service_b.send_message_to_service("fluxa.service", {}) do |response|
136
+ response_queue << response
137
+ end
138
+ sleep(0.01)
139
+ service_b.stop
140
+
141
+ response = response_queue.pop
142
+
143
+ expect(response['body']).to eq "hello"
144
+ service_b.stop
145
+ end
146
+
147
+ it 'should stop receiving messages while stopping' do
148
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
149
+ sleep(0.8);
150
+ {'body' => "hello"}
151
+ end
152
+
153
+ service_b = AlchemyFlux::Service.new("fluxb.service")
154
+
155
+ service_a.start
156
+ service_b.start
157
+
158
+ response_queue = Queue.new
159
+ service_b.send_message_to_service("fluxa.service", {}) do |response|
160
+ response_queue << response
161
+ end
162
+ sleep(0.1)
163
+ Thread.new do service_a.stop end
164
+ sleep(0.3)
165
+ expect(service_b.send_message_to_service("fluxa.service", {})).to eq AlchemyFlux::TimeoutError
166
+
167
+ response = response_queue.pop
168
+
169
+ expect(response['body']).to eq "hello"
170
+ service_b.stop
171
+ end
172
+
173
+ it 'should have default service function' do
174
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
175
+ {'body' => "hi #{message['body']['name']}"}
176
+ end
177
+
178
+ service_b = AlchemyFlux::Service.new("fluxb.service")
179
+
180
+ service_a.start
181
+ service_b.start
182
+
183
+ response = service_b.send_message_to_service("fluxa.service", {'body' => {'name' => "Bob"}})
184
+ expect(response['body']).to eq "hi Bob"
185
+
186
+ response = service_a.send_message_to_service("fluxb.service", {'body' => {'name' => "Bob"}})
187
+ expect(response['body']).to be_empty
188
+
189
+
190
+ service_a.stop
191
+ service_b.stop
192
+ end
193
+ end
194
+
195
+
196
+ describe "#send_message_to_service" do
197
+
198
+ it 'should send and receive messages between services' do
199
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
200
+ {'body' => "hi #{message['body']['name']}"}
201
+ end
202
+
203
+ service_b = AlchemyFlux::Service.new("fluxb.service")
204
+
205
+ service_a.start
206
+ service_b.start
207
+
208
+ response = service_b.send_message_to_service("fluxa.service", {'body' => {'name' => "Bob"}})
209
+ expect(response['body']).to eq "hi Bob"
210
+ service_a.stop
211
+ service_b.stop
212
+ end
213
+
214
+
215
+ it 'can send and receive messages JSON messages' do
216
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
217
+ body = JSON.parse(message['body'])
218
+ {'body' => "hi #{body['name']}"}
219
+ end
220
+
221
+ service_b = AlchemyFlux::Service.new("fluxb.service")
222
+
223
+ service_a.start
224
+ service_b.start
225
+
226
+ response = service_b.send_message_to_service("fluxa.service", {'body' => '{"name" : "Bob"}'})
227
+ expect(response['body']).to eq "hi Bob"
228
+ service_a.stop
229
+ service_b.stop
230
+ end
231
+
232
+ it 'should be able to send messages within the service call' do
233
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
234
+ resp = service_a.send_message_to_service("fluxb.service", {})
235
+ {'body' => "hi #{resp['body']}"}
236
+ end
237
+
238
+ service_b = AlchemyFlux::Service.new("fluxb.service") do |message|
239
+ {'body' => 'Bob'}
240
+ end
241
+
242
+ service_a.start
243
+ service_b.start
244
+
245
+ response = service_b.send_message_to_service("fluxa.service", {})
246
+ expect(response['body']).to eq "hi Bob"
247
+ service_a.stop
248
+ service_b.stop
249
+ end
250
+
251
+ it 'should be able to send messages to itself' do
252
+ first = true
253
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
254
+ if first
255
+ first = !first
256
+ resp = service_a.send_message_to_service("fluxa.service", {})
257
+ {'body' => "hi #{resp['body']}"}
258
+ else
259
+ {'body' => 'Bob'}
260
+ end
261
+ end
262
+
263
+ service_b = AlchemyFlux::Service.new("fluxb.service")
264
+
265
+ service_a.start
266
+ service_b.start
267
+
268
+ response = service_b.send_message_to_service("fluxa.service", {})
269
+ expect(response['body']).to eq "hi Bob"
270
+ service_a.stop
271
+ service_b.stop
272
+ end
273
+
274
+ it 'should generate transaction id if none is given' do
275
+ interaction_id = nil
276
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
277
+ expect(message['headers']).to have_key 'x-interaction-id'
278
+ expect(message['headers']['x-interaction-id']).not_to be_nil
279
+ interaction_id = message['headers']['x-interaction-id']
280
+ { 'body' => "here" }
281
+ end
282
+
283
+ service_b = AlchemyFlux::Service.new("fluxb.service")
284
+
285
+ service_a.start
286
+ service_b.start
287
+
288
+ response = service_b.send_message_to_service("fluxa.service", {})
289
+ expect(response['body']).to eq "here"
290
+ expect(response['headers']).to have_key 'x-interaction-id'
291
+ expect(response['headers']['x-interaction-id']).not_to be_nil
292
+ expect(response['headers']['x-interaction-id']).to eq interaction_id
293
+ service_a.stop
294
+ service_b.stop
295
+ end
296
+
297
+ it 'should use given transaction id if one is provided' do
298
+ interaction_id = 'a123'
299
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
300
+ expect(message['headers']['x-interaction-id']).to eq interaction_id
301
+ { 'body' => "here" }
302
+ end
303
+
304
+ service_b = AlchemyFlux::Service.new("fluxb.service")
305
+
306
+ service_a.start
307
+ service_b.start
308
+
309
+ response = service_b.send_message_to_service("fluxa.service", {
310
+ 'headers' => {'x-interaction-id' => interaction_id}
311
+ })
312
+ expect(response['body']).to eq "here"
313
+ expect(response['headers']['x-interaction-id']).to eq interaction_id
314
+ service_a.stop
315
+ service_b.stop
316
+ end
317
+
318
+ it 'should add to transactions and processing messages, and remove once complete' do
319
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
320
+ sleep(0.1)
321
+ {'body' => 'here'}
322
+ end
323
+
324
+ service_b = AlchemyFlux::Service.new("fluxb.service", timeout: 200)
325
+
326
+ service_a.start
327
+ service_b.start
328
+
329
+ expect(service_b.transactions.length).to eq 0
330
+ expect(service_a.processing_messages).to eq 0
331
+
332
+ response = Queue.new
333
+ service_b.send_message_to_service("fluxa.service", {}) do |resp|
334
+ response << resp
335
+ end
336
+ sleep(0.05)
337
+ expect(service_b.transactions.length).to eq 1
338
+ expect(service_a.processing_messages).to eq 1
339
+
340
+ expect(response.pop['body']).to eq 'here'
341
+ expect(service_b.transactions.length).to eq 0
342
+ expect(service_a.processing_messages).to eq 0
343
+
344
+ service_a.stop
345
+ service_b.stop
346
+ end
347
+
348
+
349
+ it 'should send and receive messages between services asynchronously' do
350
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
351
+ {'body' => "hola #{message['body']['name']}"}
352
+ end
353
+
354
+ service_b = AlchemyFlux::Service.new("fluxb.service")
355
+
356
+ service_a.start
357
+ service_b.start
358
+
359
+ block = Queue.new
360
+ service_b.send_message_to_service("fluxa.service", {'body' => {'name' => "Bob"}}) do |response|
361
+ block << response
362
+ end
363
+
364
+ response = block.pop
365
+ expect(response['body']).to eq "hola Bob"
366
+
367
+ service_a.stop
368
+ service_b.stop
369
+ end
370
+
371
+ describe 'unhappy path' do
372
+ it 'should be able to be nacked by the service_fn' do
373
+ called = 0
374
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
375
+ called += 1
376
+ raise AlchemyFlux::NAckError if called == 1
377
+ { 'body' => "hola #{message['body']['name']}"}
378
+ end
379
+
380
+ service_b = AlchemyFlux::Service.new("fluxb.service")
381
+
382
+ service_a.start
383
+ service_b.start
384
+
385
+ block = Queue.new
386
+ service_b.send_message_to_service("fluxa.service", {'body' => {'name' => "Bob"}}) do |response|
387
+ block << response
388
+ end
389
+ response = block.pop
390
+ expect(response['body']).to eq "hola Bob"
391
+ expect(called).to eq 2
392
+ service_a.stop
393
+ service_b.stop
394
+ end
395
+
396
+ it 'should timeout if a message takes too long' do
397
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
398
+ sleep(0.1)
399
+ {}
400
+ end
401
+
402
+ service_b = AlchemyFlux::Service.new("fluxb.service", timeout: 100)
403
+
404
+ service_a.start
405
+ service_b.start
406
+
407
+ response = service_b.send_message_to_service("fluxa.service", {'body' => {'name' => "Bob"}})
408
+
409
+ expect(response).to eq AlchemyFlux::TimeoutError
410
+ expect(service_b.transactions.length).to eq 0
411
+
412
+ service_a.stop
413
+ service_b.stop
414
+ end
415
+
416
+ it 'should 500 if service_fn raises an error' do
417
+ service_a = AlchemyFlux::Service.new("fluxa.service") do |message|
418
+ raise Error.new
419
+ end
420
+
421
+ service_b = AlchemyFlux::Service.new("fluxb.service")
422
+
423
+ service_a.start
424
+ service_b.start
425
+
426
+ response = service_b.send_message_to_service("fluxa.service", {'body' => {'name' => "Bob"}})
427
+
428
+ expect(response['status_code']).to eq 500
429
+ expect(service_b.transactions.length).to eq 0
430
+
431
+ service_a.stop
432
+ service_b.stop
433
+ end
434
+
435
+ it 'should return error on a message to non existant service' do
436
+ service_b = AlchemyFlux::Service.new("fluxb.service")
437
+
438
+ service_b.start
439
+
440
+ expect(service_b.send_message_to_service("not_a_servoces.service", {})).to eq AlchemyFlux::MessageNotDeliveredError
441
+ expect(service_b.transactions.length).to eq 0
442
+
443
+ service_b.stop
444
+ end
445
+
446
+ end
447
+ end
448
+ end