vines 0.1.0

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 (119) hide show
  1. data/LICENSE +19 -0
  2. data/README +34 -0
  3. data/Rakefile +55 -0
  4. data/bin/vines +95 -0
  5. data/conf/certs/README +32 -0
  6. data/conf/certs/ca-bundle.crt +3987 -0
  7. data/conf/config.rb +114 -0
  8. data/lib/vines.rb +155 -0
  9. data/lib/vines/command/bcrypt.rb +12 -0
  10. data/lib/vines/command/cert.rb +49 -0
  11. data/lib/vines/command/init.rb +58 -0
  12. data/lib/vines/command/ldap.rb +35 -0
  13. data/lib/vines/command/restart.rb +12 -0
  14. data/lib/vines/command/schema.rb +24 -0
  15. data/lib/vines/command/start.rb +28 -0
  16. data/lib/vines/command/stop.rb +18 -0
  17. data/lib/vines/config.rb +191 -0
  18. data/lib/vines/contact.rb +99 -0
  19. data/lib/vines/daemon.rb +78 -0
  20. data/lib/vines/error.rb +150 -0
  21. data/lib/vines/jid.rb +56 -0
  22. data/lib/vines/kit.rb +23 -0
  23. data/lib/vines/router.rb +125 -0
  24. data/lib/vines/stanza.rb +55 -0
  25. data/lib/vines/stanza/iq.rb +50 -0
  26. data/lib/vines/stanza/iq/auth.rb +18 -0
  27. data/lib/vines/stanza/iq/disco_info.rb +25 -0
  28. data/lib/vines/stanza/iq/disco_items.rb +23 -0
  29. data/lib/vines/stanza/iq/error.rb +16 -0
  30. data/lib/vines/stanza/iq/ping.rb +16 -0
  31. data/lib/vines/stanza/iq/query.rb +10 -0
  32. data/lib/vines/stanza/iq/result.rb +16 -0
  33. data/lib/vines/stanza/iq/roster.rb +153 -0
  34. data/lib/vines/stanza/iq/session.rb +22 -0
  35. data/lib/vines/stanza/iq/vcard.rb +58 -0
  36. data/lib/vines/stanza/message.rb +41 -0
  37. data/lib/vines/stanza/presence.rb +119 -0
  38. data/lib/vines/stanza/presence/error.rb +23 -0
  39. data/lib/vines/stanza/presence/probe.rb +38 -0
  40. data/lib/vines/stanza/presence/subscribe.rb +66 -0
  41. data/lib/vines/stanza/presence/subscribed.rb +64 -0
  42. data/lib/vines/stanza/presence/unavailable.rb +15 -0
  43. data/lib/vines/stanza/presence/unsubscribe.rb +57 -0
  44. data/lib/vines/stanza/presence/unsubscribed.rb +50 -0
  45. data/lib/vines/storage.rb +216 -0
  46. data/lib/vines/storage/couchdb.rb +119 -0
  47. data/lib/vines/storage/ldap.rb +59 -0
  48. data/lib/vines/storage/local.rb +66 -0
  49. data/lib/vines/storage/redis.rb +108 -0
  50. data/lib/vines/storage/sql.rb +174 -0
  51. data/lib/vines/store.rb +51 -0
  52. data/lib/vines/stream.rb +198 -0
  53. data/lib/vines/stream/client.rb +131 -0
  54. data/lib/vines/stream/client/auth.rb +94 -0
  55. data/lib/vines/stream/client/auth_restart.rb +33 -0
  56. data/lib/vines/stream/client/bind.rb +58 -0
  57. data/lib/vines/stream/client/bind_restart.rb +25 -0
  58. data/lib/vines/stream/client/closed.rb +13 -0
  59. data/lib/vines/stream/client/ready.rb +15 -0
  60. data/lib/vines/stream/client/start.rb +27 -0
  61. data/lib/vines/stream/client/tls.rb +37 -0
  62. data/lib/vines/stream/component.rb +53 -0
  63. data/lib/vines/stream/component/handshake.rb +25 -0
  64. data/lib/vines/stream/component/ready.rb +24 -0
  65. data/lib/vines/stream/component/start.rb +19 -0
  66. data/lib/vines/stream/http.rb +111 -0
  67. data/lib/vines/stream/http/http_request.rb +22 -0
  68. data/lib/vines/stream/http/http_state.rb +139 -0
  69. data/lib/vines/stream/http/http_states.rb +53 -0
  70. data/lib/vines/stream/parser.rb +78 -0
  71. data/lib/vines/stream/server.rb +126 -0
  72. data/lib/vines/stream/server/auth.rb +13 -0
  73. data/lib/vines/stream/server/auth_restart.rb +19 -0
  74. data/lib/vines/stream/server/final_restart.rb +20 -0
  75. data/lib/vines/stream/server/outbound/auth.rb +31 -0
  76. data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
  77. data/lib/vines/stream/server/outbound/auth_result.rb +28 -0
  78. data/lib/vines/stream/server/outbound/final_features.rb +27 -0
  79. data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
  80. data/lib/vines/stream/server/outbound/start.rb +20 -0
  81. data/lib/vines/stream/server/outbound/tls.rb +30 -0
  82. data/lib/vines/stream/server/outbound/tls_result.rb +31 -0
  83. data/lib/vines/stream/server/ready.rb +20 -0
  84. data/lib/vines/stream/server/start.rb +13 -0
  85. data/lib/vines/stream/server/tls.rb +13 -0
  86. data/lib/vines/stream/state.rb +55 -0
  87. data/lib/vines/token_bucket.rb +46 -0
  88. data/lib/vines/user.rb +124 -0
  89. data/lib/vines/version.rb +5 -0
  90. data/lib/vines/xmpp_server.rb +25 -0
  91. data/test/config_test.rb +396 -0
  92. data/test/error_test.rb +59 -0
  93. data/test/ext/nokogiri.rb +14 -0
  94. data/test/jid_test.rb +71 -0
  95. data/test/kit_test.rb +21 -0
  96. data/test/router_test.rb +60 -0
  97. data/test/stanza/iq/roster_test.rb +198 -0
  98. data/test/stanza/iq/session_test.rb +30 -0
  99. data/test/stanza/iq/vcard_test.rb +159 -0
  100. data/test/stanza/message_test.rb +124 -0
  101. data/test/stanza/presence/subscribe_test.rb +75 -0
  102. data/test/storage/couchdb_test.rb +102 -0
  103. data/test/storage/ldap_test.rb +207 -0
  104. data/test/storage/local_test.rb +54 -0
  105. data/test/storage/redis_test.rb +75 -0
  106. data/test/storage/sql_test.rb +55 -0
  107. data/test/storage/storage_tests.rb +134 -0
  108. data/test/storage_test.rb +90 -0
  109. data/test/stream/client/auth_test.rb +127 -0
  110. data/test/stream/client/ready_test.rb +47 -0
  111. data/test/stream/component/handshake_test.rb +46 -0
  112. data/test/stream/component/ready_test.rb +105 -0
  113. data/test/stream/component/start_test.rb +41 -0
  114. data/test/stream/parser_test.rb +121 -0
  115. data/test/stream/server/outbound/auth_test.rb +77 -0
  116. data/test/stream/server/ready_test.rb +100 -0
  117. data/test/token_bucket_test.rb +24 -0
  118. data/test/user_test.rb +64 -0
  119. metadata +318 -0
