shingara-blather 0.4.8

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 (100) hide show
  1. data/LICENSE +22 -0
  2. data/README.md +162 -0
  3. data/examples/echo.rb +18 -0
  4. data/examples/execute.rb +16 -0
  5. data/examples/ping_pong.rb +37 -0
  6. data/examples/print_hierarchy.rb +76 -0
  7. data/examples/rosterprint.rb +14 -0
  8. data/examples/stream_only.rb +27 -0
  9. data/examples/xmpp4r/echo.rb +35 -0
  10. data/lib/blather/client/client.rb +310 -0
  11. data/lib/blather/client/dsl/pubsub.rb +170 -0
  12. data/lib/blather/client/dsl.rb +264 -0
  13. data/lib/blather/client.rb +87 -0
  14. data/lib/blather/core_ext/nokogiri.rb +40 -0
  15. data/lib/blather/errors/sasl_error.rb +43 -0
  16. data/lib/blather/errors/stanza_error.rb +107 -0
  17. data/lib/blather/errors/stream_error.rb +82 -0
  18. data/lib/blather/errors.rb +69 -0
  19. data/lib/blather/jid.rb +142 -0
  20. data/lib/blather/roster.rb +111 -0
  21. data/lib/blather/roster_item.rb +122 -0
  22. data/lib/blather/stanza/disco/disco_info.rb +176 -0
  23. data/lib/blather/stanza/disco/disco_items.rb +132 -0
  24. data/lib/blather/stanza/disco.rb +25 -0
  25. data/lib/blather/stanza/iq/query.rb +53 -0
  26. data/lib/blather/stanza/iq/roster.rb +179 -0
  27. data/lib/blather/stanza/iq.rb +138 -0
  28. data/lib/blather/stanza/message.rb +332 -0
  29. data/lib/blather/stanza/presence/status.rb +212 -0
  30. data/lib/blather/stanza/presence/subscription.rb +101 -0
  31. data/lib/blather/stanza/presence.rb +163 -0
  32. data/lib/blather/stanza/pubsub/affiliations.rb +79 -0
  33. data/lib/blather/stanza/pubsub/create.rb +65 -0
  34. data/lib/blather/stanza/pubsub/errors.rb +18 -0
  35. data/lib/blather/stanza/pubsub/event.rb +123 -0
  36. data/lib/blather/stanza/pubsub/items.rb +103 -0
  37. data/lib/blather/stanza/pubsub/publish.rb +103 -0
  38. data/lib/blather/stanza/pubsub/retract.rb +92 -0
  39. data/lib/blather/stanza/pubsub/subscribe.rb +68 -0
  40. data/lib/blather/stanza/pubsub/subscription.rb +134 -0
  41. data/lib/blather/stanza/pubsub/subscriptions.rb +81 -0
  42. data/lib/blather/stanza/pubsub/unsubscribe.rb +68 -0
  43. data/lib/blather/stanza/pubsub.rb +129 -0
  44. data/lib/blather/stanza/pubsub_owner/delete.rb +52 -0
  45. data/lib/blather/stanza/pubsub_owner/purge.rb +52 -0
  46. data/lib/blather/stanza/pubsub_owner.rb +51 -0
  47. data/lib/blather/stanza.rb +149 -0
  48. data/lib/blather/stream/client.rb +31 -0
  49. data/lib/blather/stream/component.rb +38 -0
  50. data/lib/blather/stream/features/resource.rb +63 -0
  51. data/lib/blather/stream/features/sasl.rb +187 -0
  52. data/lib/blather/stream/features/session.rb +44 -0
  53. data/lib/blather/stream/features/tls.rb +28 -0
  54. data/lib/blather/stream/features.rb +53 -0
  55. data/lib/blather/stream/parser.rb +102 -0
  56. data/lib/blather/stream.rb +231 -0
  57. data/lib/blather/xmpp_node.rb +218 -0
  58. data/lib/blather.rb +78 -0
  59. data/spec/blather/client/client_spec.rb +559 -0
  60. data/spec/blather/client/dsl/pubsub_spec.rb +462 -0
  61. data/spec/blather/client/dsl_spec.rb +143 -0
  62. data/spec/blather/core_ext/nokogiri_spec.rb +83 -0
  63. data/spec/blather/errors/sasl_error_spec.rb +33 -0
  64. data/spec/blather/errors/stanza_error_spec.rb +129 -0
  65. data/spec/blather/errors/stream_error_spec.rb +108 -0
  66. data/spec/blather/errors_spec.rb +33 -0
  67. data/spec/blather/jid_spec.rb +87 -0
  68. data/spec/blather/roster_item_spec.rb +96 -0
  69. data/spec/blather/roster_spec.rb +103 -0
  70. data/spec/blather/stanza/discos/disco_info_spec.rb +226 -0
  71. data/spec/blather/stanza/discos/disco_items_spec.rb +148 -0
  72. data/spec/blather/stanza/iq/query_spec.rb +64 -0
  73. data/spec/blather/stanza/iq/roster_spec.rb +140 -0
  74. data/spec/blather/stanza/iq_spec.rb +45 -0
  75. data/spec/blather/stanza/message_spec.rb +132 -0
  76. data/spec/blather/stanza/presence/status_spec.rb +132 -0
  77. data/spec/blather/stanza/presence/subscription_spec.rb +105 -0
  78. data/spec/blather/stanza/presence_spec.rb +66 -0
  79. data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
  80. data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
  81. data/spec/blather/stanza/pubsub/event_spec.rb +84 -0
  82. data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
  83. data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
  84. data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
  85. data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
  86. data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
  87. data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
  88. data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +61 -0
  89. data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
  90. data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
  91. data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
  92. data/spec/blather/stanza/pubsub_spec.rb +67 -0
  93. data/spec/blather/stanza_spec.rb +116 -0
  94. data/spec/blather/stream/client_spec.rb +1011 -0
  95. data/spec/blather/stream/component_spec.rb +95 -0
  96. data/spec/blather/stream/parser_spec.rb +145 -0
  97. data/spec/blather/xmpp_node_spec.rb +231 -0
  98. data/spec/fixtures/pubsub.rb +311 -0
  99. data/spec/spec_helper.rb +43 -0
  100. metadata +249 -0
