blather 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/CHANGELOG +2 -0
  2. data/Manifest +36 -33
  3. data/README.rdoc +11 -17
  4. data/Rakefile +4 -3
  5. data/blather.gemspec +9 -9
  6. data/examples/drb_client.rb +7 -0
  7. data/examples/echo.rb +9 -18
  8. data/lib/blather.rb +140 -31
  9. data/lib/blather/{core/errors.rb → errors.rb} +0 -0
  10. data/lib/blather/{core/jid.rb → jid.rb} +0 -0
  11. data/lib/blather/{core/roster.rb → roster.rb} +0 -0
  12. data/lib/blather/{core/roster_item.rb → roster_item.rb} +0 -0
  13. data/lib/blather/{core/stanza.rb → stanza.rb} +6 -14
  14. data/lib/blather/stanza/error.rb +31 -0
  15. data/lib/blather/{core/stanza → stanza}/iq.rb +2 -2
  16. data/lib/blather/stanza/iq/query.rb +50 -0
  17. data/lib/blather/{core/stanza → stanza}/iq/roster.rb +3 -3
  18. data/lib/blather/{core/stanza → stanza}/message.rb +6 -2
  19. data/lib/blather/{core/stanza → stanza}/presence.rb +10 -0
  20. data/lib/blather/{core/stanza → stanza}/presence/status.rb +8 -6
  21. data/lib/blather/{core/stanza → stanza}/presence/subscription.rb +1 -1
  22. data/lib/blather/{core/stream.rb → stream.rb} +1 -1
  23. data/lib/blather/{core/stream → stream}/parser.rb +0 -5
  24. data/lib/blather/{core/stream → stream}/resource.rb +0 -0
  25. data/lib/blather/{core/stream → stream}/sasl.rb +16 -5
  26. data/lib/blather/{core/stream → stream}/session.rb +0 -0
  27. data/lib/blather/{core/stream → stream}/tls.rb +0 -0
  28. data/lib/blather/{core/sugar.rb → sugar.rb} +4 -0
  29. data/lib/blather/{core/xmpp_node.rb → xmpp_node.rb} +1 -3
  30. data/spec/blather/{core/jid_spec.rb → jid_spec.rb} +16 -1
  31. data/spec/blather/{core/roster_item_spec.rb → roster_item_spec.rb} +1 -1
  32. data/spec/blather/{core/roster_spec.rb → roster_spec.rb} +11 -1
  33. data/spec/blather/stanza/iq/query_spec.rb +34 -0
  34. data/spec/blather/stanza/iq/roster_spec.rb +7 -0
  35. data/spec/blather/stanza/iq_spec.rb +11 -0
  36. data/spec/blather/stanza/message_spec.rb +52 -0
  37. data/spec/blather/stanza/presence/status_spec.rb +102 -0
  38. data/spec/blather/stanza/presence/subscription_spec.rb +74 -0
  39. data/spec/blather/stanza/presence_spec.rb +24 -0
  40. data/spec/blather/{core/stanza_spec.rb → stanza_spec.rb} +1 -1
  41. data/spec/blather/{core/stream_spec.rb → stream_spec.rb} +208 -9
  42. data/spec/blather/{core/xmpp_node_spec.rb → xmpp_node_spec.rb} +1 -1
  43. metadata +75 -69
  44. data/examples/shell_client.rb +0 -28
  45. data/lib/blather/callback.rb +0 -24
  46. data/lib/blather/client.rb +0 -81
  47. data/lib/blather/core/stanza/iq/query.rb +0 -42
  48. data/lib/blather/extensions.rb +0 -4
  49. data/lib/blather/extensions/last_activity.rb +0 -55
  50. data/lib/blather/extensions/version.rb +0 -85
