vines 0.1.0

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