vines 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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])