tp-blather 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. data/.autotest +13 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +8 -0
  6. data/CHANGELOG.md +249 -0
  7. data/Gemfile +4 -0
  8. data/Guardfile +5 -0
  9. data/LICENSE +22 -0
  10. data/README.md +413 -0
  11. data/Rakefile +20 -0
  12. data/TODO.md +2 -0
  13. data/blather.gemspec +51 -0
  14. data/examples/certs/README +20 -0
  15. data/examples/certs/ca-bundle.crt +3987 -0
  16. data/examples/echo.rb +19 -0
  17. data/examples/execute.rb +17 -0
  18. data/examples/ping_pong.rb +38 -0
  19. data/examples/print_hierarchy.rb +77 -0
  20. data/examples/rosterprint.rb +15 -0
  21. data/examples/stream_only.rb +28 -0
  22. data/examples/trusted_echo.rb +21 -0
  23. data/examples/xmpp4r/echo.rb +36 -0
  24. data/lib/blather.rb +112 -0
  25. data/lib/blather/cert_store.rb +53 -0
  26. data/lib/blather/client.rb +95 -0
  27. data/lib/blather/client/client.rb +345 -0
  28. data/lib/blather/client/dsl.rb +320 -0
  29. data/lib/blather/client/dsl/pubsub.rb +174 -0
  30. data/lib/blather/core_ext/eventmachine.rb +125 -0
  31. data/lib/blather/core_ext/ipaddr.rb +20 -0
  32. data/lib/blather/errors.rb +69 -0
  33. data/lib/blather/errors/sasl_error.rb +44 -0
  34. data/lib/blather/errors/stanza_error.rb +110 -0
  35. data/lib/blather/errors/stream_error.rb +84 -0
  36. data/lib/blather/file_transfer.rb +107 -0
  37. data/lib/blather/file_transfer/ibb.rb +68 -0
  38. data/lib/blather/file_transfer/s5b.rb +114 -0
  39. data/lib/blather/jid.rb +141 -0
  40. data/lib/blather/roster.rb +118 -0
  41. data/lib/blather/roster_item.rb +146 -0
  42. data/lib/blather/stanza.rb +167 -0
  43. data/lib/blather/stanza/disco.rb +32 -0
  44. data/lib/blather/stanza/disco/capabilities.rb +161 -0
  45. data/lib/blather/stanza/disco/disco_info.rb +205 -0
  46. data/lib/blather/stanza/disco/disco_items.rb +134 -0
  47. data/lib/blather/stanza/iq.rb +144 -0
  48. data/lib/blather/stanza/iq/command.rb +339 -0
  49. data/lib/blather/stanza/iq/ibb.rb +86 -0
  50. data/lib/blather/stanza/iq/ping.rb +50 -0
  51. data/lib/blather/stanza/iq/query.rb +53 -0
  52. data/lib/blather/stanza/iq/roster.rb +185 -0
  53. data/lib/blather/stanza/iq/s5b.rb +208 -0
  54. data/lib/blather/stanza/iq/si.rb +415 -0
  55. data/lib/blather/stanza/iq/vcard.rb +149 -0
  56. data/lib/blather/stanza/message.rb +428 -0
  57. data/lib/blather/stanza/message/muc_user.rb +119 -0
  58. data/lib/blather/stanza/muc/muc_user_base.rb +54 -0
  59. data/lib/blather/stanza/presence.rb +172 -0
  60. data/lib/blather/stanza/presence/c.rb +100 -0
  61. data/lib/blather/stanza/presence/muc.rb +35 -0
  62. data/lib/blather/stanza/presence/muc_user.rb +147 -0
  63. data/lib/blather/stanza/presence/status.rb +218 -0
  64. data/lib/blather/stanza/presence/subscription.rb +100 -0
  65. data/lib/blather/stanza/pubsub.rb +119 -0
  66. data/lib/blather/stanza/pubsub/affiliations.rb +79 -0
  67. data/lib/blather/stanza/pubsub/create.rb +65 -0
  68. data/lib/blather/stanza/pubsub/errors.rb +18 -0
  69. data/lib/blather/stanza/pubsub/event.rb +139 -0
  70. data/lib/blather/stanza/pubsub/items.rb +103 -0
  71. data/lib/blather/stanza/pubsub/publish.rb +103 -0
  72. data/lib/blather/stanza/pubsub/retract.rb +92 -0
  73. data/lib/blather/stanza/pubsub/subscribe.rb +68 -0
  74. data/lib/blather/stanza/pubsub/subscription.rb +135 -0
  75. data/lib/blather/stanza/pubsub/subscriptions.rb +83 -0
  76. data/lib/blather/stanza/pubsub/unsubscribe.rb +84 -0
  77. data/lib/blather/stanza/pubsub_owner.rb +51 -0
  78. data/lib/blather/stanza/pubsub_owner/delete.rb +52 -0
  79. data/lib/blather/stanza/pubsub_owner/purge.rb +52 -0
  80. data/lib/blather/stanza/x.rb +416 -0
  81. data/lib/blather/stream.rb +266 -0
  82. data/lib/blather/stream/client.rb +32 -0
  83. data/lib/blather/stream/component.rb +39 -0
  84. data/lib/blather/stream/features.rb +70 -0
  85. data/lib/blather/stream/features/register.rb +38 -0
  86. data/lib/blather/stream/features/resource.rb +63 -0
  87. data/lib/blather/stream/features/sasl.rb +190 -0
  88. data/lib/blather/stream/features/session.rb +45 -0
  89. data/lib/blather/stream/features/tls.rb +29 -0
  90. data/lib/blather/stream/parser.rb +102 -0
  91. data/lib/blather/version.rb +3 -0
  92. data/lib/blather/xmpp_node.rb +94 -0
  93. data/spec/blather/client/client_spec.rb +687 -0
  94. data/spec/blather/client/dsl/pubsub_spec.rb +492 -0
  95. data/spec/blather/client/dsl_spec.rb +266 -0
  96. data/spec/blather/errors/sasl_error_spec.rb +33 -0
  97. data/spec/blather/errors/stanza_error_spec.rb +129 -0
  98. data/spec/blather/errors/stream_error_spec.rb +108 -0
  99. data/spec/blather/errors_spec.rb +33 -0
  100. data/spec/blather/file_transfer_spec.rb +135 -0
  101. data/spec/blather/jid_spec.rb +87 -0
  102. data/spec/blather/roster_item_spec.rb +134 -0
  103. data/spec/blather/roster_spec.rb +107 -0
  104. data/spec/blather/stanza/discos/disco_info_spec.rb +247 -0
  105. data/spec/blather/stanza/discos/disco_items_spec.rb +154 -0
  106. data/spec/blather/stanza/iq/command_spec.rb +206 -0
  107. data/spec/blather/stanza/iq/ibb_spec.rb +124 -0
  108. data/spec/blather/stanza/iq/ping_spec.rb +45 -0
  109. data/spec/blather/stanza/iq/query_spec.rb +64 -0
  110. data/spec/blather/stanza/iq/roster_spec.rb +139 -0
  111. data/spec/blather/stanza/iq/s5b_spec.rb +57 -0
  112. data/spec/blather/stanza/iq/si_spec.rb +98 -0
  113. data/spec/blather/stanza/iq/vcard_spec.rb +93 -0
  114. data/spec/blather/stanza/iq_spec.rb +61 -0
  115. data/spec/blather/stanza/message/muc_user_spec.rb +152 -0
  116. data/spec/blather/stanza/message_spec.rb +282 -0
  117. data/spec/blather/stanza/presence/c_spec.rb +56 -0
  118. data/spec/blather/stanza/presence/muc_spec.rb +37 -0
  119. data/spec/blather/stanza/presence/muc_user_spec.rb +83 -0
  120. data/spec/blather/stanza/presence/status_spec.rb +144 -0
  121. data/spec/blather/stanza/presence/subscription_spec.rb +102 -0
  122. data/spec/blather/stanza/presence_spec.rb +125 -0
  123. data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
  124. data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
  125. data/spec/blather/stanza/pubsub/event_spec.rb +98 -0
  126. data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
  127. data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
  128. data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
  129. data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
  130. data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
  131. data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
  132. data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +74 -0
  133. data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
  134. data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
  135. data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
  136. data/spec/blather/stanza/pubsub_spec.rb +68 -0
  137. data/spec/blather/stanza/x_spec.rb +231 -0
  138. data/spec/blather/stanza_spec.rb +134 -0
  139. data/spec/blather/stream/client_spec.rb +1090 -0
  140. data/spec/blather/stream/component_spec.rb +108 -0
  141. data/spec/blather/stream/parser_spec.rb +152 -0
  142. data/spec/blather/stream/ssl_spec.rb +32 -0
  143. data/spec/blather/xmpp_node_spec.rb +47 -0
  144. data/spec/blather_spec.rb +34 -0
  145. data/spec/fixtures/pubsub.rb +311 -0
  146. data/spec/spec_helper.rb +17 -0
  147. data/yard/templates/default/class/html/handlers.erb +18 -0
  148. data/yard/templates/default/class/setup.rb +10 -0
  149. data/yard/templates/default/class/text/handlers.erb +1 -0
  150. metadata +459 -0