@@ -0,0 +1,559 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
2
+ require 'blather/client/client'
3
+
4
+ describe Blather::Client do
5
+ before do
6
+ @client = Blather::Client.new
7
+ @stream = mock()
8
+ @stream.stubs(:send)
9
+ @jid = Blather::JID.new('n@d/r')
10
+ @client.post_init @stream, @jid
11
+ end
12
+
13
+ it 'provides a Blather::JID reader' do
14
+ @client.must_respond_to :jid
15
+ @client.jid.must_equal @jid
16
+ end
17
+
18
+ it 'provides a reader for the roster' do
19
+ @client.must_respond_to :roster
20
+ @client.roster.must_be_kind_of Blather::Roster
21
+ end
22
+
23
+ it 'provides a status reader' do
24
+ @client.must_respond_to :status
25
+ @client.status = :away
26
+ @client.status.must_equal :away
27
+ end
28
+
29
+ it 'can be setup' do
30
+ @client.must_respond_to :setup
31
+ @client.setup('me@me.com', 'pass').must_equal @client
32
+ end
33
+
34
+ it 'knows if it has been setup' do
35
+ @client.must_respond_to :setup?
36
+ @client.setup?.must_equal false
37
+ @client.setup 'me@me.com', 'pass'
38
+ @client.setup?.must_equal true
39
+ end
40
+
41
+ it 'cannot be run before being setup' do
42
+ lambda { @client.run }.must_raise RuntimeError
43
+ end
44
+
45
+ it 'starts up a Component connection when setup without a node' do
46
+ setup = 'pubsub.jabber.local', 'secret'
47
+ @client.setup *setup
48
+ Blather::Stream::Component.expects(:start).with @client, *setup
49
+ @client.run
50
+ end
51
+
52
+ it 'starts up a Client connection when setup with a node' do
53
+ setup = 'test@jabber.local', 'secret'
54
+ @client.setup *setup
55
+ Blather::Stream::Client.expects(:start).with @client, *setup
56
+ @client.run
57
+ end
58
+
59
+ it 'writes to the connection the closes when #close is called' do
60
+ stream = mock()
61
+ stream.expects(:close_connection_after_writing)
62
+ @client.setup('me.com', 'secret')
63
+ @client.post_init stream, Blather::JID.new('me.com')
64
+ @client.close
65
+ end
66
+
67
+ it 'shuts down EM when #unbind is called if it is running' do
68
+ EM.expects(:reactor_running?).returns true
69
+ EM.expects(:stop)
70
+ @client.unbind
71
+ end
72
+
73
+ it 'does nothing when #unbind is called and EM is not running' do
74
+ EM.expects(:reactor_running?).returns false
75
+ EM.expects(:stop).never
76
+ @client.unbind
77
+ end
78
+
79
+ it 'calls the :disconnected handler with #unbind is called' do
80
+ EM.expects(:reactor_running?).returns false
81
+ disconnected = mock()
82
+ disconnected.expects(:call)
83
+ @client.register_handler(:disconnected) { disconnected.call }
84
+ @client.unbind
85
+ end
86
+
87
+ it 'does not call EM.stop on #unbind if a handler returns positive' do
88
+ EM.expects(:reactor_running?).never
89
+ EM.expects(:stop).never
90
+ disconnected = mock()
91
+ disconnected.expects(:call).returns true
92
+ @client.register_handler(:disconnected) { disconnected.call }
93
+ @client.unbind
94
+ end
95
+
96
+ it 'calls EM.stop on #unbind if a handler returns negative' do
97
+ EM.expects(:reactor_running?).returns true
98
+ EM.expects(:stop)
99
+ disconnected = mock()
100
+ disconnected.expects(:call).returns false
101
+ @client.register_handler(:disconnected) { disconnected.call }
102
+ @client.unbind
103
+ end
104
+
105
+ it 'can register a temporary handler based on stanza ID' do
106
+ stanza = Blather::Stanza::Iq.new
107
+ response = mock()
108
+ response.expects(:call)
109
+ @client.register_tmp_handler(stanza.id) { |_| response.call }
110
+ @client.receive_data stanza
111
+ end
112
+
113
+ it 'removes a tmp handler as soon as it is used' do
114
+ stanza = Blather::Stanza::Iq.new
115
+ response = mock()
116
+ response.expects(:call)
117
+ @client.register_tmp_handler(stanza.id) { |_| response.call }
118
+ @client.receive_data stanza
119
+ @client.receive_data stanza
120
+ end
121
+
122
+ it 'will create a handler then write the stanza' do
123
+ stanza = Blather::Stanza::Iq.new
124
+ response = mock()
125
+ response.expects(:call)
126
+ @client.expects(:write).with do |s|
127
+ @client.receive_data stanza
128
+ s.must_equal stanza
129
+ end
130
+ @client.write_with_handler(stanza) { |_| response.call }
131
+ end
132
+
133
+ it 'can register a handler' do
134
+ stanza = Blather::Stanza::Iq.new
135
+ response = mock()
136
+ response.expects(:call).times(2)
137
+ @client.register_handler(:iq) { |_| response.call }
138
+ @client.receive_data stanza
139
+ @client.receive_data stanza
140
+ end
141
+
142
+ it 'allows for breaking out of handlers' do
143
+ stanza = Blather::Stanza::Iq.new
144
+ response = mock(:iq => nil)
145
+ @client.register_handler(:iq) do |_|
146
+ response.iq
147
+ throw :halt
148
+ response.fail
149
+ end
150
+ @client.receive_data stanza
151
+ end
152
+
153
+ it 'allows for passing to the next handler of the same type' do
154
+ stanza = Blather::Stanza::Iq.new
155
+ response = mock(:iq1 => nil, :iq2 => nil)
156
+ @client.register_handler(:iq) do |_|
157
+ response.iq1
158
+ throw :pass
159
+ response.fail
160
+ end
161
+ @client.register_handler(:iq) do |_|
162
+ response.iq2
163
+ end
164
+ @client.receive_data stanza
165
+ end
166
+
167
+ it 'allows for passing to the next handler in the hierarchy' do
168
+ stanza = Blather::Stanza::Iq::Query.new
169
+ response = mock(:query => nil, :iq => nil)
170
+ @client.register_handler(:query) do |_|
171
+ response.query
172
+ throw :pass
173
+ response.fail
174
+ end
175
+ @client.register_handler(:iq) { |_| response.iq }
176
+ @client.receive_data stanza
177
+ end
178
+ end
179
+
180
+ describe 'Blather::Client#write' do
181
+ before do
182
+ @client = Blather::Client.new
183
+ end
184
+
185
+ it 'writes to the stream' do
186
+ stanza = Blather::Stanza::Iq.new
187
+ stream = mock()
188
+ stream.expects(:send).with stanza
189
+ @client.setup('me@me.com', 'me')
190
+ @client.post_init stream, Blather::JID.new('me.com')
191
+ @client.write stanza
192
+ end
193
+ end
194
+
195
+ describe 'Blather::Client#status=' do
196
+ before do
197
+ @client = Blather::Client.new
198
+ @stream = mock()
199
+ @stream.stubs(:send)
200
+ @client.post_init @stream, Blather::JID.new('n@d/r')
201
+ end
202
+
203
+ it 'updates the state when not sending to a Blather::JID' do
204
+ @stream.stubs(:write)
205
+ @client.status.wont_equal :away
206
+ @client.status = :away, 'message'
207
+ @client.status.must_equal :away
208
+ end
209
+
210
+ it 'does not update the state when sending to a Blather::JID' do
211
+ @stream.stubs(:write)
212
+ @client.status.wont_equal :away
213
+ @client.status = :away, 'message', 'me@me.com'
214
+ @client.status.wont_equal :away
215
+ end
216
+
217
+ it 'writes the new status to the stream' do
218
+ Blather::Stanza::Presence::Status.stubs(:next_id).returns 0
219
+ status = [:away, 'message']
220
+ @stream.expects(:send).with do |s|
221
+ s.must_be_kind_of Blather::Stanza::Presence::Status
222
+ s.to_s.must_equal Blather::Stanza::Presence::Status.new(*status).to_s
223
+ end
224
+ @client.status = status
225
+ end
226
+ end
227
+
228
+ describe 'Blather::Client default handlers' do
229
+ before do
230
+ @client = Blather::Client.new
231
+ @stream = mock()
232
+ @stream.stubs(:send)
233
+ @client.post_init @stream, Blather::JID.new('n@d/r')
234
+ end
235
+
236
+ it 're-raises errors' do
237
+ err = Blather::BlatherError.new
238
+ lambda { @client.receive_data err }.must_raise Blather::BlatherError
239
+ end
240
+
241
+ # it 'responds to iq:get with a "service-unavailable" error' do
242
+ # get = Blather::Stanza::Iq.new :get
243
+ # err = Blather::StanzaError.new(get, 'service-unavailable', :cancel).to_node
244
+ # @client.expects(:write).with err
245
+ # @client.receive_data get
246
+ # end
247
+
248
+ # it 'responds to iq:get with a "service-unavailable" error' do
249
+ # get = Blather::Stanza::Iq.new :get
250
+ # err = Blather::StanzaError.new(get, 'service-unavailable', :cancel).to_node
251
+ # @client.expects(:write).with { |n| n.to_s.must_equal err.to_s }
252
+ # @client.receive_data get
253
+ # end
254
+
255
+ # it 'responds to iq:set with a "service-unavailable" error' do
256
+ # get = Blather::Stanza::Iq.new :set
257
+ # err = Blather::StanzaError.new(get, 'service-unavailable', :cancel).to_node
258
+ # @client.expects(:write).with { |n| n.to_s.must_equal err.to_s }
259
+ # @client.receive_data get
260
+ # end
261
+
262
+ it 'handles status changes by updating the roster if the status is from a Blather::JID in the roster' do
263
+ jid = 'friend@jabber.local'
264
+ status = Blather::Stanza::Presence::Status.new :away
265
+ status.stubs(:from).returns jid
266
+ roster_item = mock()
267
+ roster_item.expects(:status=).with status
268
+ @client.stubs(:roster).returns({status.from => roster_item})
269
+ @client.receive_data status
270
+ end
271
+
272
+ it 'lets status stanzas fall through to other handlers' do
273
+ jid = 'friend@jabber.local'
274
+ status = Blather::Stanza::Presence::Status.new :away
275
+ status.stubs(:from).returns jid
276
+ roster_item = mock()
277
+ roster_item.expects(:status=).with status
278
+ @client.stubs(:roster).returns({status.from => roster_item})
279
+
280
+ response = mock()
281
+ response.expects(:call).with jid
282
+ @client.register_handler(:status) { |s| response.call s.from.to_s }
283
+ @client.receive_data status
284
+ end
285
+
286
+ it 'handles an incoming roster node by processing it through the roster' do
287
+ roster = Blather::Stanza::Iq::Roster.new
288
+ client_roster = mock()
289
+ client_roster.expects(:process).with roster
290
+ @client.stubs(:roster).returns client_roster
291
+ @client.receive_data roster
292
+ end
293
+
294
+ it 'handles an incoming roster node by processing it through the roster' do
295
+ roster = Blather::Stanza::Iq::Roster.new
296
+ client_roster = mock()
297
+ client_roster.expects(:process).with roster
298
+ @client.stubs(:roster).returns client_roster
299
+
300
+ response = mock()
301
+ response.expects(:call)
302
+ @client.register_handler(:roster) { |_| response.call }
303
+ @client.receive_data roster
304
+ end
305
+ end
306
+
307
+ describe 'Blather::Client with a Component stream' do
308
+ before do
309
+ class MockComponent < Blather::Stream::Component; def initialize(); end; end
310
+ @stream = MockComponent.new('')
311
+ @stream.stubs(:send_data)
312
+ @client = Blather::Client.new
313
+ @client.setup('me.com', 'secret')
314
+ end
315
+
316
+ it 'calls the ready handler when sent post_init' do
317
+ ready = mock()
318
+ ready.expects(:call)
319
+ @client.register_handler(:ready) { ready.call }
320
+ @client.post_init @stream
321
+ end
322
+ end
323
+
324
+ describe 'Blather::Client with a Client stream' do
325
+ before do
326
+ class MockClientStream < Blather::Stream::Client; def initialize(); end; end
327
+ @stream = MockClientStream.new('')
328
+ @client = Blather::Client.new
329
+ Blather::Stream::Client.stubs(:start).returns @stream
330
+ @client.setup('me@me.com', 'secret').run
331
+ end
332
+
333
+ it 'sends a request for the roster when post_init is called' do
334
+ @stream.expects(:send).with { |stanza| stanza.must_be_kind_of Blather::Stanza::Iq::Roster }
335
+ @client.post_init @stream, Blather::JID.new('n@d/r')
336
+ end
337
+
338
+ it 'calls the ready handler after post_init and roster is received' do
339
+ result_roster = Blather::Stanza::Iq::Roster.new :result
340
+ @stream.stubs(:send).with { |s| result_roster.id = s.id; @client.receive_data result_roster; true }
341
+
342
+ ready = mock()
343
+ ready.expects(:call)
344
+ @client.register_handler(:ready) { ready.call }
345
+ @client.post_init @stream, Blather::JID.new('n@d/r')
346
+ end
347
+ end
348
+
349
+ describe 'Blather::Client filters' do
350
+ before do
351
+ @client = Blather::Client.new
352
+ @stream = mock()
353
+ @stream.stubs(:send)
354
+ @client.post_init @stream, Blather::JID.new('n@d/r')
355
+ end
356
+
357
+ it 'raises an error when an invalid filter type is registered' do
358
+ lambda { @client.register_filter(:invalid) {} }.must_raise RuntimeError
359
+ end
360
+
361
+ it 'can be guarded' do
362
+ stanza = Blather::Stanza::Iq.new
363
+ ready = mock()
364
+ ready.expects(:call).once
365
+ @client.register_filter(:before, :iq, :id => stanza.id) { |_| ready.call }
366
+ @client.register_filter(:before, :iq, :id => 'not-id') { |_| ready.call }
367
+ @client.receive_data stanza
368
+ end
369
+
370
+ it 'can pass to the next handler' do
371
+ stanza = Blather::Stanza::Iq.new
372
+ ready = mock()
373
+ ready.expects(:call).once
374
+ @client.register_filter(:before) { |_| throw :pass; ready.call }
375
+ @client.register_filter(:before) { |_| ready.call }
376
+ @client.receive_data stanza
377
+ end
378
+
379
+ it 'runs them in order' do
380
+ stanza = Blather::Stanza::Iq.new
381
+ count = 0
382
+ @client.register_filter(:before) { |_| count.must_equal 0; count = 1 }
383
+ @client.register_filter(:before) { |_| count.must_equal 1; count = 2 }
384
+ @client.register_handler(:iq) { |_| count.must_equal 2; count = 3 }
385
+ @client.register_filter(:after) { |_| count.must_equal 3; count = 4 }
386
+ @client.register_filter(:after) { |_| count.must_equal 4 }
387
+ @client.receive_data stanza
388
+ end
389
+
390
+ it 'can modify the stanza' do
391
+ stanza = Blather::Stanza::Iq.new
392
+ stanza.from = 'from@test.local'
393
+ new_jid = 'before@filter.local'
394
+ ready = mock()
395
+ ready.expects(:call).with new_jid
396
+ @client.register_filter(:before) { |s| s.from = new_jid }
397
+ @client.register_handler(:iq) { |s| ready.call s.from.to_s }
398
+ @client.receive_data stanza
399
+ end
400
+
401
+ it 'can halt the handler chain' do
402
+ stanza = Blather::Stanza::Iq.new
403
+ ready = mock()
404
+ ready.expects(:call).never
405
+ @client.register_filter(:before) { |_| throw :halt }
406
+ @client.register_handler(:iq) { |_| ready.call }
407
+ @client.receive_data stanza
408
+ end
409
+
410
+ it 'can be specific to a handler' do
411
+ stanza = Blather::Stanza::Iq.new
412
+ ready = mock()
413
+ ready.expects(:call).once
414
+ @client.register_filter(:before, :iq) { |_| ready.call }
415
+ @client.register_filter(:before, :message) { |_| ready.call }
416
+ @client.receive_data stanza
417
+ end
418
+ end
419
+
420
+ describe 'Blather::Client guards' do
421
+ before do
422
+ stream = mock()
423
+ stream.stubs(:send)
424
+ @client = Blather::Client.new
425
+ @client.post_init stream, Blather::JID.new('n@d/r')
426
+ @stanza = Blather::Stanza::Iq.new
427
+ @response = mock()
428
+ end
429
+
430
+ it 'can be a symbol' do
431
+ @response.expects :call
432
+ @client.register_handler(:iq, :chat?) { |_| @response.call }
433
+
434
+ @stanza.expects(:chat?).returns true
435
+ @client.receive_data @stanza
436
+
437
+ @stanza.expects(:chat?).returns false
438
+ @client.receive_data @stanza
439
+ end
440
+
441
+ it 'can be a hash with string match' do
442
+ @response.expects :call
443
+ @client.register_handler(:iq, :body => 'exit') { |_| @response.call }
444
+
445
+ @stanza.expects(:body).returns 'exit'
446
+ @client.receive_data @stanza
447
+
448
+ @stanza.expects(:body).returns 'not-exit'
449
+ @client.receive_data @stanza
450
+ end
451
+
452
+ it 'can be a hash with a value' do
453
+ @response.expects :call
454
+ @client.register_handler(:iq, :number => 0) { |_| @response.call }
455
+
456
+ @stanza.expects(:number).returns 0
457
+ @client.receive_data @stanza
458
+
459
+ @stanza.expects(:number).returns 1
460
+ @client.receive_data @stanza
461
+ end
462
+
463
+ it 'can be a hash with a regexp' do
464
+ @response.expects :call
465
+ @client.register_handler(:iq, :body => /exit/) { |_| @response.call }
466
+
467
+ @stanza.expects(:body).returns 'more than just exit, but exit still'
468
+ @client.receive_data @stanza
469
+
470
+ @stanza.expects(:body).returns 'keyword not found'
471
+ @client.receive_data @stanza
472
+
473
+ @stanza.expects(:body).returns nil
474
+ @client.receive_data @stanza
475
+ end
476
+
477
+ it 'can be a hash with an array' do
478
+ @response.expects(:call).times(2)
479
+ @client.register_handler(:iq, :type => [:result, :error]) { |_| @response.call }
480
+
481
+ stanza = Blather::Stanza::Iq.new
482
+ stanza.expects(:type).at_least_once.returns :result
483
+ @client.receive_data stanza
484
+
485
+ stanza = Blather::Stanza::Iq.new
486
+ stanza.expects(:type).at_least_once.returns :error
487
+ @client.receive_data stanza
488
+
489
+ stanza = Blather::Stanza::Iq.new
490
+ stanza.expects(:type).at_least_once.returns :get
491
+ @client.receive_data stanza
492
+ end
493
+
494
+ it 'chained are treated like andand (short circuited)' do
495
+ @response.expects :call
496
+ @client.register_handler(:iq, :type => :get, :body => 'test') { |_| @response.call }
497
+
498
+ stanza = Blather::Stanza::Iq.new
499
+ stanza.expects(:type).at_least_once.returns :get
500
+ stanza.expects(:body).returns 'test'
501
+ @client.receive_data stanza
502
+
503
+ stanza = Blather::Stanza::Iq.new
504
+ stanza.expects(:type).at_least_once.returns :set
505
+ stanza.expects(:body).never
506
+ @client.receive_data stanza
507
+ end
508
+
509
+ it 'within an Array are treated as oror (short circuited)' do
510
+ @response.expects(:call).times 2
511
+ @client.register_handler(:iq, [{:type => :get}, {:body => 'test'}]) { |_| @response.call }
512
+
513
+ stanza = Blather::Stanza::Iq.new
514
+ stanza.expects(:type).at_least_once.returns :set
515
+ stanza.expects(:body).returns 'test'
516
+ @client.receive_data stanza
517
+
518
+ stanza = Blather::Stanza::Iq.new
519
+ stanza.stubs(:type).at_least_once.returns :get
520
+ stanza.expects(:body).never
521
+ @client.receive_data stanza
522
+ end
523
+
524
+ it 'can be a lambda' do
525
+ @response.expects :call
526
+ @client.register_handler(:iq, lambda { |s| s.number % 3 == 0 }) { |_| @response.call }
527
+
528
+ @stanza.expects(:number).at_least_once.returns 3
529
+ @client.receive_data @stanza
530
+
531
+ @stanza.expects(:number).at_least_once.returns 2
532
+ @client.receive_data @stanza
533
+ end
534
+
535
+ it 'can be an xpath and will send the result to the handler' do
536
+ @response.expects(:call).with do |stanza, xpath|
537
+ xpath.must_be_instance_of Nokogiri::XML::NodeSet
538
+ xpath.wont_be_empty
539
+ stanza.must_equal @stanza
540
+ end
541
+ @client.register_handler(:iq, "/iq[@id='#{@stanza.id}']") { |stanza, xpath| @response.call stanza, xpath }
542
+ @client.receive_data @stanza
543
+ end
544
+
545
+ it 'can be an xpath with namespaces and will send the result to the handler' do
546
+ @stanza = Blather::Stanza.import(parse_stanza('<message><foo xmlns="http://bar.com"></message>').root)
547
+ @response.expects(:call).with do |stanza, xpath|
548
+ xpath.must_be_instance_of Nokogiri::XML::NodeSet
549
+ xpath.wont_be_empty
550
+ stanza.must_equal @stanza
551
+ end
552
+ @client.register_handler(:message, "/message/bar:foo", :bar => 'http://bar.com') { |stanza, xpath| @response.call stanza, xpath }
553
+ @client.receive_data @stanza
554
+ end
555
+
556
+ it 'raises an error when a bad guard is tried' do
557
+ lambda { @client.register_handler(:iq, 0) {} }.must_raise RuntimeError
558
+ end
559
+ end