vines 0.4.0 → 0.4.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.
Files changed (66) hide show
  1. data/LICENSE +1 -1
  2. data/Rakefile +33 -53
  3. data/conf/certs/README +10 -3
  4. data/conf/certs/ca-bundle.crt +55 -410
  5. data/lib/vines/cluster/connection.rb +1 -1
  6. data/lib/vines/command/ldap.rb +1 -1
  7. data/lib/vines/command/schema.rb +1 -1
  8. data/lib/vines/config.rb +11 -5
  9. data/lib/vines/jid.rb +3 -3
  10. data/lib/vines/stanza/iq/disco_items.rb +1 -1
  11. data/lib/vines/storage/local.rb +1 -0
  12. data/lib/vines/store.rb +50 -9
  13. data/lib/vines/stream.rb +5 -9
  14. data/lib/vines/stream/client/session.rb +1 -1
  15. data/lib/vines/stream/http.rb +3 -1
  16. data/lib/vines/stream/http/ready.rb +5 -1
  17. data/lib/vines/stream/http/request.rb +22 -0
  18. data/lib/vines/version.rb +1 -1
  19. data/test/config/host_test.rb +9 -9
  20. data/test/config/pubsub_test.rb +2 -2
  21. data/test/config_test.rb +5 -3
  22. data/test/jid_test.rb +9 -0
  23. data/test/rake_test_loader.rb +1 -1
  24. data/test/router_test.rb +7 -7
  25. data/test/stanza/iq/disco_info_test.rb +2 -2
  26. data/test/stanza/iq/private_storage_test.rb +1 -1
  27. data/test/stanza/iq_test.rb +1 -1
  28. data/test/stanza/presence/subscribe_test.rb +1 -1
  29. data/test/stanza/pubsub/subscribe_test.rb +3 -3
  30. data/test/stanza/pubsub/unsubscribe_test.rb +3 -3
  31. data/test/storage_test.rb +15 -8
  32. data/test/store_test.rb +131 -0
  33. data/test/stream/client/ready_test.rb +6 -6
  34. data/test/stream/component/ready_test.rb +1 -1
  35. data/test/stream/http/ready_test.rb +46 -11
  36. data/test/stream/http/request_test.rb +83 -11
  37. data/test/stream/http/sessions_test.rb +2 -2
  38. data/web/chat/coffeescripts/chat.coffee +1 -1
  39. data/web/chat/index.html +9 -10
  40. data/web/chat/javascripts/app.js +1 -1
  41. data/web/lib/coffeescripts/button.coffee +1 -1
  42. data/web/lib/coffeescripts/contact.coffee +2 -2
  43. data/web/lib/coffeescripts/filter.coffee +1 -1
  44. data/web/lib/coffeescripts/layout.coffee +2 -2
  45. data/web/lib/coffeescripts/login.coffee +1 -1
  46. data/web/lib/coffeescripts/logout.coffee +2 -2
  47. data/web/lib/coffeescripts/navbar.coffee +1 -1
  48. data/web/lib/coffeescripts/notification.coffee +1 -1
  49. data/web/lib/coffeescripts/router.coffee +1 -1
  50. data/web/lib/coffeescripts/session.coffee +1 -1
  51. data/web/lib/coffeescripts/transfer.coffee +1 -1
  52. data/web/lib/javascripts/base.js +10 -9
  53. metadata +65 -43
  54. data/web/chat/javascripts/chat.js +0 -390
  55. data/web/chat/javascripts/init.js +0 -21
  56. data/web/lib/javascripts/button.js +0 -39
  57. data/web/lib/javascripts/contact.js +0 -94
  58. data/web/lib/javascripts/filter.js +0 -88
  59. data/web/lib/javascripts/layout.js +0 -48
  60. data/web/lib/javascripts/login.js +0 -88
  61. data/web/lib/javascripts/logout.js +0 -11
  62. data/web/lib/javascripts/navbar.js +0 -69
  63. data/web/lib/javascripts/notification.js +0 -26
  64. data/web/lib/javascripts/router.js +0 -105
  65. data/web/lib/javascripts/session.js +0 -291
  66. data/web/lib/javascripts/transfer.js +0 -124
@@ -2,7 +2,7 @@ require 'rake'
2
2
 
3
3
  # Use the latest MiniTest gem instead of the buggy
4
4
  # version included with Ruby 1.9.2.
5
- gem 'minitest', '2.2.2'
5
+ gem 'minitest', '~> 2.11.2'
6
6
 