@@ -0,0 +1,52 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
2
+
3
+ describe 'Blather::Stanza::Message' do
4
+ it 'registers itself' do
5
+ XMPPNode.class_from_registration(:message, nil).must_equal Stanza::Message
6
+ end
7
+
8
+ it 'provides "attr_accessor" for body' do
9
+ s = Stanza::Message.new
10
+ s.body.must_be_nil
11
+ s.detect { |n| n.element_name == 'body' }.must_be_nil
12
+
13
+ s.body = 'test message'
14
+ s.body.wont_be_nil
15
+ s.detect { |n| n.element_name == 'body' }.wont_be_nil
16
+ end
17
+
18
+ it 'provides "attr_accessor" for subject' do
19
+ s = Stanza::Message.new
20
+ s.subject.must_be_nil
21
+ s.detect { |n| n.element_name == 'subject' }.must_be_nil
22
+
23
+ s.subject = 'test subject'
24
+ s.subject.wont_be_nil
25
+ s.detect { |n| n.element_name == 'subject' }.wont_be_nil
26
+ end
27
+
28
+ it 'provides "attr_accessor" for thread' do
29
+ s = Stanza::Message.new
30
+ s.thread.must_be_nil
31
+ s.detect { |n| n.element_name == 'thread' }.must_be_nil
32
+
33
+ s.thread = 1234
34
+ s.thread.wont_be_nil
35
+ s.detect { |n| n.element_name == 'thread' }.wont_be_nil
36
+ end
37
+
38
+ it 'ensures type is one of Stanza::Message::VALID_TYPES' do
39
+ lambda { Stanza::Message.new nil, nil, :invalid_type_name }.must_raise(Blather::ArgumentError)
40
+
41
+ Stanza::Message::VALID_TYPES.each do |valid_type|
42
+ msg = Stanza::Message.new nil, nil, valid_type
43
+ msg.type.must_equal valid_type
44
+ end
45
+ end
46
+
47
+ Stanza::Message::VALID_TYPES.each do |valid_type|
48
+ it "provides a helper (#{valid_type}?) for type #{valid_type}" do
49
+ Stanza::Message.new.must_respond_to :"#{valid_type}?"
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,102 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. .. .. spec_helper])
2
+
3
+ describe 'Blather::Stanza::Presence::Status' do
4
+ it 'registers itself' do
5
+ XMPPNode.class_from_registration(:status, nil).must_equal Stanza::Presence::Status
6
+ end
7
+
8
+ it 'can set state on creation' do
9
+ status = Stanza::Presence::Status.new :away
10
+ status.state.must_equal :away
11
+ end
12
+
13
+ it 'can set a message on creation' do
14
+ status = Stanza::Presence::Status.new nil, 'Say hello!'
15
+ status.message.must_equal 'Say hello!'
16
+ end
17
+
18
+ it 'ensures type is nil or :unavailable' do
19
+ status = Stanza::Presence::Status.new
20
+ lambda { status.type = :invalid_type_name }.must_raise(Blather::ArgumentError)
21
+
22
+ [nil, :unavailable].each do |valid_type|
23
+ status.type = valid_type
24
+ status.type.must_equal valid_type
25
+ end
26
+ end
27
+
28
+ it 'ensures state is one of Presence::Status::VALID_STATES' do
29
+ status = Stanza::Presence::Status.new
30
+ lambda { status.state = :invalid_type_name }.must_raise(Blather::ArgumentError)
31
+
32
+ Stanza::Presence::Status::VALID_STATES.each do |valid_state|
33
+ status.state = valid_state
34
+ status.state.must_equal valid_state
35
+ end
36
+ end
37
+
38
+ it 'returns :available if state is nil' do
39
+ Stanza::Presence::Status.new.state.must_equal :available
40
+ end
41
+
42
+ it 'returns :unavailable if type is :unavailable' do
43
+ status = Stanza::Presence::Status.new
44
+ status.type = :unavailable
45
+ status.state.must_equal :unavailable
46
+ end
47
+
48
+ it 'ensures priority is not greater than 127' do
49
+ lambda { Stanza::Presence::Status.new.priority = 128 }.must_raise(Blather::ArgumentError)
50
+ end
51
+
52
+ it 'ensures priority is not less than -128' do
53
+ lambda { Stanza::Presence::Status.new.priority = -129 }.must_raise(Blather::ArgumentError)
54
+ end
55
+
56
+ it 'has "attr_accessor" for priority' do
57
+ status = Stanza::Presence::Status.new
58
+ status.priority.must_equal 0
59
+
60
+ status.priority = 10
61
+ status.children.detect { |n| n.element_name == 'priority' }.wont_be_nil
62
+ status.priority.must_equal 10
63
+ end
64
+
65
+ it 'has "attr_accessor" for message' do
66
+ status = Stanza::Presence::Status.new
67
+ status.message.must_be_nil
68
+
69
+ status.message = 'new message'
70
+ status.children.detect { |n| n.element_name == 'status' }.wont_be_nil
71
+ status.message.must_equal 'new message'
72
+ end
73
+
74
+ it 'must be comparable by priority' do
75
+ jid = JID.new 'a@b/c'
76
+
77
+ status1 = Stanza::Presence::Status.new
78
+ status1.from = jid
79
+
80
+ status2 = Stanza::Presence::Status.new
81
+ status2.from = jid
82
+
83
+ status1.priority = 1
84
+ status2.priority = -1
85
+ (status1 <=> status2).must_equal 1
86
+ (status2 <=> status1).must_equal -1
87
+
88
+ status2.priority = 1
89
+ (status1 <=> status2).must_equal 0
90
+ end
91
+
92
+ it 'raises an argument error if compared to a status with a different JID' do
93
+ status1 = Stanza::Presence::Status.new
94
+ status1.from = 'a@b/c'
95
+
96
+ status2 = Stanza::Presence::Status.new
97
+ status2.from = 'd@e/f'
98
+
99
+ lambda { status1 <=> status2 }.must_raise(Blather::ArgumentError)
100
+ end
101
+ end
102
+
@@ -0,0 +1,74 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. .. .. spec_helper])
2
+
3
+ describe 'Blather::Stanza::Presence::Subscription' do
4
+ it 'registers itself' do
5
+ XMPPNode.class_from_registration(:subscription, nil).must_equal Stanza::Presence::Subscription
6
+ end
7
+
8
+ it 'can set to on creation' do
9
+ sub = Stanza::Presence::Subscription.new 'a@b'
10
+ sub.to.to_s.must_equal 'a@b'
11
+ end
12
+
13
+ it 'can set a type on creation' do
14
+ sub = Stanza::Presence::Subscription.new nil, :subscribed
15
+ sub.type.must_equal :subscribed
16
+ end
17
+
18
+ it 'strips JIDs when setting #to' do
19
+ sub = Stanza::Presence::Subscription.new 'a@b/c'
20
+ sub.to.to_s.must_equal 'a@b'
21
+ end
22
+
23
+ it 'generates an approval using #approve!' do
24
+ jid = JID.new 'a@b'
25
+ sub = Stanza::Presence::Subscription.new
26
+ sub.from = jid
27
+ sub.approve!
28
+ sub.to.must_equal jid
29
+ sub.type.must_equal :subscribed
30
+ end
31
+
32
+ it 'generates a refusal using #refuse!' do
33
+ jid = JID.new 'a@b'
34
+ sub = Stanza::Presence::Subscription.new
35
+ sub.from = jid
36
+ sub.refuse!
37
+ sub.to.must_equal jid
38
+ sub.type.must_equal :unsubscribed
39
+ end
40
+
41
+ it 'generates an unsubscript using #unsubscribe!' do
42
+ jid = JID.new 'a@b'
43
+ sub = Stanza::Presence::Subscription.new
44
+ sub.from = jid
45
+ sub.unsubscribe!
46
+ sub.to.must_equal jid
47
+ sub.type.must_equal :unsubscribe
48
+ end
49
+
50
+ it 'generates a cancellation using #cancel!' do
51
+ jid = JID.new 'a@b'
52
+ sub = Stanza::Presence::Subscription.new
53
+ sub.from = jid
54
+ sub.cancel!
55
+ sub.to.must_equal jid
56
+ sub.type.must_equal :unsubscribed
57
+ end
58
+
59
+ it 'generates a request using #request!' do
60
+ jid = JID.new 'a@b'
61
+ sub = Stanza::Presence::Subscription.new
62
+ sub.from = jid
63
+ sub.request!
64
+ sub.to.must_equal jid
65
+ sub.type.must_equal :subscribe
66
+ end
67
+
68
+ it 'has a #request? helper' do
69
+ sub = Stanza::Presence::Subscription.new
70
+ sub.must_respond_to :request?
71
+ sub.type = :subscribe
72
+ sub.request?.must_equal true
73
+ end
74
+ end
@@ -0,0 +1,24 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
2
+
3
+ describe 'Blather::Stanza::Presence' do
4
+ it 'registers itself' do
5
+ XMPPNode.class_from_registration(:presence, nil).must_equal Stanza::Presence
6
+ end
7
+
8
+ it 'ensures type is one of Stanza::Presence::VALID_TYPES' do
9
+ presence = Stanza::Presence.new
10
+ lambda { presence.type = :invalid_type_name }.must_raise(Blather::ArgumentError)
11
+
12
+ Stanza::Presence::VALID_TYPES.each do |valid_type|
13
+ presence.type = valid_type
14
+ presence.type.must_equal valid_type
15
+ end
16
+ end
17
+
18
+ Stanza::Presence::VALID_TYPES.each do |valid_type|
19
+ it "provides a helper (#{valid_type}?) for type #{valid_type}" do
20
+ Stanza::Presence.new.must_respond_to :"#{valid_type}?"
21
+ end
22
+ end
23
+
24
+ end
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
1
+ require File.join(File.dirname(__FILE__), *%w[.. spec_helper])
2
2
 
