onstomp 1.0.0pre1

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.
Files changed (80) hide show
  1. data/.autotest +2 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +2 -0
  4. data/.yardopts +5 -0
  5. data/CHANGELOG.md +4 -0
  6. data/DeveloperNarrative.md +15 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.md +221 -0
  9. data/README.md +73 -0
  10. data/Rakefile +6 -0
  11. data/UserNarrative.md +8 -0
  12. data/examples/basic.rb +40 -0
  13. data/examples/events.rb +72 -0
  14. data/lib/onstomp/client.rb +152 -0
  15. data/lib/onstomp/components/frame.rb +108 -0
  16. data/lib/onstomp/components/frame_headers.rb +212 -0
  17. data/lib/onstomp/components/nil_processor.rb +20 -0
  18. data/lib/onstomp/components/scopes/header_scope.rb +25 -0
  19. data/lib/onstomp/components/scopes/receipt_scope.rb +25 -0
  20. data/lib/onstomp/components/scopes/transaction_scope.rb +191 -0
  21. data/lib/onstomp/components/scopes.rb +45 -0
  22. data/lib/onstomp/components/subscription.rb +30 -0
  23. data/lib/onstomp/components/threaded_processor.rb +62 -0
  24. data/lib/onstomp/components/uri.rb +30 -0
  25. data/lib/onstomp/components.rb +13 -0
  26. data/lib/onstomp/connections/base.rb +208 -0
  27. data/lib/onstomp/connections/heartbeating.rb +82 -0
  28. data/lib/onstomp/connections/serializers/stomp_1.rb +166 -0
  29. data/lib/onstomp/connections/serializers/stomp_1_0.rb +41 -0
  30. data/lib/onstomp/connections/serializers/stomp_1_1.rb +134 -0
  31. data/lib/onstomp/connections/serializers.rb +9 -0
  32. data/lib/onstomp/connections/stomp_1.rb +69 -0
  33. data/lib/onstomp/connections/stomp_1_0.rb +28 -0
  34. data/lib/onstomp/connections/stomp_1_1.rb +65 -0
  35. data/lib/onstomp/connections.rb +119 -0
  36. data/lib/onstomp/interfaces/client_configurable.rb +55 -0
  37. data/lib/onstomp/interfaces/client_events.rb +168 -0
  38. data/lib/onstomp/interfaces/connection_events.rb +62 -0
  39. data/lib/onstomp/interfaces/event_manager.rb +69 -0
  40. data/lib/onstomp/interfaces/frame_methods.rb +190 -0
  41. data/lib/onstomp/interfaces/receipt_manager.rb +33 -0
  42. data/lib/onstomp/interfaces/subscription_manager.rb +48 -0
  43. data/lib/onstomp/interfaces/uri_configurable.rb +106 -0
  44. data/lib/onstomp/interfaces.rb +14 -0
  45. data/lib/onstomp/version.rb +13 -0
  46. data/lib/onstomp.rb +147 -0
  47. data/onstomp.gemspec +29 -0
  48. data/spec/onstomp/client_spec.rb +265 -0
  49. data/spec/onstomp/components/frame_headers_spec.rb +163 -0
  50. data/spec/onstomp/components/frame_spec.rb +144 -0
  51. data/spec/onstomp/components/nil_processor_spec.rb +32 -0
  52. data/spec/onstomp/components/scopes/header_scope_spec.rb +27 -0
  53. data/spec/onstomp/components/scopes/receipt_scope_spec.rb +33 -0
  54. data/spec/onstomp/components/scopes/transaction_scope_spec.rb +227 -0
  55. data/spec/onstomp/components/scopes_spec.rb +63 -0
  56. data/spec/onstomp/components/subscription_spec.rb +58 -0
  57. data/spec/onstomp/components/threaded_processor_spec.rb +92 -0
  58. data/spec/onstomp/components/uri_spec.rb +33 -0
  59. data/spec/onstomp/connections/base_spec.rb +349 -0
  60. data/spec/onstomp/connections/heartbeating_spec.rb +132 -0
  61. data/spec/onstomp/connections/serializers/stomp_1_0_spec.rb +50 -0
  62. data/spec/onstomp/connections/serializers/stomp_1_1_spec.rb +99 -0
  63. data/spec/onstomp/connections/serializers/stomp_1_spec.rb +104 -0
  64. data/spec/onstomp/connections/stomp_1_0_spec.rb +54 -0
  65. data/spec/onstomp/connections/stomp_1_1_spec.rb +137 -0
  66. data/spec/onstomp/connections/stomp_1_spec.rb +113 -0
  67. data/spec/onstomp/connections_spec.rb +135 -0
  68. data/spec/onstomp/interfaces/client_events_spec.rb +108 -0
  69. data/spec/onstomp/interfaces/connection_events_spec.rb +55 -0
  70. data/spec/onstomp/interfaces/event_manager_spec.rb +72 -0
  71. data/spec/onstomp/interfaces/frame_methods_spec.rb +109 -0
  72. data/spec/onstomp/interfaces/receipt_manager_spec.rb +53 -0
  73. data/spec/onstomp/interfaces/subscription_manager_spec.rb +64 -0
  74. data/spec/onstomp_spec.rb +15 -0
  75. data/spec/spec_helper.rb +12 -0
  76. data/spec/support/custom_argument_matchers.rb +51 -0
  77. data/spec/support/frame_matchers.rb +88 -0
  78. data/spec/support/shared_frame_method_examples.rb +116 -0
  79. data/yard_extensions.rb +32 -0
  80. metadata +219 -0
