diaspora-vines 0.1.2

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 (174) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/LICENSE +19 -0
  4. data/README.md +7 -0
  5. data/Rakefile +23 -0
  6. data/bin/vines +4 -0
  7. data/conf/certs/README +39 -0
  8. data/conf/certs/ca-bundle.crt +3895 -0
  9. data/conf/config.rb +42 -0
  10. data/lib/vines/cli.rb +132 -0
  11. data/lib/vines/cluster/connection.rb +26 -0
  12. data/lib/vines/cluster/publisher.rb +55 -0
  13. data/lib/vines/cluster/pubsub.rb +92 -0
  14. data/lib/vines/cluster/sessions.rb +125 -0
  15. data/lib/vines/cluster/subscriber.rb +108 -0
  16. data/lib/vines/cluster.rb +246 -0
  17. data/lib/vines/command/bcrypt.rb +12 -0
  18. data/lib/vines/command/cert.rb +50 -0
  19. data/lib/vines/command/init.rb +68 -0
  20. data/lib/vines/command/ldap.rb +38 -0
  21. data/lib/vines/command/restart.rb +12 -0
  22. data/lib/vines/command/schema.rb +24 -0
  23. data/lib/vines/command/start.rb +28 -0
  24. data/lib/vines/command/stop.rb +18 -0
  25. data/lib/vines/config/host.rb +125 -0
  26. data/lib/vines/config/port.rb +132 -0
  27. data/lib/vines/config/pubsub.rb +108 -0
  28. data/lib/vines/config.rb +223 -0
  29. data/lib/vines/contact.rb +111 -0
  30. data/lib/vines/daemon.rb +78 -0
  31. data/lib/vines/error.rb +150 -0
  32. data/lib/vines/jid.rb +95 -0
  33. data/lib/vines/kit.rb +23 -0
  34. data/lib/vines/log.rb +24 -0
  35. data/lib/vines/router.rb +179 -0
  36. data/lib/vines/stanza/iq/auth.rb +18 -0
  37. data/lib/vines/stanza/iq/disco_info.rb +45 -0
  38. data/lib/vines/stanza/iq/disco_items.rb +29 -0
  39. data/lib/vines/stanza/iq/error.rb +16 -0
  40. data/lib/vines/stanza/iq/ping.rb +16 -0
  41. data/lib/vines/stanza/iq/private_storage.rb +83 -0
  42. data/lib/vines/stanza/iq/query.rb +10 -0
  43. data/lib/vines/stanza/iq/result.rb +16 -0
  44. data/lib/vines/stanza/iq/roster.rb +140 -0
  45. data/lib/vines/stanza/iq/session.rb +17 -0
  46. data/lib/vines/stanza/iq/vcard.rb +56 -0
  47. data/lib/vines/stanza/iq/version.rb +25 -0
  48. data/lib/vines/stanza/iq.rb +48 -0
  49. data/lib/vines/stanza/message.rb +40 -0
  50. data/lib/vines/stanza/presence/error.rb +23 -0
  51. data/lib/vines/stanza/presence/probe.rb +37 -0
  52. data/lib/vines/stanza/presence/subscribe.rb +42 -0
  53. data/lib/vines/stanza/presence/subscribed.rb +51 -0
  54. data/lib/vines/stanza/presence/unavailable.rb +15 -0
  55. data/lib/vines/stanza/presence/unsubscribe.rb +38 -0
  56. data/lib/vines/stanza/presence/unsubscribed.rb +38 -0
  57. data/lib/vines/stanza/presence.rb +141 -0
  58. data/lib/vines/stanza/pubsub/create.rb +39 -0
  59. data/lib/vines/stanza/pubsub/delete.rb +41 -0
  60. data/lib/vines/stanza/pubsub/publish.rb +66 -0
  61. data/lib/vines/stanza/pubsub/subscribe.rb +44 -0
  62. data/lib/vines/stanza/pubsub/unsubscribe.rb +30 -0
  63. data/lib/vines/stanza/pubsub.rb +22 -0
  64. data/lib/vines/stanza.rb +175 -0
  65. data/lib/vines/storage/ldap.rb +71 -0
  66. data/lib/vines/storage/local.rb +139 -0
  67. data/lib/vines/storage/null.rb +39 -0
  68. data/lib/vines/storage/sql.rb +138 -0
  69. data/lib/vines/storage.rb +239 -0
  70. data/lib/vines/store.rb +110 -0
  71. data/lib/vines/stream/client/auth.rb +74 -0
  72. data/lib/vines/stream/client/auth_restart.rb +29 -0
  73. data/lib/vines/stream/client/bind.rb +72 -0
  74. data/lib/vines/stream/client/bind_restart.rb +24 -0
  75. data/lib/vines/stream/client/closed.rb +13 -0
  76. data/lib/vines/stream/client/ready.rb +17 -0
  77. data/lib/vines/stream/client/session.rb +210 -0
  78. data/lib/vines/stream/client/start.rb +27 -0
  79. data/lib/vines/stream/client/tls.rb +38 -0
  80. data/lib/vines/stream/client.rb +84 -0
  81. data/lib/vines/stream/component/handshake.rb +26 -0
  82. data/lib/vines/stream/component/ready.rb +23 -0
  83. data/lib/vines/stream/component/start.rb +19 -0
  84. data/lib/vines/stream/component.rb +58 -0
  85. data/lib/vines/stream/http/auth.rb +22 -0
  86. data/lib/vines/stream/http/bind.rb +32 -0
  87. data/lib/vines/stream/http/bind_restart.rb +37 -0
  88. data/lib/vines/stream/http/ready.rb +29 -0
  89. data/lib/vines/stream/http/request.rb +172 -0
  90. data/lib/vines/stream/http/session.rb +120 -0
  91. data/lib/vines/stream/http/sessions.rb +65 -0
  92. data/lib/vines/stream/http/start.rb +23 -0
  93. data/lib/vines/stream/http.rb +157 -0
  94. data/lib/vines/stream/parser.rb +79 -0
  95. data/lib/vines/stream/sasl.rb +128 -0
  96. data/lib/vines/stream/server/auth.rb +13 -0
  97. data/lib/vines/stream/server/auth_restart.rb +13 -0
  98. data/lib/vines/stream/server/final_restart.rb +21 -0
  99. data/lib/vines/stream/server/outbound/auth.rb +31 -0
  100. data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
  101. data/lib/vines/stream/server/outbound/auth_result.rb +32 -0
  102. data/lib/vines/stream/server/outbound/final_features.rb +28 -0
  103. data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
  104. data/lib/vines/stream/server/outbound/start.rb +20 -0
  105. data/lib/vines/stream/server/outbound/tls.rb +30 -0
  106. data/lib/vines/stream/server/outbound/tls_result.rb +34 -0
  107. data/lib/vines/stream/server/ready.rb +24 -0
  108. data/lib/vines/stream/server/start.rb +13 -0
  109. data/lib/vines/stream/server/tls.rb +13 -0
  110. data/lib/vines/stream/server.rb +150 -0
  111. data/lib/vines/stream/state.rb +60 -0
  112. data/lib/vines/stream.rb +247 -0
  113. data/lib/vines/token_bucket.rb +55 -0
  114. data/lib/vines/user.rb +123 -0
  115. data/lib/vines/version.rb +6 -0
  116. data/lib/vines/xmpp_server.rb +25 -0
  117. data/lib/vines.rb +203 -0
  118. data/test/cluster/publisher_test.rb +57 -0
  119. data/test/cluster/sessions_test.rb +47 -0
  120. data/test/cluster/subscriber_test.rb +109 -0
  121. data/test/config/host_test.rb +369 -0
  122. data/test/config/pubsub_test.rb +187 -0
  123. data/test/config_test.rb +732 -0
  124. data/test/contact_test.rb +102 -0
  125. data/test/error_test.rb +58 -0
  126. data/test/ext/nokogiri.rb +14 -0
  127. data/test/jid_test.rb +147 -0
  128. data/test/kit_test.rb +31 -0
  129. data/test/router_test.rb +243 -0
  130. data/test/stanza/iq/disco_info_test.rb +78 -0
  131. data/test/stanza/iq/disco_items_test.rb +49 -0
  132. data/test/stanza/iq/private_storage_test.rb +184 -0
  133. data/test/stanza/iq/roster_test.rb +229 -0
  134. data/test/stanza/iq/session_test.rb +25 -0
  135. data/test/stanza/iq/vcard_test.rb +146 -0
  136. data/test/stanza/iq/version_test.rb +64 -0
  137. data/test/stanza/iq_test.rb +70 -0
  138. data/test/stanza/message_test.rb +126 -0
  139. data/test/stanza/presence/probe_test.rb +50 -0
  140. data/test/stanza/presence/subscribe_test.rb +83 -0
  141. data/test/stanza/pubsub/create_test.rb +116 -0
  142. data/test/stanza/pubsub/delete_test.rb +169 -0
  143. data/test/stanza/pubsub/publish_test.rb +309 -0
  144. data/test/stanza/pubsub/subscribe_test.rb +205 -0
  145. data/test/stanza/pubsub/unsubscribe_test.rb +148 -0
  146. data/test/stanza_test.rb +85 -0
  147. data/test/storage/ldap_test.rb +201 -0
  148. data/test/storage/local_test.rb +59 -0
  149. data/test/storage/mock_redis.rb +97 -0
  150. data/test/storage/null_test.rb +29 -0
  151. data/test/storage/storage_tests.rb +182 -0
  152. data/test/storage_test.rb +85 -0
  153. data/test/store_test.rb +130 -0
  154. data/test/stream/client/auth_test.rb +137 -0
  155. data/test/stream/client/ready_test.rb +47 -0
  156. data/test/stream/client/session_test.rb +27 -0
  157. data/test/stream/component/handshake_test.rb +52 -0
  158. data/test/stream/component/ready_test.rb +103 -0
  159. data/test/stream/component/start_test.rb +39 -0
  160. data/test/stream/http/auth_test.rb +70 -0
  161. data/test/stream/http/ready_test.rb +86 -0
  162. data/test/stream/http/request_test.rb +209 -0
  163. data/test/stream/http/sessions_test.rb +49 -0
  164. data/test/stream/http/start_test.rb +50 -0
  165. data/test/stream/parser_test.rb +122 -0
  166. data/test/stream/sasl_test.rb +195 -0
  167. data/test/stream/server/auth_test.rb +61 -0
  168. data/test/stream/server/outbound/auth_test.rb +75 -0
  169. data/test/stream/server/ready_test.rb +98 -0
  170. data/test/test_helper.rb +42 -0
  171. data/test/token_bucket_test.rb +44 -0
  172. data/test/user_test.rb +96 -0
  173. data/vines.gemspec +30 -0
  174. metadata +387 -0