3
3
  describe 'Blather::Stanza' do
4
4
  it 'provides .next_id helper for generating new IDs' do
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
1
+ require File.join(File.dirname(__FILE__), *%w[.. spec_helper])
2
2
 
3
3
  describe 'Blather::Stream' do
4
4
  class MockStream; include Stream; end
@@ -44,17 +44,108 @@ describe 'Blather::Stream' do
44
44
  s.connection_completed
45
45
  end
46
46
 
47
+ it 'sends stanzas to the client when the stream is ready' do
48
+ client = mock()
49
+ client.stubs(:jid=)
50
+ client.expects(:call)
51
+ stream = MockStream.new client, JID.new('n@d/r'), 'pass'
52
+
53
+ stream.expects(:send_data).with do |val|
54
+ val.must_match(/stream:stream/)
55
+ stream.receive_data "<stream:stream><message to='a@b/c' from='d@e/f' type='chat' xml:lang='en'><body>Message!</body></message>"
56
+ end
57
+ stream.connection_completed
58
+ end
59
+
60
+ it 'puts itself in the stopped state when unbound' do
61
+ stream = mock_stream do |val|
62
+ val.must_match(/stream:stream/)
63
+ stream.receive_data "<stream:stream>"
64
+
65
+ stream.stopped?.wont_equal true
66
+ stream.unbind
67
+ stream.stopped?.must_equal true
68
+ end
69
+ stream.connection_completed
70
+ end
71
+
72
+ it 'stops when sent </stream:stream>' do
73
+ state = nil
74
+ stream = mock_stream do |val|
75
+ case state
76
+ when nil
77
+ val.must_match(/stream:stream/)
78
+ state = :started
79
+
80
+ when :started
81
+ stream.stopped?.wont_equal true
82
+ state = :stopped
83
+ stream.receive_data "</stream:stream>"
84
+ true
85
+
86
+ when :stopped
87
+ stream.stopped?.must_equal true
88
+ val.must_equal "</stream:stream>"
89
+ true
90
+
91
+ else
92
+ false
93
+
94
+ end
95
+ end
96
+ stream.connection_completed
97
+ stream.receive_data "<stream:stream><stream:features><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' /></stream:features>"
98
+ end
99
+
100
+ it 'raises an error when it receives stream:error' do
101
+ lambda do
102
+ state = nil
103
+ stream = mock_stream do |val|
104
+ case state
105
+ when nil
106
+ val.must_match(/stream:stream/)
107
+ state = :started
108
+
109
+ when :started
110
+ stream.stopped?.wont_equal true
111
+ state = :stopped
112
+ stream.receive_data "<stream:error><conflict xmlns='urn:ietf:params:xml:ns:xmpp-streams'/></stream:error>"
113
+ true
114
+
115
+ when :stopped
116
+ stream.stopped?.must_equal true
117
+ val.must_equal "</stream:stream>"
118
+ true
119
+
120
+ else
121
+ false
122
+
123
+ end
124
+ end
125
+ stream.connection_completed
126
+ stream.receive_data "<stream:stream><stream:features><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' /></stream:features>"
127
+ end.must_raise(StreamError)
128
+ end
129
+
47
130
  it 'starts TLS when asked' do