@@ -0,0 +1,349 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ module OnStomp::Connections
5
+ describe Base do
6
+ let(:io) {
7
+ mock('io', :close => nil)
8
+ }
9
+ let(:client) {
10
+ mock('client', :dispatch_transmitted => nil,
11
+ :dispatch_received => nil)
12
+ }
13
+ let(:serializer) {
14
+ mock('serializer')
15
+ }
16
+ let(:connection) {
17
+ Base.new(io, client)
18
+ }
19
+ let(:frame) {
20
+ mock('frame')
21
+ }
22
+
23
+ describe ".method_missing" do
24
+ it "should raise an unsupported command error if the method ends in _frame" do
25
+ lambda { connection.lame_frame }.should raise_error(OnStomp::UnsupportedCommandError)
26
+ end
27
+
28
+ it "should do the regular jazz for other missing methods" do
29
+ lambda { connection.lame_lame }.should raise_error(NameError)
30
+ end
31
+ end
32
+
33
+ describe ".configure" do
34
+
35
+ end
36
+
37
+ describe ".connected?" do
38
+ it "should be connected if io is not closed" do
39
+ io.stub(:closed? => false)
40
+ connection.should be_connected
41
+ io.stub(:closed? => true)
42
+ connection.should_not be_connected
43
+ end
44
+ end
45
+
46
+ describe ".close" do
47
+ it "should close the socket if blocking is true" do
48
+ io.should_receive(:close)
49
+ connection.close true
50
+ end
51
+
52
+ it "should close the socket within single_io_write" do
53
+ io.should_receive :close
54
+ connection.close
55
+ connection.io_process_write
56
+ end
57
+ end
58
+
59
+ describe ".io_process" do
60
+ it "should invoke one io_process_read and one io_process_write" do
61
+ connection.should_receive :io_process_read
62
+ connection.should_receive :io_process_write
63
+ connection.io_process
64
+ end
65
+ end
66
+
67
+ describe ".write_frame_nonblock" do
68
+ before(:each) do
69
+ connection.stub(:serializer => serializer)
70
+ end
71
+ it "should serialize the frame and push it to the buffer" do
72
+ serializer.stub(:frame_to_bytes).with(frame).and_return('FRAME_SERIALIZED')
73
+ connection.should_receive(:push_write_buffer).with('FRAME_SERIALIZED', frame)
74
+ connection.write_frame_nonblock frame
75
+ end
76
+ end
77
+
78
+ describe ".(push|shift|unshift)_write_buffer" do
79
+ it "should not add to the buffer if it's closing" do
80
+ connection.close
81
+ connection.push_write_buffer 'FRAME_SERIALIZED', frame
82
+ connection.should_not_receive :io_process_write
83
+ connection.close true
84
+ end
85
+ it "should shift the first element off the write buffer" do
86
+ connection.push_write_buffer 'FRAME_SERIALIZED', frame
87
+ connection.push_write_buffer 'FRAME_SERIALIZED2', frame
88
+ connection.shift_write_buffer.should == ['FRAME_SERIALIZED', frame]
89
+ end
90
+ it "should put the data and frame at the beginning of the buffer" do
91
+ connection.push_write_buffer 'FRAME_SERIALIZED', frame
92
+ connection.unshift_write_buffer 'FRAME_SERIALIZED2', frame
93
+ connection.shift_write_buffer.should == ['FRAME_SERIALIZED2', frame]
94
+ end
95
+ end
96
+
97
+ describe ".io_process_write" do
98
+ it "should not write if the buffer is empty" do
99
+ io.should_not_receive :write_nonblock
100
+ connection.io_process_write
101
+ end
102
+ it "should not write if the buffer has something but IO.select returns nil" do
103
+ IO.stub(:select => nil)
104
+ connection.push_write_buffer 'FRAME_SERIALIZED', frame
105
+ io.should_not_receive :write_nonblock
106
+ connection.io_process_write
107
+ end
108
+ it "should write to IO in a non-blocking fashion otherwise" do
109
+ connection.stub(:connected? => true)
110
+ IO.stub(:select => true)
111
+ connection.push_write_buffer 'FRAME_SERIALIZED', frame
112
+ io.should_receive(:write_nonblock).with('FRAME_SERIALIZED').and_return(16)
113
+ client.should_receive(:dispatch_transmitted).with(frame)
114
+ connection.io_process_write
115
+ end
116
+ it "should put back the remaining data" do
117
+ connection.stub(:connected? => true)
118
+ IO.stub(:select => true)
119
+ connection.push_write_buffer 'FRAME_SERIALIZED', frame
120
+ io.should_receive(:write_nonblock).with('FRAME_SERIALIZED').and_return(5)
121
+ connection.should_receive(:unshift_write_buffer).with('_SERIALIZED', frame)
122
+ connection.io_process_write
123
+ end
124
+ it "should put all the data back if EINTR is raised" do
125
+ connection.stub(:connected? => true)
126
+ IO.stub(:select => true)
127
+ connection.push_write_buffer 'FRAME_SERIALIZED', frame
128
+ io.should_receive(:write_nonblock).with('FRAME_SERIALIZED').and_raise(Errno::EINTR)
129
+ connection.should_receive(:unshift_write_buffer).with('FRAME_SERIALIZED', frame)
130
+ connection.io_process_write
131
+ end
132
+ it "should put all the data back if EAGAIN is raised" do
133
+ connection.stub(:connected? => true)
134
+ IO.stub(:select => true)
135
+ connection.push_write_buffer 'FRAME_SERIALIZED', frame
136
+ io.should_receive(:write_nonblock).with('FRAME_SERIALIZED').and_raise(Errno::EAGAIN)
137
+ connection.should_receive(:unshift_write_buffer).with('FRAME_SERIALIZED', frame)
138
+ connection.io_process_write
139
+ end
140
+ it "should put all the data back if EWOULDBLOCK is raised" do
141
+ connection.stub(:connected? => true)
142
+ IO.stub(:select => true)
143
+ connection.push_write_buffer 'FRAME_SERIALIZED', frame
144
+ io.should_receive(:write_nonblock).with('FRAME_SERIALIZED').and_raise(Errno::EWOULDBLOCK)
145
+ connection.should_receive(:unshift_write_buffer).with('FRAME_SERIALIZED', frame)
146
+ connection.io_process_write
147
+ end
148
+ it "should close the connection and re-raise if an EOFError is raised" do
149
+ connection.stub(:connected? => true)
150
+ IO.stub(:select => true)
151
+ connection.push_write_buffer 'FRAME_SERIALIZED', frame
152
+ io.should_receive(:write_nonblock).with('FRAME_SERIALIZED').and_raise(EOFError)
153
+ io.should_receive(:close)
154
+ lambda { connection.io_process_write }.should raise_error(EOFError)
155
+ end
156
+ it "should close the connection and re-raise if an IOError is raised" do
157
+ connection.stub(:connected? => true)
158
+ IO.stub(:select => true)
159
+ connection.push_write_buffer 'FRAME_SERIALIZED', frame
160
+ io.should_receive(:write_nonblock).with('FRAME_SERIALIZED').and_raise(IOError)
161
+ io.should_receive(:close)
162
+ lambda { connection.io_process_write }.should raise_error(IOError)
163
+ end
164
+ it "should close the connection and re-raise if an EOFError is raised" do
165
+ connection.stub(:connected? => true)
166
+ IO.stub(:select => true)
167
+ connection.push_write_buffer 'FRAME_SERIALIZED', frame
168
+ io.should_receive(:write_nonblock).with('FRAME_SERIALIZED').and_raise(SystemCallError.new('msg', 13))
169
+ io.should_receive(:close)
170
+ lambda { connection.io_process_write }.should raise_error(SystemCallError)
171
+ end
172
+ it "should re-raise on any other exception" do
173
+ triggered = false
174
+ connection.on_terminated { triggered = true }
175
+ connection.stub(:connected? => true)
176
+ IO.stub(:select => true)
177
+ connection.push_write_buffer 'FRAME_SERIALIZED', frame
178
+ io.should_receive(:close)
179
+ io.should_receive(:write_nonblock).with('FRAME_SERIALIZED').and_raise(Exception)
180
+ lambda { connection.io_process_write }.should raise_error(Exception)
181
+ triggered.should be_true
182
+ end
183
+ end
184
+ describe ".io_process_read" do
185
+ before(:each) do
186
+ connection.stub(:serializer => serializer)
187
+ end
188
+ it "should not read if the connection is not alive" do
189
+ connection.stub(:connected? => false)
190
+ io.should_not_receive :read_nonblock
191
+ connection.io_process_read
192
+ end
193
+ it "should not read if IO.select returns nil" do
194
+ connection.stub(:connected? => true)
195
+ IO.stub(:select => nil)
196
+ io.should_not_receive :read_nonblock
197
+ connection.io_process_read
198
+ end
199
+ it "should read IO in a non-blocking fashion otherwise" do
200
+ connection.stub(:connected? => true)
201
+ IO.stub(:select => true)
202
+ io.should_receive(:read_nonblock).with(Base::MAX_BYTES_PER_READ).and_return('FRAME_SERIALIZED')
203
+ serializer.should_receive(:bytes_to_frame).with(['FRAME_SERIALIZED'])
204
+ connection.io_process_read
205
+ end
206
+ it "should dispatch and yield a frame if the serializer yields one" do
207
+ yielded = nil
208
+ connection.stub(:connected? => true)
209
+ IO.stub(:select => true)
210
+ io.should_receive(:read_nonblock).with(Base::MAX_BYTES_PER_READ).and_return('FRAME_SERIALIZED')
211
+ serializer.should_receive(:bytes_to_frame).with(['FRAME_SERIALIZED']).and_yield(frame)
212
+ client.should_receive(:dispatch_received).with(frame)
213
+ connection.io_process_read do |f|
214
+ yielded = f
215
+ end
216
+ yielded.should == frame
217
+ end
218
+ it "should not raise an error if EINTR is raised" do
219
+ connection.stub(:connected? => true)
220
+ IO.stub(:select => true)
221
+ io.should_receive(:read_nonblock).with(Base::MAX_BYTES_PER_READ).and_raise(Errno::EINTR)
222
+ lambda { connection.io_process_read }.should_not raise_error
223
+ end
224
+ it "should put all the data back if EAGAIN is raised" do
225
+ connection.stub(:connected? => true)
226
+ IO.stub(:select => true)
227
+ io.should_receive(:read_nonblock).with(Base::MAX_BYTES_PER_READ).and_raise(Errno::EAGAIN)
228
+ lambda { connection.io_process_read }.should_not raise_error
229
+ end
230
+ it "should put all the data back if EWOULDBLOCK is raised" do
231
+ connection.stub(:connected? => true)
232
+ IO.stub(:select => true)
233
+ io.should_receive(:read_nonblock).with(Base::MAX_BYTES_PER_READ).and_raise(Errno::EWOULDBLOCK)
234
+ lambda { connection.io_process_read }.should_not raise_error
235
+ end
236
+ it "should close the connection and not raise error if an EOFError is raised?" do
237
+ connection.stub(:connected? => true)
238
+ IO.stub(:select => true)
239
+ io.should_receive(:read_nonblock).with(Base::MAX_BYTES_PER_READ).and_raise(EOFError)
240
+ io.should_receive(:close)
241
+ lambda { connection.io_process_read }.should_not raise_error
242
+ end
243
+ it "should close the connection and re-raise if an IOError is raised" do
244
+ connection.stub(:connected? => true)
245
+ IO.stub(:select => true)
246
+ io.should_receive(:read_nonblock).with(Base::MAX_BYTES_PER_READ).and_raise(IOError)
247
+ io.should_receive(:close)
248
+ lambda { connection.io_process_read }.should raise_error(IOError)
249
+ end
250
+ it "should close the connection and re-raise if an EOFError is raised" do
251
+ connection.stub(:connected? => true)
252
+ IO.stub(:select => true)
253
+ io.should_receive(:read_nonblock).with(Base::MAX_BYTES_PER_READ).and_raise(SystemCallError.new('msg', 13))
254
+ io.should_receive(:close)
255
+ lambda { connection.io_process_read }.should raise_error(SystemCallError)
256
+ end
257
+ it "should re-raise on any other exception" do
258
+ triggered = false
259
+ connection.on_terminated { triggered = true }
260
+ connection.stub(:connected? => true)
261
+ IO.stub(:select => true)
262
+ io.should_receive(:close)
263
+ io.should_receive(:read_nonblock).with(Base::MAX_BYTES_PER_READ).and_raise(Exception)
264
+ lambda { connection.io_process_read }.should raise_error(Exception)
265
+ triggered.should be_true
266
+ end
267
+ end
268
+
269
+ describe ".connect" do
270
+ let(:headers) { [] }
271
+ let(:connect_frame) {
272
+ OnStomp::Components::Frame.new('CONNECT')
273
+ }
274
+ let(:connected_frame) {
275
+ OnStomp::Components::Frame.new('CONNECTED')
276
+ }
277
+
278
+ it "should raise an error if the first frame read is not CONNECTED" do
279
+ connection.should_receive(:connect_frame).and_return(connect_frame)
280
+ connection.should_receive(:write_frame_nonblock).with(connect_frame)
281
+ connection.should_receive(:io_process_write).and_yield(connect_frame)
282
+ connection.should_receive(:io_process_read).and_yield(connected_frame)
283
+ connected_frame.command = 'NOT CONNECTED'
284
+ lambda { connection.connect(client, *headers) }.should raise_error(OnStomp::ConnectFailedError)
285
+ end
286
+ it "should raise an error if the CONNECTED frame specifies an unsolicited version" do
287
+ connection.should_receive(:connect_frame).and_return(connect_frame)
288
+ connection.should_receive(:write_frame_nonblock).with(connect_frame)
289
+ connection.should_receive(:io_process_write).and_yield(connect_frame)
290
+ connection.should_receive(:io_process_read).and_yield(connected_frame)
291
+ connected_frame[:version] = '1.9'
292
+ client.stub(:versions => [ '1.0', '1.1' ])
293
+ lambda { connection.connect(client, *headers) }.should raise_error(OnStomp::UnsupportedProtocolVersionError)
294
+ end
295
+ it "should assume version 1.0 if no version header is set" do
296
+ connection.should_receive(:connect_frame).and_return(connect_frame)
297
+ connection.should_receive(:write_frame_nonblock).with(connect_frame)
298
+ connection.should_receive(:io_process_write).and_yield(connect_frame)
299
+ connection.should_receive(:io_process_read).and_yield(connected_frame)
300
+ client.stub(:versions => [ '1.0', '1.1' ])
301
+ connection.connect(client, *headers).should == ['1.0', connected_frame]
302
+ end
303
+ it "should return the CONNECTED version header if it's included" do
304
+ connection.should_receive(:connect_frame).and_return(connect_frame)
305
+ connection.should_receive(:write_frame_nonblock).with(connect_frame)
306
+ connection.should_receive(:io_process_write).and_yield(connect_frame)
307
+ connection.should_receive(:io_process_read).and_yield(connected_frame)
308
+ connected_frame[:version] = '2.3'
309
+ client.stub(:versions => [ '1.0', '2.3' ])
310
+ connection.connect(client, *headers).should == ['2.3', connected_frame]
311
+ end
312
+
313
+ it "should trigger :on_died once, if the connection was up but is no longer connected" do
314
+ connection.stub(:connect_frame => connect_frame,
315
+ :write_frame_nonblock => connect_frame)
316
+ client.stub(:versions => ['1.0', '1.1'])
317
+ connection.stub(:io_process_write).and_yield(connect_frame)
318
+ connection.stub(:io_process_read).and_yield(connected_frame)
319
+ triggered = 0
320
+ connection.on_died { |cl, cn| triggered += 1 }
321
+ connection.connect client
322
+ connection.stub(:io_process_write => nil, :io_process_read => nil)
323
+ connection.stub(:connected? => false)
324
+ connection.io_process
325
+ connection.io_process
326
+ triggered.should == 1
327
+ end
328
+ end
329
+
330
+ describe ".configure" do
331
+ let(:client_bindings) {
332
+ { :died => nil, :established => nil }
333
+ }
334
+ it "should set its version parameter based on the supplied CONNECTED frame" do
335
+ frame.stub(:header?).with(:version).and_return(true)
336
+ frame.stub(:[]).with(:version).and_return('9.x')
337
+ connection.should_receive(:install_bindings_from_client).with(client_bindings)
338
+ connection.configure frame, client_bindings
339
+ connection.version.should == '9.x'
340
+ end
341
+ it "should set its version parameter to 1.0 if the header is not present" do
342
+ frame.stub(:header?).with(:version).and_return(false)
343
+ connection.should_receive(:install_bindings_from_client).with(client_bindings)
344
+ connection.configure frame, client_bindings
345
+ connection.version.should == '1.0'
346
+ end
347
+ end
348
+ end
349
+ end
@@ -0,0 +1,132 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ module OnStomp::Connections
5
+ describe Heartbeating do
6
+ let(:connection) {
7
+ mock('connection').tap do |m|
8
+ m.extend Heartbeating
9
+ end
10
+ }
11
+
12
+ describe ".configure_heartbeating" do
13
+ it "should use 0 for client beats if either side is zero" do
14
+ connection.configure_heartbeating([0,500], [100, 300])
15
+ connection.heartbeating.first.should == 0
16
+ connection.configure_heartbeating([300,500], [100, 0])
17
+ connection.heartbeating.first.should == 0
18
+ end
19
+ it "should use 0 for broker beats if either side is zero" do
20
+ connection.configure_heartbeating([500,0], [100, 300])
21
+ connection.heartbeating.last.should == 0
22
+ connection.configure_heartbeating([300,100], [0, 500])
23
+ connection.heartbeating.last.should == 0
24
+ end
25
+ it "should use the maximums for heartbeating" do
26
+ connection.configure_heartbeating([500,700], [100, 300])
27
+ connection.heartbeating.should == [500, 700]
28
+ end
29
+ end
30
+
31
+ describe ".pulse?" do
32
+ it "should not have a pulse if it has no client pulse" do
33
+ connection.stub(:client_pulse? => false, :broker_pulse? => true)
34
+ connection.pulse?.should be_false
35
+ end
36
+ it "should not have a pulse if it has no broker pulse" do
37
+ connection.stub(:client_pulse? => true, :broker_pulse? => false)
38
+ connection.pulse?.should be_false
39
+ end
40
+ it "should have a pulse if it has both a client and broker pulse" do
41
+ connection.stub(:client_pulse? => true, :broker_pulse? => true)
42
+ connection.pulse?.should be_true
43
+ end
44
+ end
45
+
46
+ describe ".heartbeat_client_limit" do
47
+ it "should be 110% of a positive client heartbeat value" do
48
+ connection.stub(:heartbeating => [64, 0])
49
+ connection.heartbeat_client_limit.should == 70.4
50
+ end
51
+ it "should be 0 if the client heartbeat value is 0" do
52
+ connection.stub(:heartbeating => [0, 90])
53
+ connection.heartbeat_client_limit.should == 0
54
+ end
55
+ end
56
+
57
+ describe ".heartbeat_broker_limit" do
58
+ it "should be 110% of a positive broker heartbeat value" do
59
+ connection.stub(:heartbeating => [0, 32])
60
+ connection.heartbeat_broker_limit.should == 35.2
61
+ end
62
+ it "should be 0 if the broker heartbeat value is 0" do
63
+ connection.stub(:heartbeating => [90, 0])
64
+ connection.heartbeat_broker_limit.should == 0
65
+ end
66
+ end
67
+
68
+ describe ".duration_since_transmitted" do
69
+ it "should be nil if last_transmitted_at is nil" do
70
+ connection.stub(:last_transmitted_at => nil)
71
+ connection.duration_since_transmitted.should be_nil
72
+ end
73
+ it "should be the difference between now and the last_transmitted_at in milliseconds" do
74
+ Time.stub(:now => 10)
75
+ connection.stub(:last_transmitted_at => 8.5)
76
+ connection.duration_since_transmitted.should == 1500
77
+ end
78
+ end
79
+
80
+ describe ".duration_since_received" do
81
+ it "should be nil if last_received_at is nil" do
82
+ connection.stub(:last_received_at => nil)
83
+ connection.duration_since_received.should be_nil
84
+ end
85
+ it "should be the difference between now and the last_received_at in milliseconds" do
86
+ Time.stub(:now => 10)
87
+ connection.stub(:last_received_at => 6)
88
+ connection.duration_since_received.should == 4000
89
+ end
90
+ end
91
+
92
+ describe ".client_pulse?" do
93
+ it "should be true if client heartbeating is disabled" do
94
+ connection.stub(:heartbeat_client_limit => 0)
95
+ connection.client_pulse?.should be_true
96
+ end
97
+ it "should be true if duration since transmitted is at most client limit" do
98
+ connection.stub(:heartbeat_client_limit => 10)
99
+ connection.stub(:duration_since_transmitted => 1)
100
+ connection.client_pulse?.should be_true
101
+ connection.stub(:heartbeat_client_limit => 10)
102
+ connection.stub(:duration_since_transmitted => 10)
103
+ connection.client_pulse?.should be_true
104
+ end
105
+ it "should be false if duration since transmitted is greater than client limit" do
106
+ connection.stub(:heartbeat_client_limit => 10)
107
+ connection.stub(:duration_since_transmitted => 11)
108
+ connection.client_pulse?.should be_false
109
+ end
110
+ end
111
+
112
+ describe ".broker_pulse?" do
113
+ it "should be true if broker heartbeating is disabled" do
114
+ connection.stub(:heartbeat_broker_limit => 0)
115
+ connection.broker_pulse?.should be_true
116
+ end
117
+ it "should be true if duration since transmitted is at most broker limit" do
118
+ connection.stub(:heartbeat_broker_limit => 10)
119
+ connection.stub(:duration_since_received => 1)
120
+ connection.broker_pulse?.should be_true
121
+ connection.stub(:heartbeat_broker_limit => 10)
122
+ connection.stub(:duration_since_received => 10)
123
+ connection.broker_pulse?.should be_true
124
+ end
125
+ it "should be false if duration since received is greater than broker limit" do
126
+ connection.stub(:heartbeat_broker_limit => 10)
127
+ connection.stub(:duration_since_received => 11)
128
+ connection.broker_pulse?.should be_false
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,50 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ module OnStomp::Connections::Serializers
5
+ describe Stomp_1_0 do
6
+ let(:serializer) { Stomp_1_0.new }
7
+ describe "ancestors" do
8
+ it "should be a kind of Stomp_1 serializer" do
9
+ serializer.should be_a_kind_of(OnStomp::Connections::Serializers::Stomp_1)
10
+ end
11
+ end
12
+ describe ".frame_to_string" do
13
+ # So tests pass with Ruby 1.8.7, we need to get the headers in order
14
+ let(:frame_without_body) {
15
+ OnStomp::Components::Frame.new('COMMAND').tap do |f|
16
+ f[:header1] = 'value 1'
17
+ f[:header2] = 'value 2'
18
+ f["header\nwith:specials"] = "value\nwith\nspecials"
19
+ end
20
+ }
21
+ let(:frame_with_body) {
22
+ OnStomp::Components::Frame.new('COMMAND',{},"body of message").tap do |f|
23
+ f[:header1] = 'value 1'
24
+ f[:header2] = 'value 2'
25
+ f["header\nwith:specials"] = "value\nwith\nspecials"
26
+ end
27
+ }
28
+ it "should convert a frame to a string with escaped headers" do
29
+ serializer.frame_to_string(frame_without_body).should ==
30
+ "COMMAND\nheader1:value 1\nheader2:value 2\nheaderwithspecials:valuewithspecials\n\n\000"
31
+ end
32
+ it "should generate a content-length" do
33
+ serializer.frame_to_string(frame_with_body).should ==
34
+ "COMMAND\nheader1:value 1\nheader2:value 2\nheaderwithspecials:valuewithspecials\ncontent-length:15\n\nbody of message\000"
35
+ end
36
+ end
37
+
38
+ describe ".split_header" do
39
+ it "should raise a malformed header error if the header line has no ':'" do
40
+ lambda {
41
+ serializer.split_header('header-name')
42
+ }.should raise_error(OnStomp::MalformedHeaderError)
43
+ end
44
+ it "should return a header name / value pair" do
45
+ serializer.split_header('header-name: some : value ').should ==
46
+ ['header-name', ' some : value ']
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,99 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ module OnStomp::Connections::Serializers
5
+ describe Stomp_1_1 do
6
+ let(:serializer) { Stomp_1_1.new }
7
+ describe "ancestors" do
8
+ it "should be a kind of Stomp_1 serializer" do
9
+ serializer.should be_a_kind_of(OnStomp::Connections::Serializers::Stomp_1)
10
+ end
11
+ end
12
+
13
+ describe ".frame_to_string" do
14
+ # So tests pass with Ruby 1.8.7, we need to get the headers in order
15
+ let(:frame_without_body) {
16
+ OnStomp::Components::Frame.new('COMMAND').tap do |f|
17
+ f[:header1] = 'value 1'
18
+ f[:header2] = 'value 2'
19
+ f["header\nwith:special \\characters"] = "value:with\\special\ncharacters"
20
+ end
21
+ }
22
+ let(:frame_with_body) {
23
+ OnStomp::Components::Frame.new('COMMAND',{},'body of message').tap do |f|
24
+ f[:header1] = 'value 1'
25
+ f[:header2] = 'value 2'
26
+ f["header\nwith:special \\characters"] = "value:with\\special\ncharacters"
27
+ end
28
+ }
29
+ it "should convert a frame to a string with escaped headers" do
30
+ serializer.frame_to_string(frame_without_body).should ==
31
+ "COMMAND\nheader1:value 1\nheader2:value 2\nheader\\nwith\\cspecial \\\\characters:value\\cwith\\\\special\\ncharacters\n\n\000"
32
+ end
33
+ it "should generate a content-length and content-type" do
34
+ expected_str = if RUBY_VERSION >= '1.9'
35
+ frame_with_body.body = frame_with_body.body.encode('ISO-8859-1')
36
+ "COMMAND\nheader1:value 1\nheader2:value 2\nheader\\nwith\\cspecial \\\\characters:value\\cwith\\\\special\\ncharacters\ncontent-type:text/plain;charset=ISO-8859-1\ncontent-length:15\n\nbody of message\000"
37
+ else
38
+ "COMMAND\nheader1:value 1\nheader2:value 2\nheader\\nwith\\cspecial \\\\characters:value\\cwith\\\\special\\ncharacters\ncontent-length:15\n\nbody of message\000"
39
+ end
40
+ serializer.frame_to_string(frame_with_body).should == expected_str
41
+ end
42
+ end
43
+
44
+ describe ".split_header" do
45
+ it "should raise a malformed header error if the header line has no ':'" do
46
+ lambda {
47
+ serializer.split_header('header-name')
48
+ }.should raise_error(OnStomp::MalformedHeaderError)
49
+ end
50
+ it "should raise an invalid escape sequence error given a bad escape sequence" do
51
+ lambda {
52
+ serializer.split_header('header-name:header\\rvalue')
53
+ }.should raise_error(OnStomp::InvalidHeaderEscapeSequenceError)
54
+ end
55
+ it "should raise an invalid escape sequence error given an incomplete escape sequence" do
56
+ lambda {
57
+ serializer.split_header('header-name\\:header\\cvalue')
58
+ }.should raise_error(OnStomp::InvalidHeaderEscapeSequenceError)
59
+ end
60
+ it "should return an unescaped header name/value pair" do
61
+ serializer.split_header('header\\cname\\\\is\\nme:value\\\\is\\nthis\\cguy').should ==
62
+ ["header:name\\is\nme", "value\\is\nthis:guy"]
63
+ end
64
+ end
65
+
66
+ describe ".prepare_parsed_frame" do
67
+ let(:iso8859_frame) {
68
+ OnStomp::Components::Frame.new('COMMAND', {
69
+ :header1 => 'value 1',
70
+ :header2 => 'value 2',
71
+ :'content-length' => '5',
72
+ :'content-type' => 'text/plain;charset=ISO-8859-1',
73
+ "header\nwith:special \\characters" => "value:with\\special\ncharacters"
74
+ }, "h\xEBllo")
75
+ }
76
+ let(:utf8_frame) {
77
+ OnStomp::Components::Frame.new('COMMAND', {
78
+ :header1 => 'value 1',
79
+ :header2 => 'value 2',
80
+ :'content-length' => '6',
81
+ :'content-type' => 'text/plain',
82
+ "header\nwith:special \\characters" => "value:with\\special\ncharacters"
83
+ }, "h\xC3\xABllo")
84
+ }
85
+ before(:each) do
86
+ if RUBY_VERSION > '1.9'
87
+ iso8859_frame.body.force_encoding('ASCII-8BIT')
88
+ utf8_frame.body.force_encoding('ASCII-8BIT')
89
+ end
90
+ end
91
+ it "should force a body encoding on the frame" do
92
+ serializer.prepare_parsed_frame(iso8859_frame)
93
+ serializer.prepare_parsed_frame(utf8_frame)
94
+ iso8859_frame.should have_body_encoding('ISO-8859-1')
95
+ utf8_frame.should have_body_encoding('UTF-8')
96
+ end
97
+ end
98
+ end
99
+ end