tp-blather 0.8.2

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 (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