48
131
  state = nil
49
- @stream = mock_stream do |val|
50
- case
51
- when state.nil? && val =~ /stream:stream/ then state = :started
52
- when state == :started && val =~ /starttls/ then true
53
- else false
132
+ stream = mock_stream do |val|
133
+ case state
134
+ when nil
135
+ val.must_match(/stream:stream/)
136
+ state = :started
137
+
138
+ when :started
139
+ val.must_match(/starttls/)
140
+ true
141
+
142
+ else
143
+ false
144
+
54
145
  end
55
146
  end
56
- @stream.connection_completed
57
- @stream.receive_data "<stream:stream><stream:features><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' /></stream:features>"
147
+ stream.connection_completed
148
+ stream.receive_data "<stream:stream><stream:features><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' /></stream:features>"
58
149
  end
59
150
 
60
151
  it 'connects via SASL MD5 when asked' do
@@ -80,7 +171,7 @@ describe 'Blather::Stream' do
80
171
  true
81
172
 
82
173
  when :auth_sent
83
- val.must_equal('<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">bm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixjaGFyc2V0PXV0Zi04LHVzZXJuYW1lPSJuIixyZWFsbT0ic29tZXJlYWxtIixjbm9uY2U9Ijc3N2Q0NWJiYmNkZjUwZDQ5YzQyYzcwYWQ3YWNmNWZlIixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJ4bXBwL2Qi</response>')
174
+ val.must_equal('<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">bm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixjaGFyc2V0PXV0Zi04LHVzZXJuYW1lPSJuIixyZWFsbT0ic29tZXJlYWxtIixjbm9uY2U9Ijc3N2Q0NWJiYmNkZjUwZDQ5YzQyYzcwYWQ3YWNmNWZlIixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJ4bXBwL2QiLHJlc3BvbnNlPTZiNTlhY2Q1ZWJmZjhjZTA0NTYzMGFiMDU2Zjg3MTdm</response>')
84
175
  state = :response1_sent