@@ -0,0 +1,47 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'minitest/mock'
5
+ require 'test/unit'
6
+
7
+ class ClientReadyTest < Test::Unit::TestCase
8
+ STANZAS = []
9
+
10
+ def setup
11
+ @stream = MiniTest::Mock.new
12
+ @state = Vines::Stream::Client::Ready.new(@stream, nil)
13
+ def @state.to_stanza(node)
14
+ if node.name == 'bogus'
15
+ nil
16
+ else
17
+ MiniTest::Mock.new.tap do |stanza|
18
+ stanza.expect(:process, nil)
19
+ ClientReadyTest::STANZAS << stanza
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ def teardown
26
+ STANZAS.clear
27
+ end
28
+
29
+ def test_good_node_processes
30
+ node = node('<message/>')
31
+ assert_nothing_raised { @state.node(node) }
32
+ assert_equal 1, STANZAS.size
33
+ assert STANZAS.map {|s| s.verify }.all?
34
+ end
35
+
36
+ def test_unsupported_stanza_type
37
+ node = node('<bogus/>')
38
+ assert_raise(Vines::StreamErrors::UnsupportedStanzaType) { @state.node(node) }
39
+ assert STANZAS.empty?
40
+ end
41
+
42
+ private
43
+
44
+ def node(xml)
45
+ Nokogiri::XML(xml).root
46
+ end
47
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'minitest/mock'
5
+ require 'test/unit'
6
+
7
+ class HandshakeTest < Test::Unit::TestCase
8
+ def setup
9
+ @stream = MiniTest::Mock.new
10
+ @state = Vines::Stream::Component::Handshake.new(@stream)
11
+ end
12
+
13
+ def test_invalid_element
14
+ node = node('<message/>')
15
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
16
+ end
17
+
18
+ def test_missing_text
19
+ @stream.expect(:secret, 'secr3t')
20
+ node = node('<handshake/>')
21
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
22
+ assert @stream.verify
23
+ end
24
+
25
+ def test_invalid_secret
26
+ @stream.expect(:secret, 'secr3t')
27
+ node = node('<handshake>bogus</handshake>')
28
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
29
+ assert @stream.verify
30
+ end
31
+
32
+ def test_valid_secret
33
+ @stream.expect(:secret, 'secr3t')
34
+ @stream.expect(:write, nil, ['<handshake/>'])
35
+ @stream.expect(:advance, nil, [Vines::Stream::Component::Ready.new(@stream)])
36
+ node = node('<handshake>secr3t</handshake>')
37
+ assert_nothing_raised { @state.node(node) }
38
+ assert @stream.verify
39
+ end
40
+
41
+ private
42
+
43
+ def node(xml)
44
+ Nokogiri::XML(xml).root
45
+ end
46
+ end
@@ -0,0 +1,105 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'ext/nokogiri'
5
+ require 'minitest/mock'
6
+ require 'test/unit'
7
+
8
+ class ComponentReadyTest < Test::Unit::TestCase
9
+ STANZAS = []
10
+
11
+ def setup
12
+ @stream = MiniTest::Mock.new
13
+ @state = Vines::Stream::Component::Ready.new(@stream, nil)
14
+ def @state.to_stanza(node)
15
+ if node.name == 'bogus'
16
+ nil
17
+ else
18
+ MiniTest::Mock.new.tap do |stanza|
19
+ if node['to'] == 'hatter@wonderland.lit'
20
+ stanza.expect(:local?, true)
21
+ else
22
+ stanza.expect(:local?, false)
23
+ stanza.expect(:route, nil)
24
+ end
25
+ ComponentReadyTest::STANZAS << stanza
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ def teardown
32
+ STANZAS.clear
33
+ end
34
+
35
+ def test_missing_to_and_from_addresses
36
+ node = node('<message/>')
37
+ assert_raise(Vines::StreamErrors::ImproperAddressing) { @state.node(node) }
38
+ assert_equal 1, STANZAS.size
39
+ assert @stream.verify
40
+ end
41
+
42
+ def test_missing_from_address
43
+ @stream.expect(:remote_domain, 'tea.wonderland.lit')
44
+ node = node(%q{<message to="hatter@wonderland.lit"/>})
45
+ assert_raise(Vines::StreamErrors::ImproperAddressing) { @state.node(node) }
46
+ assert_equal 1, STANZAS.size
47
+ assert @stream.verify
48
+ end
49
+
50
+ def test_missing_to_address
51
+ node = node(%q{<message from="alice@tea.wonderland.lit"/>})
52
+ assert_raise(Vines::StreamErrors::ImproperAddressing) { @state.node(node) }
53
+ assert_equal 1, STANZAS.size
54
+ assert @stream.verify
55
+ end
56
+
57
+ def test_invalid_from_address
58
+ @stream.expect(:remote_domain, 'tea.wonderland.lit')
59
+ node = node(%q{<message from="alice@bogus.wonderland.lit" to="hatter@wonderland.lit"/>})
60
+ assert_raise(Vines::StreamErrors::ImproperAddressing) { @state.node(node) }
61
+ assert_equal 1, STANZAS.size
62
+ assert @stream.verify
63
+ end
64
+
65
+ def test_unsupported_stanza_type
66
+ node = node('<bogus/>')
67
+ assert_raise(Vines::StreamErrors::UnsupportedStanzaType) { @state.node(node) }
68
+ assert STANZAS.empty?
69
+ assert @stream.verify
70
+ end
71
+
72
+ def test_remote_message_routes
73
+ @stream.expect(:remote_domain, 'tea.wonderland.lit')
74
+ node = node(%q{<message from="alice@tea.wonderland.lit" to="romeo@verona.lit"/>})
75
+ assert_nothing_raised { @state.node(node) }
76
+ assert_equal 1, STANZAS.size
77
+ assert STANZAS.map {|s| s.verify }.all?
78
+ assert @stream.verify
79
+ end
80
+
81
+ def test_local_message_processes
82
+ node = node(%q{<message from="alice@tea.wonderland.lit" to="hatter@wonderland.lit"/>})
83
+ @stream.expect(:remote_domain, 'tea.wonderland.lit')
84
+
85
+ @recipient = MiniTest::Mock.new
86
+ @recipient.expect(:write, nil, [node])
87
+
88
+ @router = MiniTest::Mock.new
89
+ @router.expect(:connected_resources, [@recipient], ['hatter@wonderland.lit'])
90
+ @stream.expect(:router, @router)
91
+
92
+ assert_nothing_raised { @state.node(node) }
93
+ assert_equal 1, STANZAS.size
94
+ assert STANZAS.map {|s| s.verify }.all?
95
+ assert @stream.verify
96
+ assert @router.verify
97
+ assert @recipient.verify
98
+ end
99
+
100
+ private
101
+
102
+ def node(xml)
103
+ Nokogiri::XML(xml).root
104
+ end
105
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'minitest/mock'
5
+ require 'test/unit'
6
+
7
+ class ComponentStartTest < Test::Unit::TestCase
8
+ def setup
9
+ @stream = MiniTest::Mock.new
10
+ @state = Vines::Stream::Component::Start.new(@stream)
11
+ end
12
+
13
+ def test_invalid_element
14
+ node = node('<message/>')
15
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
16
+ end
17
+
18
+ def test_missing_stream_namespace
19
+ node = node('<stream:stream/>')
20
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
21
+ end
22
+
23
+ def test_invalid_stream_namespace
24
+ node = node('<stream:stream xmlns="bogus"/>')
25
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
26
+ end
27
+
28
+ def test_valid_stream_header
29
+ node = node(%q{<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:component:accept" to="tea.wonderland.lit"/>})
30
+ @stream.expect(:start, nil, [node])
31
+ @stream.expect(:advance, nil, [Vines::Stream::Component::Handshake.new(@stream)])
32
+ assert_nothing_raised { @state.node(node) }
33
+ assert @stream.verify
34
+ end
35
+
36
+ private
37
+
38
+ def node(xml)
39
+ Nokogiri::XML(xml).root
40
+ end
41
+ end
@@ -0,0 +1,121 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'test/unit'
5
+
6
+ class ParserTest < Test::Unit::TestCase
7
+ STREAM_START = '<stream:stream to="wonderland.lit" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams">'.freeze
8
+
9
+ def setup
10
+ @events = []
11
+ @parser = Vines::Stream::Parser.new.tap do |p|
12
+ p.stream_open {|el| @events << el }
13
+ p.stream_close { @events << :close }
14
+ p.stanza {|el| @events << el }
15
+ end
16
+ end
17
+
18
+ def test_xpath_to_subclass
19
+ expected = []
20
+ stanzas = [
21
+ ['<message></message>', Vines::Stanza::Message],
22
+ ['<presence/>', Vines::Stanza::Presence],
23
+ ['<presence type="bogus"/>', Vines::Stanza::Presence],
24
+ ['<presence type="error"/>', Vines::Stanza::Presence::Error],
25
+ ['<presence type="probe"/>', Vines::Stanza::Presence::Probe],
26
+ ['<presence type="subscribe"/>', Vines::Stanza::Presence::Subscribe],
27
+ ['<presence type="subscribed"/>', Vines::Stanza::Presence::Subscribed],
28
+ ['<presence type="unavailable"/>', Vines::Stanza::Presence::Unavailable],
29
+ ['<presence type="unsubscribe"/>', Vines::Stanza::Presence::Unsubscribe],
30
+ ['<presence type="unsubscribed"/>', Vines::Stanza::Presence::Unsubscribed],
31
+ ['<iq id="42" type="get"><query xmlns="http://jabber.org/protocol/disco#info"></query></iq>', Vines::Stanza::Iq::Query::DiscoInfo],
32
+ ['<iq id="42" type="get"><query xmlns="http://jabber.org/protocol/disco#items"></query></iq>', Vines::Stanza::Iq::Query::DiscoItems],
33
+ ['<iq id="42" type="error"></iq>', Vines::Stanza::Iq::Error],
34
+ ['<iq id="42" type="get"><ping xmlns="urn:xmpp:ping"/></iq>', Vines::Stanza::Iq::Ping],
35
+ ['<iq id="42" type="result"></iq>', Vines::Stanza::Iq::Result],
36
+ ['<iq id="42" type="get"><query xmlns="jabber:iq:roster"/></iq>', Vines::Stanza::Iq::Query::Roster],
37
+ ['<iq id="42" type="set"><query xmlns="jabber:iq:roster"/></iq>', Vines::Stanza::Iq::Query::Roster],
38
+ ['<iq id="42" type="set"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>', Vines::Stanza::Iq::Session],
39
+ ['<iq id="42" type="get"><vCard xmlns="vcard-temp"/></iq>', Vines::Stanza::Iq::Vcard],
40
+ ['<iq type="get"><vCard xmlns="vcard-temp"/></iq>', Vines::Stanza::Iq],
41
+ ['<iq id="42"><vCard xmlns="vcard-temp"/></iq>', Vines::Stanza::Iq],
42
+ ['<iq><vCard xmlns="vcard-temp"/></iq>', Vines::Stanza::Iq],
43
+ ['<bogus/>', NilClass],
44
+ ]
45
+ @parser << STREAM_START
46
+ stanzas.each do |stanza, klass|
47
+ @parser << stanza
48
+ expected << klass
49
+ end
50
+ @parser << '</stream:stream>'
51
+ assert_equal 'stream', @events.shift.name
52
+ assert_equal :close, @events.pop
53
+ assert_equal expected.size, @events.size
54
+ @events.each_with_index do |ev, ix|
55
+ assert_equal expected[ix], Vines::Stanza.from_node(ev, nil).class
56
+ end
57
+ end
58
+
59
+ def test_stream_namespace_with_default_prefix
60
+ @parser << STREAM_START
61
+ assert_equal 1, @events.size
62
+ stream = @events.shift
63
+ assert_equal 'stream', stream.name
64
+ assert_not_nil stream.namespace
65
+ assert_equal 'stream', stream.namespace.prefix
66
+ assert_equal 'http://etherx.jabber.org/streams', stream.namespace.href
67
+ expected = {'xmlns' => 'jabber:client', 'xmlns:stream' => 'http://etherx.jabber.org/streams'}
68
+ assert_equal expected, stream.namespaces
69
+ end
70
+
71
+ def test_stanzas_ignore_default_namespace
72
+ @parser << STREAM_START
73
+ @parser << '<message to="alice@wonderland.lit">hello!</message>'
74
+ assert_equal 2, @events.size
75
+ @events.shift # discard stream
76
+ msg = @events.shift
77
+ assert_equal 'message', msg.name
78
+ assert msg.namespaces.empty?
79
+ assert_nil msg.namespace
80
+ end
81
+
82
+ def test_nested_elements_have_namespace
83
+ @parser << STREAM_START
84
+ @parser << %q{
85
+ <iq from='alice@wonderland.lit/tea' id='42' type='set'>
86
+ <query xmlns='jabber:iq:roster'>
87
+ <item jid='hatter@wonderland.lit' name='Mad Hatter'>
88
+ <group>Tea Party</group>
89
+ </item>
90
+ </query>
91
+ </iq>
92
+ }
93
+ assert_equal 2, @events.size
94
+ @events.shift # discard stream
95
+ iq = @events.shift
96
+ assert_equal 'iq', iq.name
97
+ assert iq.namespaces.empty?
98
+ assert_nil iq.namespace
99
+
100
+ query = iq.elements.first
101
+ assert_not_nil query.namespace
102
+ assert_nil query.namespace.prefix
103
+ assert_equal 'jabber:iq:roster', query.namespace.href
104
+ expected = {'xmlns' => 'jabber:iq:roster'}
105
+ assert_equal expected, query.namespaces
106
+ end
107
+
108
+ def test_error_stanzas_have_stream_namespace
109
+ @parser << STREAM_START
110
+ @parser << '<stream:error><not-well-formed xmlns="urn:ietf:params:xml:ns:xmpp-streams"/></stream:error>'
111
+ assert_equal 2, @events.size
112
+ @events.shift # discard stream
113
+ error = @events.shift
114
+ assert_equal 'error', error.name
115
+ assert_not_nil error.namespace
116
+ assert_equal 'stream', error.namespace.prefix
117
+ assert_equal 'http://etherx.jabber.org/streams', error.namespace.href
118
+ expected = {'xmlns:stream' => 'http://etherx.jabber.org/streams'}
119
+ assert_equal expected, error.namespaces
120
+ end
121
+ end
@@ -0,0 +1,77 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'minitest/mock'
5
+ require 'test/unit'
6
+
7
+ class OutboundAuthTest < Test::Unit::TestCase
8
+ def setup
9
+ @stream = MiniTest::Mock.new
10
+ @state = Vines::Stream::Server::Outbound::Auth.new(@stream)
11
+ end
12
+
13
+ def test_invalid_element
14
+ node = node('<message/>')
15
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
16
+ end
17
+
18
+ def test_invalid_sasl_element
19
+ node = node(%Q{<message xmlns="#{Vines::NAMESPACES[:sasl]}"/>})
20
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
21
+ end
22
+
23
+ def test_missing_namespace
24
+ node = node('<stream:features/>')
25
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
26
+ end
27
+
28
+ def test_invalid_namespace
29
+ node = node('<stream:features xmlns="bogus"/>')
30
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
31
+ end
32
+
33
+ def test_missing_mechanisms
34
+ node = node(%Q{<stream:features xmlns:stream="http://etherx.jabber.org/streams"/>})
35
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
36
+ end
37
+
38
+ def test_missing_mechanisms_namespace
39
+ node = node(%Q{<stream:features xmlns:stream="http://etherx.jabber.org/streams"><mechanisms/></stream:features>})
40
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
41
+ end
42
+
43
+ def test_missing_mechanism
44
+ mechanisms = %q{<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>}
45
+ node = node(%Q{<stream:features xmlns:stream="http://etherx.jabber.org/streams">#{mechanisms}</stream:features>})
46
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
47
+ end
48
+
49
+ def test_missing_mechanism_text
50
+ mechanisms = %q{<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism></mechanism></mechanisms>}
51
+ node = node(%Q{<stream:features xmlns:stream="http://etherx.jabber.org/streams">#{mechanisms}</stream:features>})
52
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
53
+ end
54
+
55
+ def test_invalid_mechanism_text
56
+ mechanisms = %q{<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>BOGUS</mechanism></mechanisms>}
57
+ node = node(%Q{<stream:features xmlns:stream="http://etherx.jabber.org/streams">#{mechanisms}</stream:features>})
58
+ assert_raise(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
59
+ end
60
+
61
+ def test_valid_mechanism
62
+ @stream.expect(:domain, 'wonderland.lit')
63
+ expected = %Q{<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="EXTERNAL">d29uZGVybGFuZC5saXQ=</auth>}
64
+ @stream.expect(:write, nil, [expected])
65
+ @stream.expect(:advance, nil, [Vines::Stream::Server::Outbound::AuthResult.new(@stream)])
66
+ mechanisms = %q{<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>EXTERNAL</mechanism></mechanisms>}
67
+ node = node(%Q{<stream:features xmlns:stream="http://etherx.jabber.org/streams">#{mechanisms}</stream:features>})
68
+ assert_nothing_raised { @state.node(node) }
69
+ assert @stream.verify
70
+ end
71
+
72
+ private
73
+
74
+ def node(xml)
75
+ Nokogiri::XML(xml).root
76
+ end
77
+ end
@@ -0,0 +1,100 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'minitest/mock'
5
+ require 'test/unit'
6
+
7
+ class ServerReadyTest < Test::Unit::TestCase
8
+ STANZAS = []
9
+
10
+ def setup
11
+ @stream = MiniTest::Mock.new
12
+ @state = Vines::Stream::Server::Ready.new(@stream, nil)
13
+ def @state.to_stanza(node)
14
+ if node.name == 'bogus'
15
+ nil
16
+ else
17
+ MiniTest::Mock.new.tap do |stanza|
18
+ stanza.expect(:process, nil)
19
+ stanza.class.send(:define_method, :[]) do |a|
20
+ node[a]
21
+ end
22
+ ServerReadyTest::STANZAS << stanza
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ def teardown
29
+ STANZAS.clear
30
+ end
31
+
32
+ def test_good_node_processes
33
+ @stream.expect(:remote_domain, 'wonderland.lit')
34
+ @stream.expect(:domain, 'verona.lit')
35
+ @stream.expect(:user=, nil, [Vines::User.new(:jid => 'alice@wonderland.lit')])
36
+ node = node(%Q{<message from="alice@wonderland.lit" to="romeo@verona.lit"/>})
37
+ assert_nothing_raised { @state.node(node) }
38
+ assert_equal 1, STANZAS.size
39
+ assert STANZAS.map {|s| s.verify }.all?
40
+ assert @stream.verify
41
+ end
42
+
43
+ def test_unsupported_stanza_type
44
+ node = node('<bogus/>')
45
+ assert_raise(Vines::StreamErrors::UnsupportedStanzaType) { @state.node(node) }
46
+ assert STANZAS.empty?
47
+ assert @stream.verify
48
+ end
49
+
50
+ def test_improper_addressing_missing_to
51
+ node = node(%Q{<message from="alice@wonderland.lit"/>})
52
+ assert_raise(Vines::StreamErrors::ImproperAddressing) { @state.node(node) }
53
+ assert_equal 1, STANZAS.size
54
+ assert @stream.verify
55
+ end
56
+
57
+ def test_improper_addressing_empty_to
58
+ node = node(%Q{<message from="alice@wonderland.lit" to=" "/>})
59
+ assert_raise(Vines::StreamErrors::ImproperAddressing) { @state.node(node) }
60
+ assert_equal 1, STANZAS.size
61
+ assert @stream.verify
62
+ end
63
+
64
+ def test_improper_addressing_missing_from
65
+ node = node(%Q{<message to="romeo@verona.lit"/>})
66
+ assert_raise(Vines::StreamErrors::ImproperAddressing) { @state.node(node) }
67
+ assert_equal 1, STANZAS.size
68
+ assert @stream.verify
69
+ end
70
+
71
+ def test_improper_addressing_empty_from
72
+ node = node(%Q{<message from=" " to="romeo@verona.lit"/>})
73
+ assert_raise(Vines::StreamErrors::ImproperAddressing) { @state.node(node) }
74
+ assert_equal 1, STANZAS.size
75
+ assert @stream.verify
76
+ end
77
+
78
+ def test_invalid_from
79
+ @stream.expect(:remote_domain, 'wonderland.lit')
80
+ node = node(%Q{<message from="alice@bogus.lit" to="romeo@verona.lit"/>})
81
+ assert_raise(Vines::StreamErrors::InvalidFrom) { @state.node(node) }
82
+ assert_equal 1, STANZAS.size
83
+ assert @stream.verify
84
+ end
85
+
86
+ def test_host_unknown
87
+ @stream.expect(:remote_domain, 'wonderland.lit')
88
+ @stream.expect(:domain, 'verona.lit')
89
+ node = node(%Q{<message from="alice@wonderland.lit" to="romeo@bogus.lit"/>})
90
+ assert_raise(Vines::StreamErrors::HostUnknown) { @state.node(node) }
91
+ assert_equal 1, STANZAS.size
92
+ assert @stream.verify
93
+ end
94
+
95
+ private
96
+
97
+ def node(xml)
98
+ Nokogiri::XML(xml).root
99
+ end
100
+ end