7
7
  # Load the test files from the command line.
8
8
 
@@ -46,7 +46,7 @@ class RouterTest < MiniTest::Unit::TestCase
46
46
 
47
47
  assert_equal 2, @router.size
48
48
  assert_equal 0, @router.connected_resources(@alice, romeo).size
49
- @config.vhosts['wonderland.lit'].cross_domain_messages true
49
+ @config.vhost('wonderland.lit').cross_domain_messages true
50
50
  assert_equal 1, @router.connected_resources(@alice, romeo).size
51
51
 
52
52
  assert stream1.verify
@@ -148,7 +148,7 @@ class RouterTest < MiniTest::Unit::TestCase
148
148
  end
149
149
 
150
150
  def test_multiple_s2s_streams_are_load_balanced
151
- @config.vhosts['wonderland.lit'].cross_domain_messages true
151
+ @config.vhost('wonderland.lit').cross_domain_messages true
152
152
  stream1 = s2s('wonderland.lit', 'verona.lit')
153
153
  stream2 = s2s('wonderland.lit', 'verona.lit')
154
154
  @router << stream1
@@ -166,11 +166,11 @@ class RouterTest < MiniTest::Unit::TestCase
166
166
  private
167
167
 
168
168
  def stream(jid)
169
- MiniTest::Mock.new.tap do |stream|
170
- stream.expect(:connected?, true)
171
- stream.expect(:stream_type, :client)
172
- stream.expect(:user, Vines::User.new(jid: jid))
173
- end
169
+ stream = MiniTest::Mock.new
170
+ stream.expect(:connected?, true)
171
+ stream.expect(:stream_type, :client)
172
+ stream.expect(:user, Vines::User.new(jid: jid))
173
+ stream
174
174
  end
175
175
 
176
176
  def component(jid)
@@ -36,7 +36,7 @@ class DiscoInfoTest < MiniTest::Unit::TestCase
36
36
  </iq>
37
37
  }.strip.gsub(/\n|\s{2,}/, ''))
38
38
 
39
- @config.vhosts['wonderland.lit'].private_storage false
39
+ @config.vhost('wonderland.lit').private_storage false
40
40
  @stream.expect(:write, nil, [expected])
41
41
 
42
42
  stanza = Vines::Stanza::Iq::DiscoInfo.new(node, @stream)
@@ -62,7 +62,7 @@ class DiscoInfoTest < MiniTest::Unit::TestCase
62
62
  </iq>
63
63
  }.strip.gsub(/\n|\s{2,}/, ''))
64
64
 
65
- @config.vhosts['wonderland.lit'].private_storage true
65
+ @config.vhost('wonderland.lit').private_storage true
66
66
  @stream.expect(:write, nil, [expected])
67
67
 
68
68
  stanza = Vines::Stanza::Iq::DiscoInfo.new(node, @stream)
@@ -20,7 +20,7 @@ class PrivateStorageTest < MiniTest::Unit::TestCase
20
20
  query = %q{<query xmlns="jabber:iq:private"><one xmlns="a"/></query>}