85
176
  stream.receive_data "<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZAo=</challenge>"
86
177
  true
@@ -170,6 +261,112 @@ describe 'Blather::Stream' do
170
261
  stream.connection_completed
171
262
  end
172
263
 
264
+ it 'tried each possible mechanism until it fails completely' do
265
+ state = nil
266
+ client = mock()
267
+ client.stubs(:jid=)
268
+ stream = MockStream.new client, JID.new('n@d/r'), 'pass'
269
+
270
+ stream.expects(:send_data).times(5).with do |val|
271
+ case state
272
+ when nil
273
+ val.must_match(/stream:stream/)
274
+ state = :started
275
+ stream.receive_data "<stream:stream><stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>DIGEST-MD5</mechanism><mechanism>PLAIN</mechanism><mechanism>ANONYMOUS</mechanism></mechanisms></stream:features>"
276
+ true
277
+
278
+ when :started
279
+ val.must_match(/mechanism="DIGEST-MD5"/)
280
+ state = :failed_md5
281
+ stream.receive_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>"
282
+ true
283
+
284
+ when :failed_md5
285
+ val.must_match(/mechanism="PLAIN"/)
286
+ state = :failed_plain
287
+ stream.receive_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>"
288
+ true
289
+
290
+ when :failed_plain
291
+ val.must_match(/mechanism="ANONYMOUS"/)
292
+ state = :failed_anon
293
+ stream.receive_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>"
294
+ true
295
+
296
+ when :failed_anon
297
+ val.must_match(/\/stream:stream/)
298
+ state = :complete
299
+ true
300
+
301
+ else
302
+ false
303
+
304
+ end
305
+ end
306
+ stream.connection_completed
307
+ end
308
+
309
+ it 'tries each mechanism until it succeeds' do
310
+ state = nil
311
+ client = mock()
312
+ client.stubs(:jid=)
313
+ stream = MockStream.new client, JID.new('n@d/r'), 'pass'
314
+
315
+ stream.expects(:send_data).times(4).with do |val|
316
+ case state
317
+ when nil
318
+ val.must_match(/stream:stream/)
319
+ state = :started
320
+ stream.receive_data "<stream:stream><stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>DIGEST-MD5</mechanism><mechanism>PLAIN</mechanism><mechanism>ANONYMOUS</mechanism></mechanisms></stream:features>"
321
+ true
322
+
323
+ when :started
324
+ val.must_match(/mechanism="DIGEST-MD5"/)
325
+ state = :failed_md5
326
+ stream.receive_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>"
327
+ true
328
+
329
+ when :failed_md5
330
+ val.must_match(/mechanism="PLAIN"/)
331
+ state = :plain_sent
332
+ stream.receive_data "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>"
333
+ true
334
+
335
+ when :plain_sent
336
+ val.must_match(/stream:stream/)
337
+ state = :complete
338
+ true
339
+
340
+ else
341
+ false
342
+
343
+ end
344
+ end
345
+ stream.connection_completed
346
+ end
347
+
348
+ it 'raises an exception when an unknown mechanism is sent' do
349
+ state = nil
350
+ client = mock()
351
+ client.stubs(:jid=)
352
+ stream = MockStream.new client, JID.new('n@d/r'), 'pass'
353
+
354
+ stream.expects(:send_data).times(2).with do |val|
355
+ if !state
356
+ state = :started
357
+ val.must_match(/stream:stream/)
358
+ lambda do
359
+ stream.receive_data "<stream:stream><stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>UNKNOWN</mechanism></mechanisms></stream:features>"
360
+ end.must_raise(Stream::SASL::UnknownMechanism)
361
+
362
+ else
363
+ val.must_match(/failure(.*)invalid\-mechanism/)
364
+
365
+ end
366
+ end
367
+ stream.connection_completed
368
+ end
369
+
173
370
  it 'will bind to a resource set by the server' do
174
371
  state = nil
175
372
  class Client; attr_accessor :jid; end
@@ -240,6 +437,7 @@ describe 'Blather::Stream' do
240
437
  client.stubs(:jid=)
241
438
  stream = MockStream.new client, JID.new('n@d/r'), 'pass'
242
439
 
440
+ client.expects(:stream_started)
243
441
  stream.expects(:send_data).times(2).with do |val|
244
442
  case state
245
443
  when nil
@@ -251,6 +449,7 @@ describe 'Blather::Stream' do
251
449
  when :started
252
450
  val.must_match('<iq id="[^"]+" type="set" to="d"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>')
253
451
  state = :completed
452
+ stream.receive_data "<iq from='d' type='result' id='#{val[/id="([^"]+)"/,1]}'/>"
254
453
  true
255
454
 
256
455
  else