@@ -0,0 +1,687 @@
1
+ require '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
+ end
11
+
12
+ it 'provides a Blather::JID reader' do
13
+ @client.post_init @stream, @jid
14
+ @client.should respond_to :jid
15
+ @client.jid.should == @jid
16
+ end
17
+
18
+ it 'provides a reader for the roster' do
19
+ @client.should respond_to :roster
20
+ @client.roster.should be_kind_of Blather::Roster
21
+ end
22
+
23
+ it 'provides a status reader' do
24
+ @client.post_init @stream, @jid
25
+ @client.should respond_to :status
26
+ @client.status = :away
27
+ @client.status.should == :away
28
+ end
29
+
30
+ it 'should have a caps handler' do
31
+ @client.should respond_to :caps
32
+ @client.caps.should be_kind_of Blather::Stanza::Capabilities
33
+ end
34
+
35
+ it 'can be setup' do
36
+ @client.should respond_to :setup
37
+ @client.setup('me@me.com', 'pass').should == @client
38
+ end
39
+
40
+ it 'knows if it has been setup' do
41
+ @client.should respond_to :setup?
42
+ @client.setup?.should == false
43
+ @client.setup 'me@me.com', 'pass'
44
+ @client.setup?.should == true
45
+ end
46
+
47
+ it 'cannot be run before being setup' do
48
+ lambda { @client.run }.should raise_error RuntimeError
49
+ end
50
+
51
+ it 'starts up a Component connection when setup without a node' do
52
+ setup = 'pubsub.jabber.local', 'secret'
53
+ @client.setup *setup
54
+ Blather::Stream::Component.expects(:start).with @client, *setup + [nil, nil, nil, nil]
55
+ @client.run
56
+ end
57
+
58
+ it 'starts up a Client connection when setup with a node' do
59
+ setup = 'test@jabber.local', 'secret'
60
+ @client.setup *setup
61
+ Blather::Stream::Client.expects(:start).with @client, *setup + [nil, nil, nil, nil]
62
+ @client.run
63
+ end
64
+
65
+ it 'knows if it is disconnected' do
66
+ @client.should respond_to :connected?
67
+ @client.connected?.should == false
68
+ end
69
+
70
+ it 'knows if it is connected' do
71
+ stream = mock
72
+ stream.expects(:stopped?).returns false
73
+ @client.setup('me.com', 'secret')
74
+ @client.post_init stream, Blather::JID.new('me.com')
75
+ @client.connected?.should == true
76
+ end
77
+
78
+ describe 'if it has been setup but not connected yet' do
79
+ it 'should consider itself disconnected' do
80
+ @client.setup('me.com', 'secret')
81
+ @client.connected?.should == false
82
+ end
83
+ end
84
+
85
+ it 'writes to the connection the closes when #close is called' do
86
+ stream = mock()
87
+ stream.expects(:close_connection_after_writing)
88
+ @client.setup('me.com', 'secret')
89
+ @client.post_init stream, Blather::JID.new('me.com')
90
+ @client.close
91
+ end
92
+
93
+ it 'shuts down EM when #unbind is called if it is running' do
94
+ EM.expects(:reactor_running?).returns true
95
+ EM.expects(:stop)
96
+ @client.unbind
97
+ end
98
+
99
+ it 'does nothing when #unbind is called and EM is not running' do
100
+ EM.expects(:reactor_running?).returns false
101
+ EM.expects(:stop).never
102
+ @client.unbind
103
+ end
104
+
105
+ it 'calls the :disconnected handler with #unbind is called' do
106
+ EM.expects(:reactor_running?).returns false
107
+ disconnected = mock()
108
+ disconnected.expects(:call)
109
+ @client.register_handler(:disconnected) { disconnected.call }
110
+ @client.unbind
111
+ end
112
+
113
+ it 'does not call EM.stop on #unbind if a handler returns positive' do
114
+ EM.expects(:reactor_running?).never
115
+ EM.expects(:stop).never
116
+ disconnected = mock()
117
+ disconnected.expects(:call).returns true
118
+ @client.register_handler(:disconnected) { disconnected.call }
119
+ @client.unbind
120
+ end
121
+
122
+ it 'calls EM.stop on #unbind if a handler returns negative' do
123
+ EM.expects(:reactor_running?).returns true
124
+ EM.expects(:stop)
125
+ disconnected = mock()
126
+ disconnected.expects(:call).returns false
127
+ @client.register_handler(:disconnected) { disconnected.call }
128
+ @client.unbind
129
+ end
130
+
131
+ it 'can register a temporary handler based on stanza ID' do
132
+ stanza = Blather::Stanza::Iq.new
133
+ response = mock()
134
+ response.expects(:call)
135
+ @client.register_tmp_handler(stanza.id) { |_| response.call }
136
+ @client.receive_data stanza
137
+ end
138
+
139
+ it 'removes a tmp handler as soon as it is used' do
140
+ stanza = Blather::Stanza::Iq.new
141
+ response = mock()
142
+ response.expects(:call)
143
+ @client.register_tmp_handler(stanza.id) { |_| response.call }
144
+ @client.receive_data stanza
145
+ @client.receive_data stanza
146
+ end
147
+
148
+ it 'will create a handler then write the stanza' do
149
+ stanza = Blather::Stanza::Iq.new
150
+ response = mock()
151
+ response.expects(:call)
152
+ @client.expects(:write).with do |s|
153
+ @client.receive_data stanza
154
+ s.should == stanza
155
+ end
156
+ @client.write_with_handler(stanza) { |_| response.call }
157
+ end
158
+
159
+ it 'can register a handler' do
160
+ stanza = Blather::Stanza::Iq.new
161
+ response = mock()
162
+ response.expects(:call).times(2)
163
+ @client.register_handler(:iq) { |_| response.call }
164
+ @client.receive_data stanza
165
+ @client.receive_data stanza
166
+ end
167
+
168
+ it 'allows for breaking out of handlers' do
169
+ stanza = Blather::Stanza::Iq.new
170
+ response = mock(:iq => nil)
171
+ @client.register_handler(:iq) do |_|
172
+ response.iq
173
+ throw :halt
174
+ response.fail
175
+ end
176
+ @client.receive_data stanza
177
+ end
178
+
179
+ it 'allows for passing to the next handler of the same type' do
180
+ stanza = Blather::Stanza::Iq.new
181
+ response = mock(:iq1 => nil, :iq2 => nil)
182
+ @client.register_handler(:iq) do |_|
183
+ response.iq1
184
+ throw :pass
185
+ response.fail
186
+ end
187
+ @client.register_handler(:iq) do |_|
188
+ response.iq2
189
+ end
190
+ @client.receive_data stanza
191
+ end
192
+
193
+ it 'allows for passing to the next handler in the hierarchy' do
194
+ stanza = Blather::Stanza::Iq::Query.new
195
+ response = mock(:query => nil, :iq => nil)
196
+ @client.register_handler(:query) do |_|
197
+ response.query
198
+ throw :pass
199
+ response.fail
200
+ end
201
+ @client.register_handler(:iq) { |_| response.iq }
202
+ @client.receive_data stanza
203
+ end
204
+
205
+ it 'can clear handlers' do
206
+ stanza = Blather::Stanza::Message.new
207
+ stanza.expects(:chat?).returns true
208
+
209
+ response = mock
210
+ response.expects(:call).once
211
+
212
+ @client.register_handler(:message, :chat?) { |_| response.call }
213
+ @client.receive_data stanza
214
+
215
+ @client.clear_handlers(:message, :chat?)
216
+ @client.receive_data stanza
217
+ end
218
+ end
219
+
220
+ describe 'Blather::Client#write' do
221
+ before do
222
+ @client = Blather::Client.new
223
+ end
224
+
225
+ it 'writes to the stream' do
226
+ stanza = Blather::Stanza::Iq.new
227
+ stream = mock()
228
+ stream.expects(:send).with stanza
229
+ @client.setup('me@me.com', 'me')
230
+ @client.post_init stream, Blather::JID.new('me.com')
231
+ @client.write stanza
232
+ end
233
+ end
234
+
235
+ describe 'Blather::Client#status=' do
236
+ before do
237
+ @client = Blather::Client.new
238
+ @stream = mock()
239
+ @stream.stubs(:send)
240
+ @client.post_init @stream, Blather::JID.new('n@d/r')
241
+ end
242
+
243
+ it 'updates the state when not sending to a Blather::JID' do
244
+ @stream.stubs(:write)
245
+ @client.status.should_not equal :away
246
+ @client.status = :away, 'message'
247
+ @client.status.should == :away
248
+ end
249
+
250
+ it 'does not update the state when sending to a Blather::JID' do
251
+ @stream.stubs(:write)
252
+ @client.status.should_not equal :away
253
+ @client.status = :away, 'message', 'me@me.com'
254
+ @client.status.should_not equal :away
255
+ end
256
+
257
+ it 'writes the new status to the stream' do
258
+ Blather::Stanza::Presence::Status.stubs(:next_id).returns 0
259
+ status = [:away, 'message']
260
+ @stream.expects(:send).with do |s|
261
+ s.should be_kind_of Blather::Stanza::Presence::Status
262
+ s.to_s.should == Blather::Stanza::Presence::Status.new(*status).to_s
263
+ end
264
+ @client.status = status
265
+ end
266
+ end
267
+
268
+ describe 'Blather::Client default handlers' do
269
+ before do
270
+ @client = Blather::Client.new
271
+ @stream = mock()
272
+ @stream.stubs(:send)
273
+ @client.post_init @stream, Blather::JID.new('n@d/r')
274
+ end
275
+
276
+ it 're-raises errors' do
277
+ err = Blather::BlatherError.new
278
+ lambda { @client.receive_data err }.should raise_error Blather::BlatherError
279
+ end
280
+
281
+ # it 'responds to iq:get with a "service-unavailable" error' do
282
+ # get = Blather::Stanza::Iq.new :get
283
+ # err = Blather::StanzaError.new(get, 'service-unavailable', :cancel).to_node
284
+ # @client.expects(:write).with err
285
+ # @client.receive_data get
286
+ # end
287
+
288
+ # it 'responds to iq:get with a "service-unavailable" error' do
289
+ # get = Blather::Stanza::Iq.new :get
290
+ # err = Blather::StanzaError.new(get, 'service-unavailable', :cancel).to_node
291
+ # @client.expects(:write).with { |n| n.to_s.should == err.to_s }
292
+ # @client.receive_data get
293
+ # end
294
+
295
+ # it 'responds to iq:set with a "service-unavailable" error' do
296
+ # get = Blather::Stanza::Iq.new :set
297
+ # err = Blather::StanzaError.new(get, 'service-unavailable', :cancel).to_node
298
+ # @client.expects(:write).with { |n| n.to_s.should == err.to_s }
299
+ # @client.receive_data get
300
+ # end
301
+
302
+ it 'responds to s2c pings with a pong' do
303
+ ping = Blather::Stanza::Iq::Ping.new :get
304
+ pong = ping.reply
305
+ @client.expects(:write).with { |n| n.to_s.should == pong.to_s }
306
+ @client.receive_data ping
307
+ end
308
+
309
+ it 'handles status changes by updating the roster if the status is from a Blather::JID in the roster' do
310
+ jid = 'friend@jabber.local'
311
+ status = Blather::Stanza::Presence::Status.new :away
312
+ status.stubs(:from).returns jid
313
+ roster_item = mock()
314
+ roster_item.expects(:status=).with status
315
+ @client.stubs(:roster).returns({status.from => roster_item})
316
+ @client.receive_data status
317
+ end
318
+
319
+ it 'lets status stanzas fall through to other handlers' do
320
+ jid = 'friend@jabber.local'
321
+ status = Blather::Stanza::Presence::Status.new :away
322
+ status.stubs(:from).returns jid
323
+ roster_item = mock()
324
+ roster_item.expects(:status=).with status
325
+ @client.stubs(:roster).returns({status.from => roster_item})
326
+
327
+ response = mock()
328
+ response.expects(:call).with jid
329
+ @client.register_handler(:status) { |s| response.call s.from.to_s }
330
+ @client.receive_data status
331
+ end
332
+
333
+ it 'handles an incoming roster node by processing it through the roster' do
334
+ roster = Blather::Stanza::Iq::Roster.new
335
+ client_roster = mock()
336
+ client_roster.expects(:process).with roster
337
+ @client.stubs(:roster).returns client_roster
338
+ @client.receive_data roster
339
+ end
340
+
341
+ it 'handles an incoming roster node by processing it through the roster' do
342
+ roster = Blather::Stanza::Iq::Roster.new
343
+ client_roster = mock()
344
+ client_roster.expects(:process).with roster
345
+ @client.stubs(:roster).returns client_roster
346
+
347
+ response = mock()
348
+ response.expects(:call)
349
+ @client.register_handler(:roster) { |_| response.call }
350
+ @client.receive_data roster
351
+ end
352
+ end
353
+
354
+ describe 'Blather::Client with a Component stream' do
355
+ before do
356
+ class MockComponent < Blather::Stream::Component; def initialize(); end; end
357
+ @stream = MockComponent.new('')
358
+ @stream.stubs(:send_data)
359
+ @client = Blather::Client.new
360
+ @client.setup('me.com', 'secret')
361
+ end
362
+
363
+ it 'calls the ready handler when sent post_init' do
364
+ ready = mock()
365
+ ready.expects(:call)
366
+ @client.register_handler(:ready) { ready.call }
367
+ @client.post_init @stream
368
+ end
369
+ end
370
+
371
+ describe 'Blather::Client with a Client stream' do
372
+ before do
373
+ class MockClientStream < Blather::Stream::Client; def initialize(); end; end
374
+ @stream = MockClientStream.new('')
375
+ @client = Blather::Client.new
376
+ Blather::Stream::Client.stubs(:start).returns @stream
377
+ @client.setup('me@me.com', 'secret').run
378
+ end
379
+
380
+ it 'sends a request for the roster when post_init is called' do
381
+ @stream.expects(:send).with { |stanza| stanza.should be_kind_of Blather::Stanza::Iq::Roster }
382
+ @client.post_init @stream, Blather::JID.new('n@d/r')
383
+ end
384
+
385
+ it 'calls the ready handler after post_init and roster is received' do
386
+ result_roster = Blather::Stanza::Iq::Roster.new :result
387
+ @stream.stubs(:send).with { |s| result_roster.id = s.id; @client.receive_data result_roster; true }
388
+
389
+ ready = mock()
390
+ ready.expects(:call)
391
+ @client.register_handler(:ready) { ready.call }
392
+ @client.post_init @stream, Blather::JID.new('n@d/r')
393
+ end
394
+ end
395
+
396
+ describe 'Blather::Client filters' do
397
+ before do
398
+ @client = Blather::Client.new
399
+ @stream = mock()
400
+ @stream.stubs(:send)
401
+ @client.post_init @stream, Blather::JID.new('n@d/r')
402
+ end
403
+
404
+ it 'raises an error when an invalid filter type is registered' do
405
+ lambda { @client.register_filter(:invalid) {} }.should raise_error RuntimeError
406
+ end
407
+
408
+ it 'can be guarded' do
409
+ stanza = Blather::Stanza::Iq.new
410
+ ready = mock()
411
+ ready.expects(:call).once
412
+ @client.register_filter(:before, :iq, :id => stanza.id) { |_| ready.call }
413
+ @client.register_filter(:before, :iq, :id => 'not-id') { |_| ready.call }
414
+ @client.receive_data stanza
415
+ end
416
+
417
+ it 'can pass to the next handler' do
418
+ stanza = Blather::Stanza::Iq.new
419
+ ready = mock()
420
+ ready.expects(:call).once
421
+ @client.register_filter(:before) { |_| throw :pass; ready.call }
422
+ @client.register_filter(:before) { |_| ready.call }
423
+ @client.receive_data stanza
424
+ end
425
+
426
+ it 'runs them in order' do
427
+ stanza = Blather::Stanza::Iq.new
428
+ count = 0
429
+ @client.register_filter(:before) { |_| count.should == 0; count = 1 }
430
+ @client.register_filter(:before) { |_| count.should == 1; count = 2 }
431
+ @client.register_handler(:iq) { |_| count.should == 2; count = 3 }
432
+ @client.register_filter(:after) { |_| count.should == 3; count = 4 }
433
+ @client.register_filter(:after) { |_| count.should == 4 }
434
+ @client.receive_data stanza
435
+ end
436
+
437
+ it 'can modify the stanza' do
438
+ stanza = Blather::Stanza::Iq.new
439
+ stanza.from = 'from@test.local'
440
+ new_jid = 'before@filter.local'
441
+ ready = mock()
442
+ ready.expects(:call).with new_jid
443
+ @client.register_filter(:before) { |s| s.from = new_jid }
444
+ @client.register_handler(:iq) { |s| ready.call s.from.to_s }
445
+ @client.receive_data stanza
446
+ end
447
+
448
+ it 'can halt the handler chain' do
449
+ stanza = Blather::Stanza::Iq.new
450
+ ready = mock()
451
+ ready.expects(:call).never
452
+ @client.register_filter(:before) { |_| throw :halt }
453
+ @client.register_handler(:iq) { |_| ready.call }
454
+ @client.receive_data stanza
455
+ end
456
+
457
+ it 'can be specific to a handler' do
458
+ stanza = Blather::Stanza::Iq.new
459
+ ready = mock()
460
+ ready.expects(:call).once
461
+ @client.register_filter(:before, :iq) { |_| ready.call }
462
+ @client.register_filter(:before, :message) { |_| ready.call }
463
+ @client.receive_data stanza
464
+ end
465
+ end
466
+
467
+ describe 'Blather::Client guards' do
468
+ before do
469
+ stream = mock()
470
+ stream.stubs(:send)
471
+ @client = Blather::Client.new
472
+ @client.post_init stream, Blather::JID.new('n@d/r')
473
+ @stanza = Blather::Stanza::Iq.new
474
+ @response = mock()
475
+ end
476
+
477
+ it 'can be a symbol' do
478
+ @response.expects :call
479
+ @client.register_handler(:iq, :chat?) { |_| @response.call }
480
+
481
+ @stanza.expects(:chat?).returns true
482
+ @client.receive_data @stanza
483
+
484
+ @stanza.expects(:chat?).returns false
485
+ @client.receive_data @stanza
486
+ end
487
+
488
+ it 'can be a hash with string match' do
489
+ @response.expects :call
490
+ @client.register_handler(:iq, :body => 'exit') { |_| @response.call }
491
+
492
+ @stanza.expects(:body).returns 'exit'
493
+ @client.receive_data @stanza
494
+
495
+ @stanza.expects(:body).returns 'not-exit'
496
+ @client.receive_data @stanza
497
+ end
498
+
499
+ it 'can be a hash with a value' do
500
+ @response.expects :call
501
+ @client.register_handler(:iq, :number => 0) { |_| @response.call }
502
+
503
+ @stanza.expects(:number).returns 0
504
+ @client.receive_data @stanza
505
+
506
+ @stanza.expects(:number).returns 1
507
+ @client.receive_data @stanza
508
+ end
509
+
510
+ it 'can be a hash with a regexp' do
511
+ @response.expects :call
512
+ @client.register_handler(:iq, :body => /exit/) { |_| @response.call }
513
+
514
+ @stanza.expects(:body).returns 'more than just exit, but exit still'
515
+ @client.receive_data @stanza
516
+
517
+ @stanza.expects(:body).returns 'keyword not found'
518
+ @client.receive_data @stanza
519
+
520
+ @stanza.expects(:body).returns nil
521
+ @client.receive_data @stanza
522
+ end
523
+
524
+ it 'can be a hash with an array' do
525
+ @response.expects(:call).times(2)
526
+ @client.register_handler(:iq, :type => [:result, :error]) { |_| @response.call }
527
+
528
+ stanza = Blather::Stanza::Iq.new
529
+ stanza.expects(:type).at_least_once.returns :result
530
+ @client.receive_data stanza
531
+
532
+ stanza = Blather::Stanza::Iq.new
533
+ stanza.expects(:type).at_least_once.returns :error
534
+ @client.receive_data stanza
535
+
536
+ stanza = Blather::Stanza::Iq.new
537
+ stanza.expects(:type).at_least_once.returns :get
538
+ @client.receive_data stanza
539
+ end
540
+
541
+ it 'chained are treated like andand (short circuited)' do
542
+ @response.expects :call
543
+ @client.register_handler(:iq, :type => :get, :body => 'test') { |_| @response.call }
544
+
545
+ stanza = Blather::Stanza::Iq.new
546
+ stanza.expects(:type).at_least_once.returns :get
547
+ stanza.expects(:body).returns 'test'
548
+ @client.receive_data stanza
549
+
550
+ stanza = Blather::Stanza::Iq.new
551
+ stanza.expects(:type).at_least_once.returns :set
552
+ stanza.expects(:body).never
553
+ @client.receive_data stanza
554
+ end
555
+
556
+ it 'within an Array are treated as oror (short circuited)' do
557
+ @response.expects(:call).times 2
558
+ @client.register_handler(:iq, [{:type => :get}, {:body => 'test'}]) { |_| @response.call }
559
+
560
+ stanza = Blather::Stanza::Iq.new
561
+ stanza.expects(:type).at_least_once.returns :set
562
+ stanza.expects(:body).returns 'test'
563
+ @client.receive_data stanza
564
+
565
+ stanza = Blather::Stanza::Iq.new
566
+ stanza.stubs(:type).at_least_once.returns :get
567
+ stanza.expects(:body).never
568
+ @client.receive_data stanza
569
+ end
570
+
571
+ it 'can be a lambda' do
572
+ @response.expects :call
573
+ @client.register_handler(:iq, lambda { |s| s.number % 3 == 0 }) { |_| @response.call }
574
+
575
+ @stanza.expects(:number).at_least_once.returns 3
576
+ @client.receive_data @stanza
577
+
578
+ @stanza.expects(:number).at_least_once.returns 2
579
+ @client.receive_data @stanza
580
+ end
581
+
582
+ it 'can be an xpath and will send the result to the handler' do
583
+ @response.expects(:call).with do |stanza, xpath|
584
+ xpath.should be_instance_of Nokogiri::XML::NodeSet
585
+ xpath.should_not be_empty
586
+ stanza.should == @stanza
587
+ end
588
+ @client.register_handler(:iq, "/iq[@id='#{@stanza.id}']") { |stanza, xpath| @response.call stanza, xpath }
589
+ @client.receive_data @stanza
590
+ end
591
+
592
+ it 'can be an xpath with namespaces and will send the result to the handler' do
593
+ @stanza = Blather::Stanza.parse('<message><foo xmlns="http://bar.com"></message>')
594
+ @response.expects(:call).with do |stanza, xpath|
595
+ xpath.should be_instance_of Nokogiri::XML::NodeSet
596
+ xpath.should_not be_empty
597
+ stanza.should == @stanza
598
+ end
599
+ @client.register_handler(:message, "/message/bar:foo", :bar => 'http://bar.com') { |stanza, xpath| @response.call stanza, xpath }
600
+ @client.receive_data @stanza
601
+ end
602
+
603
+ it 'raises an error when a bad guard is tried' do
604
+ lambda { @client.register_handler(:iq, 0) {} }.should raise_error RuntimeError
605
+ end
606
+ end
607
+
608
+ describe 'Blather::Client::Caps' do
609
+ before do
610
+ @client = Blather::Client.new
611
+ @stream = mock()
612
+ @stream.stubs(:send)
613
+ @client.post_init @stream, Blather::JID.new('n@d/r')
614
+ @caps = @client.caps
615
+ end
616
+
617
+ it 'must be of type result' do
618
+ @caps.should respond_to :type
619
+ @caps.type.should == :result
620
+ end
621
+
622
+ it 'can have a client node set' do
623
+ @caps.should respond_to :node=
624
+ @caps.node = "somenode"
625
+ end
626
+
627
+ it 'provides a client node reader' do
628
+ @caps.should respond_to :node
629
+ @caps.node = "somenode"
630
+ @caps.node.should == "somenode##{@caps.ver}"
631
+ end
632
+
633
+ it 'can have identities set' do
634
+ @caps.should respond_to :identities=
635
+ @caps.identities = [{:name => "name", :type => "type", :category => "cat"}]
636
+ end
637
+
638
+ it 'provides an identities reader' do
639
+ @caps.should respond_to :identities
640
+ @caps.identities = [{:name => "name", :type => "type", :category => "cat"}]
641
+ @caps.identities.should == [Blather::Stanza::Iq::DiscoInfo::Identity.new({:name => "name", :type => "type", :category => "cat"})]
642
+ end
643
+
644
+ it 'can have features set' do
645
+ @caps.should respond_to :features=
646
+ @caps.features.size.should == 0
647
+ @caps.features = ["feature1"]
648
+ @caps.features.size.should == 1
649
+ @caps.features += [Blather::Stanza::Iq::DiscoInfo::Feature.new("feature2")]
650
+ @caps.features.size.should == 2
651
+ @caps.features = nil
652
+ @caps.features.size.should == 0
653
+ end
654
+
655
+ it 'provides a features reader' do
656
+ @caps.should respond_to :features
657
+ @caps.features = %w{feature1 feature2}
658
+ @caps.features.should == [Blather::Stanza::Iq::DiscoInfo::Feature.new("feature1"), Blather::Stanza::Iq::DiscoInfo::Feature.new("feature2")]
659
+ end
660
+
661
+ it 'provides a client ver reader' do
662
+ @caps.should respond_to :ver
663
+ @caps.node = 'http://code.google.com/p/exodus'
664
+ @caps.identities = [Blather::Stanza::Iq::DiscoInfo::Identity.new({:name => 'Exodus 0.9.1', :type => 'pc', :category => 'client'})]
665
+ @caps.features = %w{
666
+ http://jabber.org/protocol/caps
667
+ http://jabber.org/protocol/disco#info
668
+ http://jabber.org/protocol/disco#items
669
+ http://jabber.org/protocol/muc
670
+ }
671
+ @caps.ver.should == 'QgayPKawpkPSDYmwT/WM94uAlu0='
672
+ @caps.node.should == "http://code.google.com/p/exodus#QgayPKawpkPSDYmwT/WM94uAlu0="
673
+ end
674
+
675
+ it 'can construct caps presence correctly' do
676
+ @caps.should respond_to :c
677
+ @caps.node = 'http://code.google.com/p/exodus'
678
+ @caps.identities = [Blather::Stanza::Iq::DiscoInfo::Identity.new({:name => 'Exodus 0.9.1', :type => 'pc', :category => 'client'})]
679
+ @caps.features = %w{
680
+ http://jabber.org/protocol/caps
681
+ http://jabber.org/protocol/disco#info
682
+ http://jabber.org/protocol/disco#items
683
+ http://jabber.org/protocol/muc
684
+ }
685
+ Nokogiri::XML(@caps.c.to_xml).to_s.should == Nokogiri::XML("<presence><c xmlns=\"http://jabber.org/protocol/caps\" hash=\"sha-1\" node=\"http://code.google.com/p/exodus\" ver=\"QgayPKawpkPSDYmwT/WM94uAlu0=\"/></presence>").to_s
686
+ end
687
+ end