blather 0.3.0 → 0.3.1

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.
@@ -52,13 +52,6 @@ class Stanza
52
52
  self.category = category
53
53
  end
54
54
  end
55
-
56
- def eql?(other)
57
- other.kind_of?(self.class) &&
58
- other.name == self.name &&
59
- other.type == self.type &&
60
- other.category == self.category
61
- end
62
55
  end
63
56
 
64
57
  class Feature < XMPPNode
@@ -72,11 +65,6 @@ class Stanza
72
65
  self.var = var
73
66
  end
74
67
  end
75
-
76
- def eql?(other)
77
- other.kind_of?(self.class) &&
78
- other.var == self.var
79
- end
80
68
  end
81
69
  end
82
70
 
@@ -46,13 +46,6 @@ class Stanza
46
46
 
47
47
  attribute_accessor :node, :name, :to_sym => false
48
48
  end
49
-
50
- def eql?(other)
51
- other.kind_of?(self.class) &&
52
- other.jid == self.jid &&
53
- other.node == self.node &&
54
- other.name == self.name
55
- end
56
49
  end
57
50
 
58
51
  end #Stanza
@@ -87,8 +87,8 @@ module Blather
87
87
  def unbind # :nodoc:
88
88
  # @keepalive.cancel
89
89
  @state = :stopped
90
- @client.call @error if @error
91
- @client.stopped
90
+ @client.receive_data @error if @error
91
+ @client.unbind
92
92
  end
93
93
 
94
94
  ##
@@ -162,7 +162,7 @@ module Blather
162
162
  # Called when @state == :ready
163
163
  # Simply passes the stanza to the client
164
164
  def ready
165
- @client.call @node.to_stanza
165
+ @client.receive_data @node.to_stanza
166
166
  end
167
167
 
168
168
  def handle_stream_error
@@ -242,7 +242,7 @@ module Blather
242
242
  @session = Session.new self, @to
243
243
  # on success destroy the session object, let the client know the stream has been started
244
244
  # then continue the features dispatch process
245
- @session.on_success { LOG.debug "SESSION: SUCCESS"; @session = nil; @client.stream_started(self); @state = :features; dispatch }
245
+ @session.on_success { LOG.debug "SESSION: SUCCESS"; @session = nil; @client.post_init; @state = :features; dispatch }
246
246
  # on failure end the stream
247
247
  @session.on_failure { |err| LOG.debug "SESSION: FAILURE"; @error = err; stop }
248
248
 
@@ -6,7 +6,7 @@ class Stream
6
6
 
7
7
  def receive(node) # :nodoc:
8
8
  if node.element_name == 'handshake'
9
- @client.stream_started(self)
9
+ @client.post_init
10
10
  else
11
11
  super
12
12
  end
@@ -1,4 +1,389 @@
1
1
  require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
2
+ require 'blather/client/client'
2
3
 
3
4
  describe 'Blather::Client' do
