nebulous_stomp 1.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,575 @@
1
+ require 'stomp'
2
+
3
+ require 'spec_helper'
4
+ require 'nebulous/message'
5
+
6
+ require_relative 'helpers'
7
+
8
+ require 'pry'
9
+
10
+ include Nebulous
11
+
12
+
13
+ RSpec.configure do |c|
14
+ c.include Helpers
15
+ end
16
+
17
+
18
+
19
+ describe Message do
20
+
21
+ # the cacheing process can't preserve the symbol-or-text-ness of the
22
+ # headers; we're stuck with that. So for comparison purposes this helper
23
+ # function deep-converts all keys in a hash to symbols.
24
+ def symbolise(hash)
25
+
26
+ hash.each_with_object({}) do |(k,v),m|
27
+ m[k.to_sym] = v.kind_of?(Hash) ? symbolise(v) : v
28
+ end
29
+
30
+ end
31
+
32
+
33
+ let(:msg_pts) do
34
+ x = Message.from_parts('Daphne', 'Fred', 'Velma', 'Shaggy', 'Scooby')
35
+ x.reply_id = 42
36
+ x
37
+ end
38
+
39
+ let(:smess) { stomp_message('application/text', 'foo') }
40
+ let(:msg_stomp) { Message.from_stomp(smess) }
41
+
42
+ let(:json_hash) do
43
+
44
+ b = { verb: 'tom',
45
+ params: 'dick',
46
+ desc: 'harry' }.to_json
47
+
48
+ x = Message.from_stomp( stomp_message('application/json', b) )
49
+
50
+ { stompHeaders: x.stomp_headers,
51
+ stompBody: x.stomp_body,
52
+ verb: 'tom',
53
+ params: 'dick',
54
+ desc: 'harry',
55
+ replyTo: '/queue/thing',
56
+ replyId: '1234',
57
+ inReplyTo: '4321',
58
+ contentType: 'application/json' }
59
+
60
+ end
61
+
62
+ let(:msg_cache) { Message.from_cache( json_hash.to_json ) }
63
+
64
+
65
+ describe 'Message.from_parts' do
66
+
67
+ it 'returns a Message object' do
68
+ expect( msg_pts ).to be_a_kind_of(Message)
69
+ end
70
+
71
+ it 'sets protocol attributes if it can, raises hell otherwise' do
72
+ expect{ Message.from_parts }.to raise_exception ArgumentError
73
+
74
+ expect{ Message.from_parts(nil, nil, nil, nil, nil) }.
75
+ to raise_exception ArgumentError
76
+
77
+ expect( msg_pts.reply_to ).to eq 'Daphne'
78
+ expect( msg_pts.in_reply_to ).to eq 'Fred'
79
+ expect( msg_pts.verb ).to eq 'Velma'
80
+ expect( msg_pts.params ).to eq 'Shaggy'
81
+ expect( msg_pts.desc ).to eq 'Scooby'
82
+ end
83
+
84
+ it "assumes a content type of JSON" do
85
+ expect( msg_pts.content_type ).to match(/json$/i)
86
+ end
87
+
88
+ end
89
+ ##
90
+
91
+
92
+ describe 'Message.in_reply_to' do
93
+
94
+ let(:msg) do
95
+ Message.in_reply_to(msg_pts, 'Buffy', 'Willow', 'Xander', 'Ripper')
96
+ end
97
+
98
+
99
+ it 'requires another Message object and a verb' do
100
+ expect{ Message.in_reply_to('foo') }.to raise_exception ArgumentError
101
+
102
+ expect{ Message.in_reply_to('foo', 'bar') }.
103
+ to raise_exception ArgumentError
104
+
105
+ expect{ Message.in_reply_to(msg_pts, 'bar') }.not_to raise_exception
106
+ end
107
+
108
+ it 'returns a fresh Message object' do
109
+ expect( msg ).to be_a_kind_of(Message)
110
+ expect( msg ).not_to eq(msg_pts)
111
+ end
112
+
113
+ it 'sets Protocol attributes' do
114
+ expect( msg.verb ).to eq 'Buffy'
115
+ expect( msg.params ).to eq 'Willow'
116
+ expect( msg.desc ).to eq 'Xander'
117
+ expect( msg.reply_to ).to eq 'Ripper'
118
+
119
+ # NB the reply_id (message ID) not the reply_to (the queue)
120
+ expect( msg.in_reply_to ).to eq 42
121
+ end
122
+
123
+ it 'sets the content type from the source message' do
124
+ expect( msg.content_type ).to eq msg_pts.content_type
125
+ end
126
+
127
+ end
128
+ ##
129
+
130
+
131
+ describe 'Message.from_stomp' do
132
+
133
+ it 'requires a Stomp::Message' do
134
+ expect{ Message.from_stomp }.to raise_exception ArgumentError
135
+ expect{ Message.from_stomp('foo') }.to raise_exception ArgumentError
136
+ expect{ Message.from_stomp(smess) }.not_to raise_exception
137
+ end
138
+
139
+ it 'sets stomp attributes' do
140
+ expect( msg_stomp.stomp_headers ).to include smess.headers
141
+ expect( msg_stomp.stomp_body ).to eq smess.body
142
+ end
143
+
144
+ it 'still works if there are no Protocol attributes to set' do
145
+ expect( msg_stomp.verb ).to eq nil
146
+ expect( msg_stomp.params ).to eq nil
147
+ expect( msg_stomp.desc ).to eq nil
148
+ expect( msg_stomp.reply_to ).to eq nil
149
+ expect( msg_stomp.in_reply_to ).to eq nil
150
+ end
151
+
152
+
153
+ context "when the message body is text" do
154
+
155
+ it 'returns a Message object' do
156
+ expect( msg_stomp ).to be_a_kind_of Message
157
+ end
158
+
159
+ it 'sets Protocol attributes if it can' do
160
+ body = {verb: 'Dougal', params: 'Florence', desc: 'Ermintrude'}
161
+ mess = stomp_message('application/json', body.to_json, '23')
162
+ msg = Message.from_stomp(mess)
163
+ expect( msg.verb ).to eq 'Dougal'
164
+ expect( msg.params ).to eq 'Florence'
165
+ expect( msg.desc ).to eq 'Ermintrude'
166
+ expect( msg.in_reply_to ).to eq '23'
167
+ end
168
+
169
+ end
170
+
171
+
172
+ context "when the message body is JSON" do
173
+
174
+ let(:msg_stomp_json) do
175
+ m = {verb: 'one', params: 'two', desc: 'three'}.to_json
176
+ x = stomp_message('application/json', m, '19')
177
+ Message.from_stomp(x)
178
+ end
179
+
180
+ it 'returns a Message object' do
181
+ expect( msg_stomp_json ).to be_a_kind_of Message
182
+ end
183
+
184
+ it 'sets Protocol attributes if it can' do
185
+ expect( msg_stomp_json.verb ).to eq 'one'
186
+ expect( msg_stomp_json.params ).to eq 'two'
187
+ expect( msg_stomp_json.desc ).to eq 'three'
188
+ expect( msg_stomp_json.in_reply_to ).to eq '19'
189
+ end
190
+
191
+ end
192
+
193
+ end
194
+ ##
195
+
196
+
197
+ describe 'Message.from_cache' do
198
+
199
+ let(:msg2) do
200
+ x = { replyId: '1234',
201
+ contentType: 'application/json' }.to_json
202
+
203
+ Message.from_cache(x)
204
+ end
205
+
206
+
207
+ it 'requires some json in the right format' do
208
+ expect{ Message.from_cache }.to raise_exception ArgumentError
209
+ expect{ Message.from_cache('foo') }.to raise_exception ArgumentError
210
+ expect{ Message.from_cache({}) }.to raise_exception ArgumentError
211
+ expect{ Message.from_cache({foo:'bar'}) }.to raise_exception ArgumentError
212
+
213
+ expect{ Message.from_cache(json_hash.to_json) }.not_to raise_exception
214
+ end
215
+
216
+ it "copes with some loon passing header and not body or vice versa" do
217
+ # I know because ... I was that soldier.
218
+
219
+ loony1 = { stompHeader: 'foo' }
220
+ loony2 = { stompBody: 'bar' }
221
+
222
+ expect{ Message.from_cache(loony1.to_json) }.not_to raise_exception
223
+ expect{ Message.from_cache(loony2.to_json) }.not_to raise_exception
224
+ end
225
+
226
+ it 'still works if there are no Protocol attributes to set' do
227
+ expect( msg2.verb ).to eq nil
228
+ expect( msg2.params ).to eq nil
229
+ expect( msg2.desc ).to eq nil
230
+ expect( msg2.reply_to ).to eq nil
231
+ expect( msg2.in_reply_to ).to eq nil
232
+ end
233
+
234
+ it 'sets Protocol attributes if it can' do
235
+ expect( msg_cache.verb ).to eq 'tom'
236
+ expect( msg_cache.params ).to eq 'dick'
237
+ expect( msg_cache.desc ).to eq 'harry'
238
+ expect( msg_cache.reply_to ).to eq '/queue/thing'
239
+ expect( msg_cache.in_reply_to ).to eq '4321'
240
+ end
241
+
242
+
243
+ context "when the message body is JSON" do
244
+ # msg_cache has a json body
245
+
246
+ it 'returns a Message object' do
247
+ expect( msg_cache ).to be_a_kind_of Message
248
+ end
249
+
250
+ it 'sets the stomp attributes' do
251
+ expect( msg_cache.stomp_headers ).
252
+ to eq symbolise( json_hash[:stompHeaders] )
253
+
254
+ expect( msg_cache.stomp_body ).to eq json_hash[:stompBody]
255
+ end
256
+
257
+ it 'sets the content type' do
258
+ expect( msg_cache.content_type ).to eq json_hash[:contentType]
259
+ end
260
+
261
+ end
262
+
263
+
264
+ context "when the message body is text" do
265
+
266
+ let(:msg3_cache) do
267
+ { stompHeaders: msg_stomp.stomp_headers,
268
+ stompBody: msg_stomp.stomp_body,
269
+ verb: 'alice',
270
+ params: 'karen',
271
+ desc: 'jessica',
272
+ replyTo: '/queue/thing',
273
+ replyId: '9876',
274
+ inReplyTo: '6789',
275
+ contentType: 'application/text' }
276
+
277
+ end
278
+
279
+ let(:msg3) { Message.from_cache( msg3_cache.to_json ) }
280
+
281
+
282
+ it 'returns a Message object' do
283
+ expect( msg3 ).to be_a_kind_of Message
284
+ end
285
+
286
+ it 'sets the stomp attributes' do
287
+ heads = msg3_cache[:stompHeaders].each_with_object({}) do |(k,v),m|
288
+ m[k.to_sym] = v
289
+ end
290
+
291
+ expect( msg3.stomp_headers ).to eq heads
292
+ expect( msg3.stomp_body ).to eq msg3_cache[:stompBody]
293
+ end
294
+
295
+ it 'sets the content type' do
296
+ expect( msg3.content_type ).to eq msg3_cache[:contentType]
297
+ end
298
+
299
+ end
300
+
301
+
302
+ end
303
+ ##
304
+
305
+
306
+ describe '#parameters' do
307
+ it 'returns the same as @param' do
308
+ expect(msg_pts.parameters).to eq msg_pts.params
309
+ end
310
+ end
311
+ ##
312
+
313
+
314
+ describe '#description' do
315
+ it 'returns the same as @desc' do
316
+ expect(msg_pts.description).to eq msg_pts.desc
317
+ end
318
+ end
319
+ ##
320
+
321
+
322
+ describe '#content_is_json?' do
323
+
324
+ it 'returns true if the body is supposed to be JSON' do
325
+ expect( msg_pts.content_is_json? ).to be true
326
+ end
327
+
328
+ it 'returns false unless the body is supposed to be JSON' do
329
+ smess = stomp_message('application/text', 'foo')
330
+ mess = Message.from_stomp(smess)
331
+ expect( mess.content_is_json? ).to be false
332
+
333
+ mess = Message.from_cache( {contentType: 'dunno'}.to_json )
334
+ expect( mess.content_is_json? ).to be false
335
+
336
+ mess = Message.from_cache( {horse: 'badger'}.to_json )
337
+ expect( mess.content_is_json? ).to be false
338
+ end
339
+
340
+ end
341
+ ##
342
+
343
+
344
+ describe '#to_cache' do
345
+
346
+ it 'returns the message as a hash' do
347
+ hash = msg_pts.to_cache
348
+
349
+ expect( hash ).to be_a_kind_of Hash
350
+ expect( hash ).to include( replyTo: 'Daphne',
351
+ inReplyTo: 'Fred',
352
+ verb: 'Velma',
353
+ params: 'Shaggy',
354
+ desc: 'Scooby' )
355
+
356
+ expect( hash[:contentType] ).to match /json$/i
357
+ end
358
+
359
+ it 'always returns all the keys' do
360
+ expect( msg_stomp.to_cache.keys ).to include(*json_hash.keys)
361
+ end
362
+
363
+ it "returns a hash that Message.from_cache doesn''t freak out over" do
364
+ expect{ Message.from_cache(msg_cache.to_cache.to_json) }.
365
+ not_to raise_exception
366
+
367
+ mess = Message.from_cache(msg_cache.to_cache.to_json)
368
+ expect(mess.to_cache).to eq symbolise(json_hash)
369
+ end
370
+
371
+
372
+ end
373
+ ##
374
+
375
+
376
+ describe '#protocol_json' do
377
+ it "returns the Protocol as a JSON string" do
378
+ hash = JSON.parse( msg_pts.protocol_json, symbolize_names: true )
379
+
380
+ expect( hash ).to include(verb: 'Velma')
381
+
382
+ expect( hash ).to include(params: 'Shaggy').
383
+ or include(parameters: 'Shaggy')
384
+
385
+ expect( hash ).to include(desc: 'Scooby').
386
+ or include(description: 'Scooby')
387
+
388
+ end
389
+ end
390
+ ##
391
+
392
+
393
+ describe "#body_to_h" do
394
+
395
+ context "if the body is in JSON" do
396
+
397
+ it "returns a hash" do
398
+ x = {}
399
+ x[:stompHeaders] = {}
400
+ x[:stompBody] = @datH.to_json # JSONd twice?
401
+ x[:contentType] = "JSON"
402
+
403
+ nr = Message.from_cache(x.to_json)
404
+ expect( nr.body_to_h ).to eq @datH
405
+ end
406
+
407
+ end
408
+
409
+ context "If the body is not in JSON" do
410
+ it "returns nil" do
411
+
412
+ x = {}
413
+ x["body"] = @datS
414
+ x["content-type"] = "text"
415
+
416
+ nr = Message.from_cache(x.to_json)
417
+ expect( nr.body_to_h ).to be_nil
418
+
419
+ end
420
+ end
421
+
422
+ context "If the body is nil(!)" do
423
+ it "returns nil" do
424
+ x = {}
425
+ x["body"] = nil
426
+ x["content-type"] = "JSON"
427
+
428
+ nr = Message.from_cache(x.to_json)
429
+
430
+ expect{ nr.body_to_h }.to_not raise_exception
431
+ expect( nr.body_to_h ).to be_nil
432
+ end
433
+ end
434
+
435
+ end
436
+ ##
437
+
438
+
439
+ describe '#headers_for_stomp' do
440
+
441
+ it 'always returns a Hash' do
442
+ expect( msg_pts.headers_for_stomp ).to be_a_kind_of Hash
443
+ expect( msg_stomp.headers_for_stomp ).to be_a_kind_of Hash
444
+ expect( msg_cache.headers_for_stomp ).to be_a_kind_of Hash
445
+ end
446
+
447
+ it "returns the custom headers for the Stomp gem" do
448
+ hdrs = msg_pts.headers_for_stomp
449
+ expect( hdrs ).to include("content-type" => 'application/json')
450
+ expect( hdrs ).to include("neb-reply-id" => 42)
451
+ expect( hdrs ).to include("neb-reply-to" => 'Daphne')
452
+ expect( hdrs ).to include("neb-in-reply-to" => 'Fred')
453
+
454
+ hdrs = msg_stomp.headers_for_stomp
455
+ expect( hdrs ).to include("content-type" => 'application/text')
456
+ expect( hdrs ).to include("neb-reply-id" => nil)
457
+ end
458
+
459
+ end
460
+ ##
461
+
462
+
463
+ describe '#body_for_stomp' do
464
+
465
+ it "returns a JSON string for content type JSON" do
466
+ expect{ JSON.parse(msg_cache.body_for_stomp) }.not_to raise_exception
467
+
468
+ hash = JSON.parse(msg_cache.body_for_stomp)
469
+
470
+ expect( hash ).to include('verb' => 'tom')
471
+
472
+ expect( hash ).to include('params' => 'dick').
473
+ or include('parameters' => 'dick')
474
+
475
+ expect( hash ).to include('desc' => 'harry').
476
+ or include('description' => 'harry')
477
+
478
+ end
479
+
480
+ it "returns a header-style string for non-JSON" do
481
+ hash1 = { verb: 'tom',
482
+ params: 'dick',
483
+ desc: 'harry',
484
+ contentType: 'supposedly/boris' }
485
+
486
+ msg = Message.from_cache( hash1.to_json )
487
+
488
+ expect( msg.body_for_stomp ).to be_a_kind_of String
489
+
490
+ hash2 = msg.body_for_stomp.
491
+ split("\n").
492
+ each_with_object({}) {|e,m| k,v = e.split(/:\s*/); m[k] = v }
493
+
494
+ expect( hash2 ).to include('verb' => 'tom')
495
+
496
+ expect( hash2 ).to include('params' => 'dick').
497
+ or include('parameters' => 'dick')
498
+
499
+ expect( hash2 ).to include('desc' => 'harry').
500
+ or include('description' => 'harry')
501
+
502
+ end
503
+
504
+ end
505
+ ##
506
+
507
+
508
+ describe '#respond_success' do
509
+
510
+ it "raises an error if we have no @reply_to" do
511
+ expect{ msg_stomp.respond_success }.to raise_exception NebulousError
512
+ end
513
+
514
+ it "returns the queue to respond on" do
515
+ q,_ = msg_cache.respond_success
516
+ expect( q ).to eq '/queue/thing'
517
+ end
518
+
519
+ it "returns a new message that has the success verb" do
520
+ _,m = msg_cache.respond_success
521
+ expect( m ).to be_a_kind_of Message
522
+ expect( m.verb ).to eq 'success'
523
+ end
524
+
525
+ end
526
+ ##
527
+
528
+
529
+ describe '#respond_error' do
530
+ let(:err) { NebulousError.new("test error") }
531
+
532
+ it "raises an error if we have no @reply_to" do
533
+ expect{ msg_stomp.respond_error('foo') }.to raise_exception NebulousError
534
+ end
535
+
536
+ it "requires an error parameter" do
537
+ expect{ msg_cache.respond_error() }.to raise_exception ArgumentError
538
+ expect{ msg_cache.respond_error('foo') }.not_to raise_exception
539
+ end
540
+
541
+ it "accepts an exception object" do
542
+ expect{ msg_cache.respond_error(err) }.not_to raise_exception
543
+ end
544
+
545
+ it "accepts an optional error field" do
546
+ expect{ msg_cache.respond_error('foo', :bar) }.not_to raise_exception
547
+ end
548
+
549
+ it "returns the queue to respond on" do
550
+ q,_ = msg_cache.respond_error('foo')
551
+ expect( q ).to eq '/queue/thing'
552
+
553
+ q,_ = msg_cache.respond_error(err, :foo)
554
+ expect( q ).to eq '/queue/thing'
555
+ end
556
+
557
+ it "returns a new message with the failure verb and details" do
558
+ _,m = msg_cache.respond_error('foo')
559
+ expect( m ).to be_a_kind_of Message
560
+ expect( m.verb ).to eq 'error'
561
+ expect( m.params ).to eq []
562
+ expect( m.desc ).to eq 'foo'
563
+
564
+ _,m = msg_cache.respond_error(err, :foo)
565
+ expect( m ).to be_a_kind_of Message
566
+ expect( m.verb ).to eq 'error'
567
+ expect( m.params ).to eq "foo"
568
+ expect( m.desc ).to eq err.message
569
+ end
570
+
571
+ end
572
+ ##
573
+
574
+ end
575
+