21
21
  node = node(%Q{<iq id="42" type="get">#{query}</iq>})
22
22
 
23
- @config.vhosts['wonderland.lit'].private_storage false
23
+ @config.vhost('wonderland.lit').private_storage false
24
24
  @stream.expect(:domain, 'wonderland.lit')
25
25
  @stream.expect(:config, @config)
26
26
 
@@ -44,7 +44,7 @@ class IqTest < MiniTest::Unit::TestCase
44
44
 
45
45
  @stream.expect(:config, @config)
46
46
  @stream.expect(:user, hatter)
47
- @stream.expect(:connected_resources, [recipient], [alice.jid.to_s])
47
+ @stream.expect(:connected_resources, [recipient], [alice.jid])
48
48
 
49
49
  stanza = Vines::Stanza::Iq.new(node, @stream)
50
50
  stanza.process
@@ -13,7 +13,7 @@ class SubscribeTest < MiniTest::Unit::TestCase
13
13
 
14
14
  user = MiniTest::Mock.new
15
15
  user.expect(:jid, alice)
16
- user.expect(:request_subscription, nil, [hatter.to_s])
16
+ user.expect(:request_subscription, nil, [hatter])
17
17
  user.expect(:contact, contact, [hatter])
18
18
 
19
19
  storage = MiniTest::Mock.new
@@ -113,7 +113,7 @@ class SubscribePubSubTest < MiniTest::Unit::TestCase
113
113
  unless @mock_pubsub
114
114
  @mock_pubsub = MiniTest::Mock.new
115
115
  @mock_pubsub.expect(:node?, true, ['game_13'])
116
- @mock_pubsub.expect(:subscribed?, true, ['game_13', 'alice@wonderland.lit/tea'])
116
+ @mock_pubsub.expect(:subscribed?, true, ['game_13', Vines::JID.new('alice@wonderland.lit/tea')])
117
117
  end
118
118
  @mock_pubsub
119
119
  end
@@ -157,8 +157,8 @@ class SubscribePubSubTest < MiniTest::Unit::TestCase
157
157
  unless @mock_pubsub
158
158
  @mock_pubsub = MiniTest::Mock.new
159
159
  @mock_pubsub.expect(:node?, true, ['game_13'])
160
- @mock_pubsub.expect(:subscribed?, false, ['game_13', 'alice@wonderland.lit/tea'])
161
- @mock_pubsub.expect(:subscribe, nil, ['game_13', 'alice@wonderland.lit/tea'])
160
+ @mock_pubsub.expect(:subscribed?, false, ['game_13', Vines::JID.new('alice@wonderland.lit/tea')])
161
+ @mock_pubsub.expect(:subscribe, nil, ['game_13', Vines::JID.new('alice@wonderland.lit/tea')])
162
162
  end
163
163
  @mock_pubsub
164
164
  end
@@ -112,7 +112,7 @@ class UnsubscribePubSubTest < MiniTest::Unit::TestCase
112
112
  unless @mock_pubsub
113
113
  @mock_pubsub = MiniTest::Mock.new
114
114
  @mock_pubsub.expect(:node?, true, ['game_13'])
115
- @mock_pubsub.expect(:subscribed?, false, ['game_13', 'alice@wonderland.lit/tea'])
115
+ @mock_pubsub.expect(:subscribed?, false, ['game_13', Vines::JID.new('alice@wonderland.lit/tea')])
116
116
  end
117
117
  @mock_pubsub
118
118
  end
@@ -156,8 +156,8 @@ class UnsubscribePubSubTest < MiniTest::Unit::TestCase
156
156
  unless @mock_pubsub
157
157
  @mock_pubsub = MiniTest::Mock.new
158
158
  @mock_pubsub.expect(:node?, true, ['game_13'])
159
- @mock_pubsub.expect(:subscribed?, true, ['game_13', 'alice@wonderland.lit/tea'])
160
- @mock_pubsub.expect(:unsubscribe, nil, ['game_13', 'alice@wonderland.lit/tea'])
159
+ @mock_pubsub.expect(:subscribed?, true, ['game_13', Vines::JID.new('alice@wonderland.lit/tea')])
160
+ @mock_pubsub.expect(:unsubscribe, nil, ['game_13', Vines::JID.new('alice@wonderland.lit/tea')])
161
161
  end
162
162
  @mock_pubsub
163
163
  end
@@ -13,7 +13,14 @@ class StorageTest < MiniTest::Unit::TestCase
13
13
  def initialize(found_user=nil)
14
14
  @found_user = found_user
15
15
  @authenticate_calls = @find_user_calls = @save_user_calls = 0
16
- @ldap = MiniTest::Mock.new
16
+ @ldap = Class.new do
17
+ attr_accessor :user, :auth
18
+ def authenticate(username, password)
19
+ @auth ||= []
20
+ @auth << [username, password]
21
+ @user
22
+ end
23
+ end.new
17
24
  end
18
25
 
19
26
  def authenticate(username, password)
@@ -40,20 +47,20 @@ class StorageTest < MiniTest::Unit::TestCase
40
47
  assert_equal 0, storage.authenticate_calls
41
48
  assert_equal 0, storage.find_user_calls
42
49
  assert_equal 0, storage.save_user_calls
43
- assert storage.ldap.verify
50
+ assert_nil storage.ldap.auth # ldap never called
44
51
  end
45
52
  end
46
53
 
47
54
  def test_authenticate_with_ldap_bad_password
48
55
  StorageTests::EMLoop.new do
49
56
  storage = MockLdapStorage.new
50
- storage.ldap.expect(:authenticate, nil, [ALICE, 'bogus'])
57
+ storage.ldap.user = nil
51
58
  user = storage.authenticate(ALICE, 'bogus')
52
59
  assert_nil user
53
60
  assert_equal 0, storage.authenticate_calls
54
61
  assert_equal 0, storage.find_user_calls
55
62
  assert_equal 0, storage.save_user_calls
56
- assert storage.ldap.verify
63
+ assert_equal [ALICE, 'bogus'], storage.ldap.auth.first
57
64
  end
58
65
  end
59
66
 
@@ -61,14 +68,14 @@ class StorageTest < MiniTest::Unit::TestCase
61
68
  StorageTests::EMLoop.new do
62
69
  alice = Vines::User.new(:jid => ALICE)
63
70
  storage = MockLdapStorage.new(alice)
64
- storage.ldap.expect(:authenticate, alice, [ALICE, 'secr3t'])
71
+ storage.ldap.user = alice
65
72
  user = storage.authenticate(ALICE, 'secr3t')
66
73
  refute_nil user
67
74
  assert_equal ALICE, user.jid.to_s
68
75
  assert_equal 0, storage.authenticate_calls
69
76
  assert_equal 1, storage.find_user_calls
70
77
  assert_equal 0, storage.save_user_calls
71
- assert storage.ldap.verify
78
+ assert_equal [ALICE, 'secr3t'], storage.ldap.auth.first
72
79
  end
73
80
  end
74
81
 
@@ -76,14 +83,14 @@ class StorageTest < MiniTest::Unit::TestCase
76
83
  StorageTests::EMLoop.new do
77
84
  alice = Vines::User.new(:jid => ALICE)
78
85
  storage = MockLdapStorage.new
79
- storage.ldap.expect(:authenticate, alice, [ALICE, 'secr3t'])
86
+ storage.ldap.user = alice
80
87
  user = storage.authenticate(ALICE, 'secr3t')
81
88
  refute_nil user
82
89
  assert_equal ALICE, user.jid.to_s
83
90
  assert_equal 0, storage.authenticate_calls
84
91
  assert_equal 1, storage.find_user_calls
85
92
  assert_equal 1, storage.save_user_calls
86
- assert storage.ldap.verify
93
+ assert_equal [ALICE, 'secr3t'], storage.ldap.auth.first
87
94
  end
88
95
  end
89
96
  end
@@ -0,0 +1,131 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'minitest/autorun'
5
+
6
+ describe Vines::Store do
7
+ before do
8
+ dir = 'conf/certs'
9
+
10
+ domain, key = certificate('wonderland.lit')
11
+ File.open("#{dir}/wonderland.lit.crt", 'w') {|f| f.write(domain) }
12
+ File.open("#{dir}/wonderland.lit.key", 'w') {|f| f.write(key) }
13
+
14
+ wildcard, key = certificate('*.wonderland.lit')
15
+ File.open("#{dir}/wildcard.lit.crt", 'w') {|f| f.write(wildcard) }
16
+ File.open("#{dir}/wildcard.lit.key", 'w') {|f| f.write(key) }
17
+
18
+ @store = Vines::Store.new('conf/certs')
19
+ end
20
+
21
+ after do
22
+ %w[wonderland.lit.crt wonderland.lit.key wildcard.lit.crt wildcard.lit.key].each do |f|
23
+ name = "conf/certs/#{f}"
24
+ File.delete(name) if File.exists?(name)
25
+ end
26
+ end
27
+
28
+ it 'parses certificate files' do
29
+ refute @store.certs.empty?
30
+ assert_equal OpenSSL::X509::Certificate, @store.certs.first.class
31
+ end
32
+
33
+ it 'ignores expired certificates' do
34
+ assert @store.certs.all? {|c| c.not_after > Time.new }
35
+ end
36
+
37
+ describe 'files_for_domain' do
38
+ it 'handles invalid input' do
39
+ assert_nil @store.files_for_domain(nil)
40
+ assert_nil @store.files_for_domain('')
41
+ end
42
+
43
+ it 'finds files by name' do
44
+ refute_nil @store.files_for_domain('wonderland.lit')
45
+ cert, key = @store.files_for_domain('wonderland.lit')
46
+ assert_certificate_matches_key cert, key
47
+ assert_equal 'wonderland.lit.crt', File.basename(cert)
48
+ assert_equal 'wonderland.lit.key', File.basename(key)
49
+ end
50
+
51
+ it 'finds files for wildcard' do
52
+ refute_nil @store.files_for_domain('foo.wonderland.lit')
53
+ cert, key = @store.files_for_domain('foo.wonderland.lit')
54
+ assert_certificate_matches_key cert, key
55
+ assert_equal 'wildcard.lit.crt', File.basename(cert)
56
+ assert_equal 'wildcard.lit.key', File.basename(key)
57
+ end
58
+ end
59
+
60
+ describe 'domain?' do
61
+ it 'handles invalid input' do
62
+ cert, key = certificate('wonderland.lit')
63
+ refute @store.domain?(nil, nil)
64
+ refute @store.domain?(cert, nil)
65
+ refute @store.domain?(cert, '')
66
+ refute @store.domain?(nil, '')
67
+ assert @store.domain?(cert, 'wonderland.lit')
68
+ end
69
+
70
+ it 'verifies certificate subject domains' do
71
+ cert, key = certificate('wonderland.lit')
72
+ refute @store.domain?(cert, 'bogus')
73
+ refute @store.domain?(cert, 'www.wonderland.lit')
74
+ assert @store.domain?(cert, 'wonderland.lit')
75
+ end
76
+
77
+ it 'verifies certificate subject alt domains' do
78
+ cert, key = certificate('wonderland.lit', 'www.wonderland.lit')
79
+ refute @store.domain?(cert, 'bogus')
80
+ refute @store.domain?(cert, 'tea.wonderland.lit')
81
+ assert @store.domain?(cert, 'www.wonderland.lit')
82
+ assert @store.domain?(cert, 'wonderland.lit')
83
+ end
84
+
85
+ it 'verifies certificate wildcard domains' do
86
+ cert, key = certificate('wonderland.lit', '*.wonderland.lit')
87
+ refute @store.domain?(cert, 'bogus')
88
+ refute @store.domain?(cert, 'one.two.wonderland.lit')
89
+ assert @store.domain?(cert, 'tea.wonderland.lit')
90
+ assert @store.domain?(cert, 'www.wonderland.lit')
91
+ assert @store.domain?(cert, 'wonderland.lit')
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ def assert_certificate_matches_key(cert, key)
98
+ refute_nil cert
99
+ refute_nil key
100
+ cert = OpenSSL::X509::Certificate.new(File.read(cert))
101
+ key = OpenSSL::PKey::RSA.new(File.read(key))
102
+ assert_equal cert.public_key.to_s, key.public_key.to_s
103
+ end
104
+
105
+ def certificate(domain, altname=nil)
106
+ # use small key so tests are fast
107
+ key = OpenSSL::PKey::RSA.generate(256)
108
+
109
+ name = OpenSSL::X509::Name.parse("/C=US/ST=Colorado/L=Denver/O=Test/CN=#{domain}")
110
+ cert = OpenSSL::X509::Certificate.new
111
+ cert.version = 2
112
+ cert.subject = name
113
+ cert.issuer = name
114
+ cert.serial = Time.now.to_i
115
+ cert.public_key = key.public_key
116
+ cert.not_before = Time.now
117
+ cert.not_after = Time.now + 3600
118
+
119
+ if altname
120
+ factory = OpenSSL::X509::ExtensionFactory.new
121
+ factory.subject_certificate = cert
122
+ factory.issuer_certificate = cert
123
+ cert.extensions = [
124
+ %w[subjectKeyIdentifier hash],
125
+ %w[subjectAltName] << [domain, altname].map {|n| "DNS:#{n}" }.join(',')
126
+ ].map {|k, v| factory.create_ext(k, v) }
127
+ end
128
+
129
+ [cert.to_pem, key.to_pem]
130
+ end
131
+ end
@@ -13,12 +13,12 @@ class ClientReadyTest < MiniTest::Unit::TestCase
13
13
  if node.name == 'bogus'
14
14
  nil
15
15
  else
16
- MiniTest::Mock.new.tap do |stanza|
17
- stanza.expect(:process, nil)
18
- stanza.expect(:validate_to, nil)
19
- stanza.expect(:validate_from, nil)
20
- ClientReadyTest::STANZAS << stanza
21
- end
16
+ stanza = MiniTest::Mock.new
17
+ stanza.expect(:process, nil)
18
+ stanza.expect(:validate_to, nil)
19
+ stanza.expect(:validate_from, nil)
20
+ ClientReadyTest::STANZAS << stanza
21
+ stanza
22
22
  end
23
23
  end
24
24
  end
@@ -73,7 +73,7 @@ class ComponentReadyTest < MiniTest::Unit::TestCase
73
73
  @recipient.expect(:user, Vines::User.new(:jid => 'hatter@wonderland.lit'))
74
74
  @recipient.expect(:write, nil, [node])
75
75
 
76
- @stream.expect(:connected_resources, [@recipient], ['hatter@wonderland.lit'])
76
+ @stream.expect(:connected_resources, [@recipient], [Vines::JID.new('hatter@wonderland.lit')])
77
77
 
78
78
  @state.node(node)
79
79
  assert @stream.verify
@@ -1,40 +1,42 @@
1
1
  # encoding: UTF-8
2
2
 
3
+ require 'tmpdir'
3
4
  require 'vines'
4
5
  require 'minitest/autorun'
5
6
 
6
- class HttpReadyTest < MiniTest::Unit::TestCase
7
- def setup
7
+ describe Vines::Stream::Http::Ready do
8
+ before do
8
9
  @stream = MiniTest::Mock.new
9
10
  @state = Vines::Stream::Http::Ready.new(@stream, nil)
10
11
  end
11
12
 
12
- def test_missing_body_raises_error
13
+ it "raises when body element is missing" do
13
14
  node = node('<presence type="unavailable"/>')
14
15
  @stream.expect(:valid_session?, true, [nil])
15
- assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
16
+ -> { @state.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
16
17
  end
17
18
 
18
- def test_body_with_missing_namespace_raises_error
19
+ it "raises when namespace is missing" do
19
20
  node = node('<body rid="42" sid="12"/>')
20
21
  @stream.expect(:valid_session?, true, ['12'])
21
22
  assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
23
+ -> { @state.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
22
24
  end
23
25
 
24
- def test_missing_rid_raises_error
26
+ it "raises when rid attribute is missing" do
25
27
  node = node('<body xmlns="http://jabber.org/protocol/httpbind" sid="12"/>')
26
28
  @stream.expect(:valid_session?, true, ['12'])
27
- assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
29
+ -> { @state.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
28
30
  end
29
31
 
30
- def test_invalid_session_raises_error
32
+ it "raises when session id is invalid" do
31
33
  @stream.expect(:valid_session?, false, ['12'])
32
34
  node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12"/>')
33
- assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
35
+ -> { @state.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
34
36
  assert @stream.verify
35
37
  end
36
38
 
37
- def test_valid_body_processes
39
+ it "processes when body element is empty" do
38
40
  node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12"/>')
39
41
  @stream.expect(:valid_session?, true, ['12'])
40
42
  @stream.expect(:parse_body, [], [node])
@@ -42,7 +44,40 @@ class HttpReadyTest < MiniTest::Unit::TestCase
42
44
  assert @stream.verify
43
45
  end
44
46
 
45
- def test_terminate
47
+ it "processes all stanzas in one body element" do
48
+ alice = Vines::User.new(jid: 'alice@wonderland.lit')
49
+ hatter = Vines::User.new(jid: 'hatter@wonderland.lit')
50
+
51
+ config = Vines::Config.new do
52
+ host 'wonderland.lit' do
53
+ storage(:fs) { dir Dir.tmpdir }
54
+ end
55
+ end
56
+
57
+ bogus = node('<message type="bogus">raises stanza error</message>')
58
+ ok = node('<message to="hatter@wonderland.lit">but processes this message</message>')
59
+ node = node(%Q{<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12">#{bogus}#{ok}</body>})
60
+
61
+ raises = Vines::Stanza.from_node(bogus, @stream)
62
+ processes = Vines::Stanza.from_node(ok, @stream)
63
+
64
+ recipient = MiniTest::Mock.new
65
+ recipient.expect(:user, hatter)
66
+ recipient.expect(:write, nil, [Vines::Stanza::Message])
67
+
68
+ @stream.expect(:valid_session?, true, ['12'])
69
+ @stream.expect(:parse_body, [raises, processes], [node])
70
+ @stream.expect(:error, nil, [Vines::StanzaErrors::BadRequest])
71
+ @stream.expect(:config, config)
72
+ @stream.expect(:user, alice)
73
+ @stream.expect(:connected_resources, [recipient], [hatter.jid])
74
+
75
+ @state.node(node)
76
+ assert @stream.verify
77
+ assert recipient.verify
78
+ end
79
+
80
+ it "terminates the session" do
46
81
  node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12" type="terminate"/>')
47
82
  @stream.expect(:valid_session?, true, ['12'])
48
83
  @stream.expect(:parse_body, [], [node])