5
+ before do
6
+ @client = Blather::Client.new
7
+ end
8
+
9
+ it 'provides a JID accessor' do
10
+ @client.must_respond_to :jid
11
+ @client.jid.must_be_nil
12
+
13
+ jid = 'me@me.com/test'
14
+ @client.must_respond_to :jid=
15
+ @client.jid = jid
16
+ @client.jid.must_be_kind_of JID
17
+ @client.jid.must_equal JID.new(jid)
18
+ end
19
+
20
+ it 'provides a reader for the roster' do
21
+ @client.must_respond_to :roster
22
+ @client.roster.must_be_kind_of Roster
23
+ end
24
+
25
+ it 'provides a status reader' do
26
+ @client.must_respond_to :status
27
+ @client.status = :away
28
+ @client.status.must_equal :away
29
+ end
30
+
31
+ it 'can be setup' do
32
+ @client.must_respond_to :setup
33
+ @client.setup('me@me.com', 'pass').must_equal @client
34
+ end
35
+
36
+ it 'knows if it has been setup' do
37
+ @client.must_respond_to :setup?
38
+ @client.setup?.must_equal false
39
+ @client.setup 'me@me.com', 'pass'
40
+ @client.setup?.must_equal true
41
+ end
42
+
43
+ it 'cannot be run before being setup' do
44
+ lambda { @client.run }.must_raise RuntimeError
45
+ end
46
+
47
+ it 'starts up a Component connection when setup without a node' do
48
+ setup = 'pubsub.jabber.local', 'secret'
49
+ @client.setup *setup
50
+ Blather::Stream::Component.expects(:start).with @client, *setup
51
+ @client.run
52
+ end
53
+
54
+ it 'starts up a Client connection when setup with a node' do
55
+ setup = 'test@jabber.local', 'secret'
56
+ @client.setup *setup
57
+ Blather::Stream::Client.expects(:start).with @client, *setup
58
+ @client.run
59
+ end
60
+
61
+ it 'writes to the connection the closes when #close is called' do
62
+ stream = mock()
63
+ stream.expects(:close_connection_after_writing)
64
+ Blather::Stream::Component.stubs(:start).returns stream
65
+ @client.setup('me.com', 'secret').run
66
+ @client.close
67
+ end
68
+
69
+ it 'shuts down EM when #unbind is called if it is running' do
70
+ EM.expects(:reactor_running?).returns true
71
+ EM.expects(:stop)
72
+ @client.unbind
73
+ end
74
+
75
+ it 'does nothing when #unbind is called and EM is not running' do
76
+ EM.expects(:reactor_running?).returns false
77
+ EM.expects(:stop).never
78
+ @client.unbind
79
+ end
80
+
81
+ it 'raises an error if the stream type somehow is not supported' do
82
+ Blather::Stream::Component.stubs(:start).returns nil
83
+ @client.setup('me.com', 'secret').run
84
+ lambda { @client.post_init }.must_raise RuntimeError
85
+ end
86
+
87
+ it 'can register a temporary handler based on stanza ID' do
88
+ stanza = Stanza::Iq.new
89
+ response = mock()
90
+ response.expects(:call)
91
+ @client.register_tmp_handler(stanza.id) { |_| response.call }
92
+ @client.receive_data stanza
93
+ end
94
+
95
+ it 'removes a tmp handler as soon as it is used' do
96
+ stanza = Stanza::Iq.new
97
+ response = mock()
98
+ response.expects(:call)
99
+ @client.register_tmp_handler(stanza.id) { |_| response.call }
100
+ @client.receive_data stanza
101
+ @client.receive_data stanza
102
+ end
103
+
104
+ it 'will create a handler then write the stanza' do
105
+ stanza = Stanza::Iq.new
106
+ response = mock()
107
+ response.expects(:call)
108
+ @client.expects(:write).with do |s|
109
+ @client.receive_data stanza
110
+ s.must_equal stanza
111
+ end
112
+ @client.write_with_handler(stanza) { |_| response.call }
113
+ end
114
+
115
+ it 'can register a handler' do
116
+ stanza = Stanza::Iq.new
117
+ response = mock()
118
+ response.expects(:call).times(2)
119
+ @client.register_handler(:iq) { |_| response.call }
120
+ @client.receive_data stanza
121
+ @client.receive_data stanza
122
+ end
123
+ end
124
+
125
+ describe 'Blather::Client#write' do
126
+ before do
127
+ @client = Blather::Client.new
128
+ end
129
+
130
+ it 'sets the from attr on a stanza' do
131
+ jid = 'me@me.com'
132
+ stanza = mock(:from => nil)
133
+ stanza.expects(:from=).with jid
134
+ @client.jid = jid
135
+ @client.write stanza
136
+ end
137
+
138
+ it 'does not set the from attr if it already exists' do
139
+ jid = 'me@me.com'
140
+ stanza = Stanza::Iq.new
141
+ stanza.from = jid
142
+ stanza.expects(:from).returns jid
143
+ stanza.expects(:from=).never
144
+ @client.jid = jid
145
+ @client.write stanza
146
+ end
147
+
148
+ it 'writes to the stream' do
149
+ stanza = Stanza::Iq.new
150
+ stream = mock()
151
+ stream.expects(:send).with stanza
152
+ Blather::Stream::Client.expects(:start).returns stream
153
+ @client.setup('me@me.com', 'me').run
154
+ @client.write stanza
155
+ end
156
+ end
157
+
158
+ describe 'Blather::Client#status=' do
159
+ before do
160
+ @client = Blather::Client.new
161
+ end
162
+
163
+ it 'updates the state when not sending to a JID' do
164
+ @client.status.wont_equal :away
165
+ @client.status = :away, 'message'
166
+ @client.status.must_equal :away
167
+ end
168
+
169
+ it 'does not update the state when sending to a JID' do
170
+ @client.status.wont_equal :away
171
+ @client.status = :away, 'message', 'me@me.com'
172
+ @client.status.wont_equal :away
173
+ end
174
+
175
+ it 'writes the new status to the stream' do
176
+ Stanza::Presence::Status.stubs(:next_id).returns 0
177
+ status = [:away, 'message']
178
+ @client.expects(:write).with do |s|
179
+ s.must_be_kind_of Stanza::Presence::Status
180
+ s.to_s.must_equal Stanza::Presence::Status.new(*status).to_s
181
+ end
182
+ @client.status = status
183
+ end
184
+ end
185
+
186
+ describe 'Blather::Client default handlers' do
187
+ before do
188
+ @client = Blather::Client.new
189
+ end
190
+
191
+ it 're-raises errors' do
192
+ err = BlatherError.new
193
+ lambda { @client.receive_data err }.must_raise BlatherError
194
+ end
195
+
196
+ it 'responds to iq:get with a "service-unavailable" error' do
197
+ get = Stanza::Iq.new :get
198
+ err = StanzaError.new(get, 'service-unavailable', :cancel).to_node
199
+ @client.expects(:write).with err
200
+ @client.receive_data get
201
+ end
202
+
203
+ it 'responds to iq:get with a "service-unavailable" error' do
204
+ get = Stanza::Iq.new :get
205
+ err = StanzaError.new(get, 'service-unavailable', :cancel).to_node
206
+ @client.expects(:write).with err
207
+ @client.receive_data get
208
+ end
209
+
210
+ it 'responds to iq:set with a "service-unavailable" error' do
211
+ get = Stanza::Iq.new :set
212
+ err = StanzaError.new(get, 'service-unavailable', :cancel).to_node
213
+ @client.expects(:write).with err
214
+ @client.receive_data get
215
+ end
216
+
217
+ it 'handles status changes by updating the roster if the status is from a JID in the roster' do
218
+ jid = 'friend@jabber.local'
219
+ status = Stanza::Presence::Status.new :away
220
+ status.stubs(:from).returns jid
221
+ roster_item = mock()
222
+ roster_item.expects(:status=).with status
223
+ @client.stubs(:roster).returns({status.from => roster_item})
224
+ @client.receive_data status
225
+ end
226
+
227
+ it 'handles an incoming roster node by processing it through the roster' do
228
+ roster = Stanza::Iq::Roster.new
229
+ client_roster = mock()
230
+ client_roster.expects(:process).with roster
231
+ @client.stubs(:roster).returns client_roster
232
+ @client.receive_data roster
233
+ end
234
+ end
235
+
236
+ describe 'Blather::Client with a Component stream' do
237
+ before do
238
+ class MockComponent < Blather::Stream::Component; def initialize(); end; end
239
+ @client = Blather::Client.new
240
+ Blather::Stream::Component.stubs(:start).returns MockComponent.new('')
241
+ @client.setup('me.com', 'secret').run
242
+ end
243
+
244
+ it 'calls the ready handler when sent post_init' do
245
+ ready = mock()
246
+ ready.expects(:call)
247
+ @client.register_handler(:ready) { ready.call }
248
+ @client.post_init
249
+ end
250
+ end
251
+
252
+ describe 'Blather::Client with a Client stream' do
253
+ before do
254
+ class MockClientStream < Blather::Stream::Client; def initialize(); end; end
255
+ @stream = MockClientStream.new('')
256
+ @client = Blather::Client.new
257
+ Blather::Stream::Client.stubs(:start).returns @stream
258
+ @client.setup('me@me.com', 'secret').run
259
+ end
260
+
261
+ it 'sends a request for the roster when post_init is called' do
262
+ @stream.expects(:send).with { |stanza| stanza.must_be_kind_of Stanza::Iq::Roster }
263
+ @client.post_init
264
+ end
265
+
266
+ it 'calls the ready handler after post_init and roster is received' do
267
+ result_roster = Stanza::Iq::Roster.new :result
268
+ @stream.stubs(:send).with { |s| result_roster.id = s.id; @client.receive_data result_roster; true }
269
+
270
+ ready = mock()
271
+ ready.expects(:call)
272
+ @client.register_handler(:ready) { ready.call }
273
+ @client.post_init
274
+ end
275
+ end
276
+
277
+ describe 'Blather::Client guards' do
278
+ before do
279
+ @client = Blather::Client.new
280
+ @stanza = Stanza::Iq.new
281
+ @response = mock()
282
+ end
283
+
284
+ it 'can be a symbol' do
285
+ @response.expects :call
286
+ @client.register_handler(:iq, :chat?) { |_| @response.call }
287
+
288
+ @stanza.expects(:chat?).returns true
289
+ @client.receive_data @stanza
290
+
291
+ @stanza.expects(:chat?).returns false
292
+ @client.receive_data @stanza
293
+ end
294
+
295
+ it 'can be a hash with string match' do
296
+ @response.expects :call
297
+ @client.register_handler(:iq, :body => 'exit') { |_| @response.call }
298
+
299
+ @stanza.expects(:body).returns 'exit'
300
+ @client.receive_data @stanza
301
+
302
+ @stanza.expects(:body).returns 'not-exit'
303
+ @client.receive_data @stanza
304
+ end
305
+
306
+ it 'can be a hash with a value' do
307
+ @response.expects :call
308
+ @client.register_handler(:iq, :number => 0) { |_| @response.call }
309
+
310
+ @stanza.expects(:number).returns 0
311
+ @client.receive_data @stanza
312
+
313
+ @stanza.expects(:number).returns 1
314
+ @client.receive_data @stanza
315
+ end
316
+
317
+ it 'can be a hash with a regexp' do
318
+ @response.expects :call
319
+ @client.register_handler(:iq, :body => /exit/) { |_| @response.call }
320
+
321
+ @stanza.expects(:body).returns 'more than just exit, but exit still'
322
+ @client.receive_data @stanza
323
+
324
+ @stanza.expects(:body).returns 'keyword not found'
325
+ @client.receive_data @stanza
326
+ end
327
+
328
+ it 'can be a hash with an array' do
329
+ @response.expects(:call).times(2)
330
+ @client.register_handler(:iq, :type => [:result, :error]) { |_| @response.call }
331
+
332
+ stanza = Stanza::Iq.new
333
+ stanza.expects(:type).at_least_once.returns :result
334
+ @client.receive_data stanza
335
+
336
+ stanza = Stanza::Iq.new
337
+ stanza.expects(:type).at_least_once.returns :error
338
+ @client.receive_data stanza
339
+
340
+ stanza = Stanza::Iq.new
341
+ stanza.expects(:type).at_least_once.returns :get
342
+ @client.receive_data stanza
343
+ end
344
+
345
+ it 'chained are treated like andand (short circuited)' do
346
+ @response.expects :call
347
+ @client.register_handler(:iq, :type => :get, :body => 'test') { |_| @response.call }
348
+
349
+ stanza = Stanza::Iq.new
350
+ stanza.expects(:type).at_least_once.returns :get
351
+ stanza.expects(:body).returns 'test'
352
+ @client.receive_data stanza
353
+
354
+ stanza = Stanza::Iq.new
355
+ stanza.expects(:type).at_least_once.returns :set
356
+ stanza.expects(:body).never
357
+ @client.receive_data stanza
358
+ end
359
+
360
+ it 'within an Array are treated as oror (short circuited)' do
361
+ @response.expects(:call).times 2
362
+ @client.register_handler(:iq, [{:type => :get}, {:body => 'test'}]) { |_| @response.call }
363
+
364
+ stanza = Stanza::Iq.new
365
+ stanza.expects(:type).at_least_once.returns :set
366
+ stanza.expects(:body).returns 'test'
367
+ @client.receive_data stanza
368
+
369
+ stanza = Stanza::Iq.new
370
+ stanza.stubs(:type).at_least_once.returns :get
371
+ stanza.expects(:body).never
372
+ @client.receive_data stanza
373
+ end
374
+
375
+ it 'can be a lambda' do
376
+ @response.expects :call
377
+ @client.register_handler(:iq, lambda { |s| s.number % 3 == 0 }) { |_| @response.call }
378
+
379
+ @stanza.expects(:number).at_least_once.returns 3
380
+ @client.receive_data @stanza
381
+
382
+ @stanza.expects(:number).at_least_once.returns 2
383
+ @client.receive_data @stanza
384
+ end
385
+
386
+ it 'raises an error when a bad guard is tried' do
387
+ lambda { @client.register_handler(:iq, 0) {} }.must_raise RuntimeError
388
+ end
4
389
  end