@@ -0,0 +1,102 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Contact do
6
+ subject do
7
+ Vines::Contact.new(
8
+ jid: 'alice@wonderland.lit',
9
+ name: "Alice",
10
+ groups: %w[Friends Buddies],
11
+ subscription: 'from')
12
+ end
13
+
14
+ describe 'contact equality checks' do
15
+ let(:alice) { Vines::Contact.new(jid: 'alice@wonderland.lit') }
16
+ let(:hatter) { Vines::Contact.new(jid: 'hatter@wonderland.lit') }
17
+
18
+ it 'uses class in equality check' do
19
+ (subject <=> 42).must_be_nil
20
+ end
21
+
22
+ it 'is equal to itself' do
23
+ assert subject == subject
24
+ assert subject.eql?(subject)
25
+ assert subject.hash == subject.hash
26
+ end
27
+
28
+ it 'is equal to another contact with the same jid' do
29
+ assert subject == alice
30
+ assert subject.eql?(alice)
31
+ assert subject.hash == alice.hash
32
+ end
33
+
34
+ it 'is not equal to a different jid' do
35
+ refute subject == hatter
36
+ refute subject.eql?(hatter)
37
+ refute subject.hash == hatter.hash
38
+ end
39
+ end
40
+
41
+ describe 'initialize' do
42
+ it 'raises when not given a jid' do
43
+ -> { Vines::Contact.new }.must_raise ArgumentError
44
+ -> { Vines::Contact.new(jid: '') }.must_raise ArgumentError
45
+ end
46
+
47
+ it 'accepts a domain-only jid' do
48
+ contact = Vines::Contact.new(jid: 'tea.wonderland.lit')
49
+ contact.jid.to_s.must_equal 'tea.wonderland.lit'
50
+ end
51
+ end
52
+
53
+ describe '#to_roster_xml' do
54
+ let(:expected) do
55
+ node(%q{
56
+ <item jid="alice@wonderland.lit" name="Alice" subscription="from">
57
+ <group>Buddies</group>
58
+ <group>Friends</group>
59
+ </item>
60
+ })
61
+ end
62
+
63
+ it 'sorts group names' do
64
+ subject.to_roster_xml.must_equal expected
65
+ end
66
+ end
67
+
68
+ describe '#send_roster_push' do
69
+ let(:recipient) { MiniTest::Mock.new }
70
+ let(:expected) do
71
+ node(%q{
72
+ <iq to="hatter@wonderland.lit" type="set">
73
+ <query xmlns="jabber:iq:roster">
74
+ <item jid="alice@wonderland.lit" name="Alice" subscription="from">
75
+ <group>Buddies</group>
76
+ <group>Friends</group>
77
+ </item>
78
+ </query>
79
+ </iq>
80
+ })
81
+ end
82
+
83
+ before do
84
+ recipient.expect :user, Vines::User.new(jid: 'hatter@wonderland.lit')
85
+ class << recipient
86
+ attr_accessor :nodes
87
+ def write(node)
88
+ @nodes ||= []
89
+ @nodes << node
90
+ end
91
+ end
92
+ end
93
+
94
+ it '' do
95
+ subject.send_roster_push(recipient)
96
+ recipient.verify
97
+ recipient.nodes.size.must_equal 1
98
+ recipient.nodes.first.remove_attribute('id') # id is random
99
+ recipient.nodes.first.must_equal expected
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::XmppError do
6
+ describe Vines::SaslErrors do
7
+ it 'does not require a text element' do
8
+ expected = %q{<failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><temporary-auth-failure/></failure>}
9
+ Vines::SaslErrors::TemporaryAuthFailure.new.to_xml.must_equal expected
10
+ end
11
+
12
+ it 'includes a text element when message is given' do
13
+ text = %q{<text xml:lang="en">busted</text>}
14
+ expected = %q{<failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><temporary-auth-failure/>%s</failure>} % text
15
+ Vines::SaslErrors::TemporaryAuthFailure.new('busted').to_xml.must_equal expected
16
+ end
17
+ end
18
+
19
+ describe Vines::StreamErrors do
20
+ it 'does not require a text element' do
21
+ expected = %q{<stream:error><internal-server-error xmlns="urn:ietf:params:xml:ns:xmpp-streams"/></stream:error>}
22
+ Vines::StreamErrors::InternalServerError.new.to_xml.must_equal expected
23
+ end
24
+
25
+ it 'includes a text element when message is given' do
26
+ text = %q{<text xmlns="urn:ietf:params:xml:ns:xmpp-streams" xml:lang="en">busted</text>}
27
+ expected = %q{<stream:error><internal-server-error xmlns="urn:ietf:params:xml:ns:xmpp-streams"/>%s</stream:error>} % text
28
+ Vines::StreamErrors::InternalServerError.new('busted').to_xml.must_equal expected
29
+ end
30
+ end
31
+
32
+ describe Vines::StanzaErrors do
33
+ it 'raises when given a bad type' do
34
+ node = node('<message/>')
35
+ -> { Vines::StanzaErrors::BadRequest.new(node, 'bogus') }.must_raise RuntimeError
36
+ end
37
+
38
+ it 'raises when given a bad stanza' do
39
+ node = node('<bogus/>')
40
+ -> { Vines::StanzaErrors::BadRequest.new(node, 'modify') }.must_raise RuntimeError
41
+ end
42
+
43
+ it 'does not require a text element' do
44
+ error = %q{<error type="modify"><bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error>}
45
+ expected = %q{<message from="hatter@wonderland.lit" to="alice@wonderland.lit" type="error">%s</message>} % error
46
+ node = node(%Q{<message from="alice@wonderland.lit" to="hatter@wonderland.lit"/>})
47
+ Vines::StanzaErrors::BadRequest.new(node, 'modify').to_xml.must_equal expected
48
+ end
49
+
50
+ it 'includes a text element when message is given' do
51
+ text = %q{<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" xml:lang="en">busted</text>}
52
+ error = %q{<error type="modify"><bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>%s</error>} % text
53
+ expected = %q{<message id="42" type="error">%s</message>} % error
54
+ node = node(%Q{<message id="42"/>})
55
+ Vines::StanzaErrors::BadRequest.new(node, 'modify', 'busted').to_xml.must_equal expected
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+
3
+ module Nokogiri
4
+ module XML
5
+ class Node
6
+ # Override equality testing so we can use MiniTest::Mock#expect with
7
+ # Nokogiri::XML::Node arguments. Node's default behavior considers
8
+ # all nodes unequal.
9
+ def ==(node)
10
+ self.to_s == node.to_s
11
+ end
12
+ end
13
+ end
14
+ end
data/test/jid_test.rb ADDED
@@ -0,0 +1,147 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::JID do
6
+ it 'handles empty input' do
7
+ [nil, ''].each do |text|
8
+ Vines::JID.new(text) # shouldn't raise an error
9
+ jid = Vines::JID.new(text)
10
+ assert_nil jid.node
11
+ assert_nil jid.resource
12
+ assert_equal '', jid.domain
13
+ assert_equal '', jid.to_s
14
+ assert_equal '', jid.bare.to_s
15
+ assert jid.empty?
16
+ refute jid.domain?
17
+ end
18
+ end
19
+
20
+ it 'raises when a jid part is too long' do
21
+ Vines::JID.new('n' * 1023) # shouldn't raise an error
22
+ assert_raises(ArgumentError) { Vines::JID.new('n' * 1024) }
23
+ assert_raises(ArgumentError) { Vines::JID.new('n', 'd' * 1024) }
24
+ assert_raises(ArgumentError) { Vines::JID.new('n', 'd', 'r' * 1024) }
25
+ Vines::JID.new('n' * 1023, 'd' * 1023, 'r' * 1023) # shouldn't raise an error
26
+ end
27
+
28
+ it 'correctly handles domain only jids' do
29
+ jid = Vines::JID.new('wonderland.lit')
30
+ assert_equal 'wonderland.lit', jid.to_s
31
+ assert_equal 'wonderland.lit', jid.domain
32
+ assert_nil jid.node
33
+ assert_nil jid.resource
34
+ assert_equal jid, jid.bare
35
+ assert jid.domain?
36
+ refute jid.empty?
37
+ end
38
+
39
+ it 'correctly handles bare jid components' do
40
+ jid = Vines::JID.new('alice', 'wonderland.lit')
41
+ assert_equal 'alice@wonderland.lit', jid.to_s
42
+ assert_equal 'wonderland.lit', jid.domain
43
+ assert_equal 'alice', jid.node
44
+ assert_nil jid.resource
45
+ assert_equal jid, jid.bare
46
+ refute jid.domain?
47
+ refute jid.empty?
48
+ end
49
+
50
+ it 'correctly parses bare jids' do
51
+ jid = Vines::JID.new('alice@wonderland.lit')
52
+ assert_equal 'alice@wonderland.lit', jid.to_s
53
+ assert_equal 'wonderland.lit', jid.domain
54
+ assert_equal 'alice', jid.node
55
+ assert_nil jid.resource
56
+ assert_equal jid, jid.bare
57
+ refute jid.domain?
58
+ refute jid.empty?
59
+ end
60
+
61
+ it 'correctly handles full jid components' do
62
+ jid = Vines::JID.new('alice', 'wonderland.lit', 'tea')
63
+ assert_equal 'alice@wonderland.lit/tea', jid.to_s
64
+ assert_equal 'wonderland.lit', jid.domain
65
+ assert_equal 'alice', jid.node
66
+ assert_equal 'tea', jid.resource
67
+ refute_equal jid, jid.bare
68
+ refute jid.domain?
69
+ refute jid.empty?
70
+ end
71
+
72
+ it 'correctly parses full jids' do
73
+ jid = Vines::JID.new('alice@wonderland.lit/tea')
74
+ assert_equal 'alice@wonderland.lit/tea', jid.to_s
75
+ assert_equal 'wonderland.lit', jid.domain
76
+ assert_equal 'alice', jid.node
77
+ assert_equal 'tea', jid.resource
78
+ refute_equal jid, jid.bare
79
+ refute jid.domain?
80
+ refute jid.empty?
81
+ end
82
+
83
+ it 'accepts separator characters in resource part' do
84
+ jid = Vines::JID.new('alice@wonderland.lit/foo/bar@blarg test')
85
+ assert_equal 'alice', jid.node
86
+ assert_equal 'wonderland.lit', jid.domain
87
+ assert_equal 'foo/bar@blarg test', jid.resource
88
+ end
89
+
90
+ it 'accepts separator characters in resource part with missing node part' do
91
+ jid = Vines::JID.new('wonderland.lit/foo/bar@blarg')
92
+ assert_nil jid.node
93
+ assert_equal 'wonderland.lit', jid.domain
94
+ assert_equal 'foo/bar@blarg', jid.resource
95
+ refute jid.domain?
96
+ end
97
+
98
+ it 'accepts strange characters in node part' do
99
+ jid = Vines::JID.new(%q{nasty!#$%()*+,-.;=?[\]^_`{|}~node@example.com})
100
+ jid.node.must_equal %q{nasty!#$%()*+,-.;=?[\]^_`{|}~node}
101
+ jid.domain.must_equal 'example.com'
102
+ jid.resource.must_be_nil
103
+ end
104
+
105
+ it 'accepts strange characters in resource part' do
106
+ jid = Vines::JID.new(%q{node@example.com/repulsive !#"$%&'()*+,-./:;<=>?@[\]^_`{|}~resource})
107
+ jid.node.must_equal 'node'
108
+ jid.domain.must_equal 'example.com'
109
+ jid.resource.must_equal %q{repulsive !#"$%&'()*+,-./:;<=>?@[\]^_`{|}~resource}
110
+ end
111
+
112
+ it 'rejects empty jid parts' do
113
+ assert_raises(ArgumentError) { Vines::JID.new('@wonderland.lit') }
114
+ assert_raises(ArgumentError) { Vines::JID.new('wonderland.lit/') }
115
+ assert_raises(ArgumentError) { Vines::JID.new('@') }
116
+ assert_raises(ArgumentError) { Vines::JID.new('alice@') }
117
+ assert_raises(ArgumentError) { Vines::JID.new('/') }
118
+ assert_raises(ArgumentError) { Vines::JID.new('/res') }
119
+ assert_raises(ArgumentError) { Vines::JID.new('@/') }
120
+ end
121
+
122
+ it 'rejects invalid characters' do
123
+ assert_raises(ArgumentError) { Vines::JID.new(%q{alice"s@wonderland.lit}) }
124
+ assert_raises(ArgumentError) { Vines::JID.new(%q{alice&s@wonderland.lit}) }
125
+ assert_raises(ArgumentError) { Vines::JID.new(%q{alice's@wonderland.lit}) }
126
+ assert_raises(ArgumentError) { Vines::JID.new(%q{alice:s@wonderland.lit}) }
127
+ assert_raises(ArgumentError) { Vines::JID.new(%q{alice<s@wonderland.lit}) }
128
+ assert_raises(ArgumentError) { Vines::JID.new(%q{alice>s@wonderland.lit}) }
129
+ assert_raises(ArgumentError) { Vines::JID.new("alice\u0000s@wonderland.lit") }
130
+ assert_raises(ArgumentError) { Vines::JID.new("alice\ts@wonderland.lit") }
131
+ assert_raises(ArgumentError) { Vines::JID.new("alice\rs@wonderland.lit") }
132
+ assert_raises(ArgumentError) { Vines::JID.new("alice\ns@wonderland.lit") }
133
+ assert_raises(ArgumentError) { Vines::JID.new("alice\vs@wonderland.lit") }
134
+ assert_raises(ArgumentError) { Vines::JID.new("alice\fs@wonderland.lit") }
135
+ assert_raises(ArgumentError) { Vines::JID.new(" alice@wonderland.lit") }
136
+ assert_raises(ArgumentError) { Vines::JID.new("alice@wonderland.lit ") }
137
+ assert_raises(ArgumentError) { Vines::JID.new("alice s@wonderland.lit") }
138
+ assert_raises(ArgumentError) { Vines::JID.new("alice@w onderland.lit") }
139
+ assert_raises(ArgumentError) { Vines::JID.new("alice@w\tonderland.lit") }
140
+ assert_raises(ArgumentError) { Vines::JID.new("alice@w\ronderland.lit") }
141
+ assert_raises(ArgumentError) { Vines::JID.new("alice@w\nonderland.lit") }
142
+ assert_raises(ArgumentError) { Vines::JID.new("alice@w\vonderland.lit") }
143
+ assert_raises(ArgumentError) { Vines::JID.new("alice@w\fonderland.lit") }
144
+ assert_raises(ArgumentError) { Vines::JID.new("alice@w\u0000onderland.lit") }
145
+ assert_raises(ArgumentError) { Vines::JID.new("alice@wonderland.lit/\u0000res") }
146
+ end
147
+ end
data/test/kit_test.rb ADDED
@@ -0,0 +1,31 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Kit do
6
+ describe '#hmac' do
7
+ it 'generates a SHA-512 HMAC' do
8
+ Vines::Kit.hmac('secret', 'username').length.must_equal 128
9
+ assert_equal Vines::Kit.hmac('s1', 'u1'), Vines::Kit.hmac('s1', 'u1')
10
+ refute_equal Vines::Kit.hmac('s1', 'u1'), Vines::Kit.hmac('s2', 'u1')
11
+ refute_equal Vines::Kit.hmac('s1', 'u1'), Vines::Kit.hmac('s1', 'u2')
12
+ end
13
+ end
14
+
15
+ describe '#uuid' do
16
+ it 'returns a random uuid' do
17
+ ids = Array.new(1000) { Vines::Kit.uuid }
18
+ assert ids.all? {|id| !id.nil? }
19
+ assert ids.all? {|id| id.length == 36 }
20
+ assert ids.all? {|id| id.match(/\w{8}-\w{4}-[4]\w{3}-[89ab]\w{3}-\w{12}/) }
21
+ ids.uniq.length.must_equal ids.length
22
+ end
23
+ end
24
+
25
+ describe '#auth_token' do
26
+ it 'returns a random 128 character token' do
27
+ Vines::Kit.auth_token.wont_equal Vines::Kit.auth_token
28
+ Vines::Kit.auth_token.length.must_equal 128
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,243 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Router do
6
+ subject { Vines::Router.new(config) }
7
+ let(:alice) { Vines::JID.new('alice@wonderland.lit/tea') }
8
+ let(:hatter) { 'hatter@wonderland.lit/cake' }
9
+ let(:romeo) { 'romeo@verona.lit/party' }
10
+ let(:config) do
11
+ Vines::Config.new do
12
+ host 'wonderland.lit' do
13
+ storage(:fs) { dir Dir.tmpdir }
14
+ components 'tea' => 'secr3t'
15
+ end
16
+ end
17
+ end
18
+
19
+ describe '#connected_resources' do
20
+ let(:cake) { 'alice@wonderland.lit/cake' }
21
+ let(:stream1) { stream(alice) }
22
+ let(:stream2) { stream(cake) }
23
+
24
+ it 'is empty before any streams are connected' do
25
+ subject.connected_resources(alice, alice).size.must_equal 0
26
+ subject.connected_resources(cake, alice).size.must_equal 0
27
+ subject.size.must_equal 0
28
+ end
29
+
30
+ it 'returns only one stream matching full jid' do
31
+ subject << stream1
32
+ subject << stream2
33
+
34
+ streams = subject.connected_resources(alice, alice)
35
+ streams.size.must_equal 1
36
+ streams.first.user.jid.must_equal alice
37
+
38
+ streams = subject.connected_resources(cake, alice)
39
+ streams.size.must_equal 1
40
+ streams.first.user.jid.to_s.must_equal cake
41
+ end
42
+
43
+ it 'returns all streams matching bare jid' do
44
+ subject << stream1
45
+ subject << stream2
46
+
47
+ streams = subject.connected_resources(alice.bare, alice)
48
+ streams.size.must_equal 2
49
+ subject.size.must_equal 2
50
+ end
51
+ end
52
+
53
+ describe '#connected_resources with permissions' do
54
+ let(:stream1) { stream(alice) }
55
+ let(:stream2) { stream(romeo) }
56
+
57
+ before do
58
+ subject << stream1
59
+ subject << stream2
60
+ end
61
+
62
+ it 'denies access when cross domain messages is off' do
63
+ subject.connected_resources(alice, romeo).size.must_equal 0
64
+ end
65
+
66
+ it 'allows access when cross domain messages is on' do
67
+ config.vhost('wonderland.lit').cross_domain_messages true
68
+ subject.connected_resources(alice, romeo).size.must_equal 1
69
+ end
70
+ end
71
+
72
+ describe '#available_resources' do
73
+ let(:cake) { 'alice@wonderland.lit/cake' }
74
+ let(:stream1) { stream(alice) }
75
+ let(:stream2) { stream(cake) }
76
+
77
+ before do
78
+ stream1.send 'available?=', true
79
+ stream2.send 'available?=', false
80
+ end
81
+
82
+ it 'is empty before any streams are connected' do
83
+ subject.available_resources(alice, alice).size.must_equal 0
84
+ subject.available_resources(cake, alice).size.must_equal 0
85
+ subject.size.must_equal 0
86
+ end
87
+
88
+ it 'returns available streams based on bare jid, not full jid' do
89
+ subject << stream1
90
+ subject << stream2
91
+
92
+ streams = [alice, cake, alice.bare].map do |jid|
93
+ subject.available_resources(jid, alice)
94
+ end.flatten
95
+
96
+ # should only have found alice's stream
97
+ streams.size.must_equal 3
98
+ streams.uniq.size.must_equal 1
99
+ streams.first.user.jid.must_equal alice
100
+
101
+ subject.size.must_equal 2
102
+ end
103
+ end
104
+
105
+ describe '#interested_resources with no streams' do
106
+ it 'is empty before any streams are connected' do
107
+ subject.interested_resources(alice, alice).size.must_equal 0
108
+ subject.interested_resources(hatter, alice).size.must_equal 0
109
+ subject.interested_resources(alice, hatter, alice).size.must_equal 0
110
+ subject.size.must_equal 0
111
+ end
112
+ end
113
+
114
+ describe '#interested_resources' do
115
+ let(:stream1) { stream(alice) }
116
+ let(:stream2) { stream(hatter) }
117
+
118
+ before do
119
+ stream1.send 'interested?=', true
120
+ stream2.send 'interested?=', false
121
+ subject << stream1
122
+ subject << stream2
123
+ end
124
+
125
+ it 'does not find streams for unauthenticated jids' do
126
+ subject.interested_resources('bogus@wonderland.lit', alice).size.must_equal 0
127
+ end
128
+
129
+ it 'finds interested streams for full jids' do
130
+ subject.interested_resources(alice, hatter, alice).size.must_equal 1
131
+ subject.interested_resources([alice, hatter], alice).size.must_equal 1
132
+ subject.interested_resources(alice, hatter, alice)[0].user.jid.must_equal alice
133
+ end
134
+
135
+ it 'does not find streams for uninterested jids' do
136
+ subject.interested_resources(hatter, alice).size.must_equal 0
137
+ subject.interested_resources([hatter], alice).size.must_equal 0
138
+ end
139
+
140
+ it 'finds interested streams for bare jids' do
141
+ subject.interested_resources(alice.bare, alice).size.must_equal 1
142
+ subject.interested_resources(alice.bare, alice)[0].user.jid.must_equal alice
143
+ end
144
+ end
145
+
146
+ describe '#delete' do
147
+ let(:stream1) { stream(alice) }
148
+ let(:stream2) { stream(hatter) }
149
+
150
+ it 'correctly adds and removes streams' do
151
+ subject.size.must_equal 0
152
+
153
+ subject << stream1
154
+ subject << stream2
155
+ subject.size.must_equal 2
156
+
157
+ subject.delete(stream2)
158
+ subject.size.must_equal 1
159
+
160
+ subject.delete(stream2)
161
+ subject.size.must_equal 1
162
+
163
+ subject.delete(stream1)
164
+ subject.size.must_equal 0
165
+ end
166
+ end
167
+
168
+ describe 'load balanced component streams' do
169
+ let(:stream1) { component('tea.wonderland.lit') }
170
+ let(:stream2) { component('tea.wonderland.lit') }
171
+ let(:stanza) { node('<message from="alice@wonderland.lit" to="tea.wonderland.lit">test</message>')}
172
+
173
+ before do
174
+ subject << stream1
175
+ subject << stream2
176
+ end
177
+
178
+ it 'must evenly distribute routed stanzas to both streams' do
179
+ 100.times { subject.route(stanza) }
180
+
181
+ (stream1.count + stream2.count).must_equal 100
182
+ stream1.count.must_be :>, 33
183
+ stream2.count.must_be :>, 33
184
+ end
185
+ end
186
+
187
+ describe 'load balanced s2s streams' do
188
+ let(:stream1) { s2s('wonderland.lit', 'verona.lit') }
189
+ let(:stream2) { s2s('wonderland.lit', 'verona.lit') }
190
+ let(:stanza) { node('<message from="alice@wonderland.lit" to="romeo@verona.lit">test</message>') }
191
+
192
+ before do
193
+ config.vhost('wonderland.lit').cross_domain_messages true
194
+ subject << stream1
195
+ subject << stream2
196
+ end
197
+
198
+ it 'must evenly distribute routed stanzas to both streams' do
199
+ 100.times { subject.route(stanza) }
200
+
201
+ (stream1.count + stream2.count).must_equal 100
202
+ stream1.count.must_be :>, 33
203
+ stream2.count.must_be :>, 33
204
+ end
205
+ end
206
+
207
+ private
208
+
209
+ def stream(jid)
210
+ OpenStruct.new.tap do |stream|
211
+ stream.send('connected?=', true)
212
+ stream.stream_type = :client
213
+ stream.user = Vines::User.new(jid: jid)
214
+ end
215
+ end
216
+
217
+ def component(jid)
218
+ OpenStruct.new.tap do |stream|
219
+ stream.stream_type = :component
220
+ stream.remote_domain = jid
221
+ stream.send('ready?=', true)
222
+ def stream.count; @count || 0; end
223
+ def stream.write(stanza)
224
+ @count ||= 0
225
+ @count += 1
226
+ end
227
+ end
228
+ end
229
+
230
+ def s2s(domain, remote_domain)
231
+ OpenStruct.new.tap do |stream|
232
+ stream.stream_type = :server
233
+ stream.domain = domain
234
+ stream.remote_domain = remote_domain
235
+ stream.send('ready?=', true)
236
+ def stream.count; @count || 0; end
237
+ def stream.write(stanza)
238
+ @count ||= 0
239
+ @count += 1
240
+ end
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,78 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stanza::Iq::DiscoInfo do
6
+ subject { Vines::Stanza::Iq::DiscoInfo.new(xml, stream) }
7
+ let(:stream) { MiniTest::Mock.new }
8
+ let(:alice) { Vines::User.new(jid: 'alice@wonderland.lit/home') }
9
+ let(:config) do
10
+ Vines::Config.new do
11
+ host 'wonderland.lit' do
12
+ storage(:fs) { dir Dir.tmpdir }
13
+ end
14
+ end
15
+ end
16
+
17
+ let(:xml) do
18
+ query = %q{<query xmlns="http://jabber.org/protocol/disco#info"/>}
19
+ node(%Q{<iq id="42" to="wonderland.lit" type="get">#{query}</iq>})
20
+ end
21
+
22
+ before do
23
+ class << stream
24
+ attr_accessor :config, :user
25
+ end
26
+ stream.config = config
27
+ stream.user = alice
28
+ end
29
+
30
+ describe 'when private storage is disabled' do
31
+ let(:expected) do
32
+ node(%Q{
33
+ <iq from="wonderland.lit" id="42" to="#{alice.jid}" type="result">
34
+ <query xmlns="http://jabber.org/protocol/disco#info">
35
+ <identity category="server" type="im"/>
36
+ <feature var="http://jabber.org/protocol/disco#info"/>
37
+ <feature var="http://jabber.org/protocol/disco#items"/>
38
+ <feature var="urn:xmpp:ping"/>
39
+ <feature var="vcard-temp"/>
40
+ <feature var="jabber:iq:version"/>
41
+ </query>
42
+ </iq>
43
+ })
44
+ end
45
+
46
+ it 'returns info stanza without the private storage feature' do
47
+ config.vhost('wonderland.lit').private_storage false
48
+ stream.expect :write, nil, [expected]
49
+ subject.process
50
+ stream.verify
51
+ end
52
+ end
53
+
54
+ describe 'when private storage is enabled' do
55
+ let(:expected) do
56
+ node(%Q{
57
+ <iq from="wonderland.lit" id="42" to="#{alice.jid}" type="result">
58
+ <query xmlns="http://jabber.org/protocol/disco#info">
59
+ <identity category="server" type="im"/>
60
+ <feature var="http://jabber.org/protocol/disco#info"/>
61
+ <feature var="http://jabber.org/protocol/disco#items"/>
62
+ <feature var="urn:xmpp:ping"/>
63
+ <feature var="vcard-temp"/>
64
+ <feature var="jabber:iq:version"/>
65
+ <feature var="jabber:iq:private"/>
66
+ </query>
67
+ </iq>
68
+ })
69
+ end
70
+
71
+ it 'announces private storage feature in info stanza result' do
72
+ config.vhost('wonderland.lit').private_storage true
73
+ stream.expect :write, nil, [expected]
74
+ subject.process
75
+ stream.verify
76
+ end
77
+ end
78
+ end