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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +19 -0
- data/README.md +7 -0
- data/Rakefile +23 -0
- data/bin/vines +4 -0
- data/conf/certs/README +39 -0
- data/conf/certs/ca-bundle.crt +3895 -0
- data/conf/config.rb +42 -0
- data/lib/vines/cli.rb +132 -0
- data/lib/vines/cluster/connection.rb +26 -0
- data/lib/vines/cluster/publisher.rb +55 -0
- data/lib/vines/cluster/pubsub.rb +92 -0
- data/lib/vines/cluster/sessions.rb +125 -0
- data/lib/vines/cluster/subscriber.rb +108 -0
- data/lib/vines/cluster.rb +246 -0
- data/lib/vines/command/bcrypt.rb +12 -0
- data/lib/vines/command/cert.rb +50 -0
- data/lib/vines/command/init.rb +68 -0
- data/lib/vines/command/ldap.rb +38 -0
- data/lib/vines/command/restart.rb +12 -0
- data/lib/vines/command/schema.rb +24 -0
- data/lib/vines/command/start.rb +28 -0
- data/lib/vines/command/stop.rb +18 -0
- data/lib/vines/config/host.rb +125 -0
- data/lib/vines/config/port.rb +132 -0
- data/lib/vines/config/pubsub.rb +108 -0
- data/lib/vines/config.rb +223 -0
- data/lib/vines/contact.rb +111 -0
- data/lib/vines/daemon.rb +78 -0
- data/lib/vines/error.rb +150 -0
- data/lib/vines/jid.rb +95 -0
- data/lib/vines/kit.rb +23 -0
- data/lib/vines/log.rb +24 -0
- data/lib/vines/router.rb +179 -0
- data/lib/vines/stanza/iq/auth.rb +18 -0
- data/lib/vines/stanza/iq/disco_info.rb +45 -0
- data/lib/vines/stanza/iq/disco_items.rb +29 -0
- data/lib/vines/stanza/iq/error.rb +16 -0
- data/lib/vines/stanza/iq/ping.rb +16 -0
- data/lib/vines/stanza/iq/private_storage.rb +83 -0
- data/lib/vines/stanza/iq/query.rb +10 -0
- data/lib/vines/stanza/iq/result.rb +16 -0
- data/lib/vines/stanza/iq/roster.rb +140 -0
- data/lib/vines/stanza/iq/session.rb +17 -0
- data/lib/vines/stanza/iq/vcard.rb +56 -0
- data/lib/vines/stanza/iq/version.rb +25 -0
- data/lib/vines/stanza/iq.rb +48 -0
- data/lib/vines/stanza/message.rb +40 -0
- data/lib/vines/stanza/presence/error.rb +23 -0
- data/lib/vines/stanza/presence/probe.rb +37 -0
- data/lib/vines/stanza/presence/subscribe.rb +42 -0
- data/lib/vines/stanza/presence/subscribed.rb +51 -0
- data/lib/vines/stanza/presence/unavailable.rb +15 -0
- data/lib/vines/stanza/presence/unsubscribe.rb +38 -0
- data/lib/vines/stanza/presence/unsubscribed.rb +38 -0
- data/lib/vines/stanza/presence.rb +141 -0
- data/lib/vines/stanza/pubsub/create.rb +39 -0
- data/lib/vines/stanza/pubsub/delete.rb +41 -0
- data/lib/vines/stanza/pubsub/publish.rb +66 -0
- data/lib/vines/stanza/pubsub/subscribe.rb +44 -0
- data/lib/vines/stanza/pubsub/unsubscribe.rb +30 -0
- data/lib/vines/stanza/pubsub.rb +22 -0
- data/lib/vines/stanza.rb +175 -0
- data/lib/vines/storage/ldap.rb +71 -0
- data/lib/vines/storage/local.rb +139 -0
- data/lib/vines/storage/null.rb +39 -0
- data/lib/vines/storage/sql.rb +138 -0
- data/lib/vines/storage.rb +239 -0
- data/lib/vines/store.rb +110 -0
- data/lib/vines/stream/client/auth.rb +74 -0
- data/lib/vines/stream/client/auth_restart.rb +29 -0
- data/lib/vines/stream/client/bind.rb +72 -0
- data/lib/vines/stream/client/bind_restart.rb +24 -0
- data/lib/vines/stream/client/closed.rb +13 -0
- data/lib/vines/stream/client/ready.rb +17 -0
- data/lib/vines/stream/client/session.rb +210 -0
- data/lib/vines/stream/client/start.rb +27 -0
- data/lib/vines/stream/client/tls.rb +38 -0
- data/lib/vines/stream/client.rb +84 -0
- data/lib/vines/stream/component/handshake.rb +26 -0
- data/lib/vines/stream/component/ready.rb +23 -0
- data/lib/vines/stream/component/start.rb +19 -0
- data/lib/vines/stream/component.rb +58 -0
- data/lib/vines/stream/http/auth.rb +22 -0
- data/lib/vines/stream/http/bind.rb +32 -0
- data/lib/vines/stream/http/bind_restart.rb +37 -0
- data/lib/vines/stream/http/ready.rb +29 -0
- data/lib/vines/stream/http/request.rb +172 -0
- data/lib/vines/stream/http/session.rb +120 -0
- data/lib/vines/stream/http/sessions.rb +65 -0
- data/lib/vines/stream/http/start.rb +23 -0
- data/lib/vines/stream/http.rb +157 -0
- data/lib/vines/stream/parser.rb +79 -0
- data/lib/vines/stream/sasl.rb +128 -0
- data/lib/vines/stream/server/auth.rb +13 -0
- data/lib/vines/stream/server/auth_restart.rb +13 -0
- data/lib/vines/stream/server/final_restart.rb +21 -0
- data/lib/vines/stream/server/outbound/auth.rb +31 -0
- data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/auth_result.rb +32 -0
- data/lib/vines/stream/server/outbound/final_features.rb +28 -0
- data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/start.rb +20 -0
- data/lib/vines/stream/server/outbound/tls.rb +30 -0
- data/lib/vines/stream/server/outbound/tls_result.rb +34 -0
- data/lib/vines/stream/server/ready.rb +24 -0
- data/lib/vines/stream/server/start.rb +13 -0
- data/lib/vines/stream/server/tls.rb +13 -0
- data/lib/vines/stream/server.rb +150 -0
- data/lib/vines/stream/state.rb +60 -0
- data/lib/vines/stream.rb +247 -0
- data/lib/vines/token_bucket.rb +55 -0
- data/lib/vines/user.rb +123 -0
- data/lib/vines/version.rb +6 -0
- data/lib/vines/xmpp_server.rb +25 -0
- data/lib/vines.rb +203 -0
- data/test/cluster/publisher_test.rb +57 -0
- data/test/cluster/sessions_test.rb +47 -0
- data/test/cluster/subscriber_test.rb +109 -0
- data/test/config/host_test.rb +369 -0
- data/test/config/pubsub_test.rb +187 -0
- data/test/config_test.rb +732 -0
- data/test/contact_test.rb +102 -0
- data/test/error_test.rb +58 -0
- data/test/ext/nokogiri.rb +14 -0
- data/test/jid_test.rb +147 -0
- data/test/kit_test.rb +31 -0
- data/test/router_test.rb +243 -0
- data/test/stanza/iq/disco_info_test.rb +78 -0
- data/test/stanza/iq/disco_items_test.rb +49 -0
- data/test/stanza/iq/private_storage_test.rb +184 -0
- data/test/stanza/iq/roster_test.rb +229 -0
- data/test/stanza/iq/session_test.rb +25 -0
- data/test/stanza/iq/vcard_test.rb +146 -0
- data/test/stanza/iq/version_test.rb +64 -0
- data/test/stanza/iq_test.rb +70 -0
- data/test/stanza/message_test.rb +126 -0
- data/test/stanza/presence/probe_test.rb +50 -0
- data/test/stanza/presence/subscribe_test.rb +83 -0
- data/test/stanza/pubsub/create_test.rb +116 -0
- data/test/stanza/pubsub/delete_test.rb +169 -0
- data/test/stanza/pubsub/publish_test.rb +309 -0
- data/test/stanza/pubsub/subscribe_test.rb +205 -0
- data/test/stanza/pubsub/unsubscribe_test.rb +148 -0
- data/test/stanza_test.rb +85 -0
- data/test/storage/ldap_test.rb +201 -0
- data/test/storage/local_test.rb +59 -0
- data/test/storage/mock_redis.rb +97 -0
- data/test/storage/null_test.rb +29 -0
- data/test/storage/storage_tests.rb +182 -0
- data/test/storage_test.rb +85 -0
- data/test/store_test.rb +130 -0
- data/test/stream/client/auth_test.rb +137 -0
- data/test/stream/client/ready_test.rb +47 -0
- data/test/stream/client/session_test.rb +27 -0
- data/test/stream/component/handshake_test.rb +52 -0
- data/test/stream/component/ready_test.rb +103 -0
- data/test/stream/component/start_test.rb +39 -0
- data/test/stream/http/auth_test.rb +70 -0
- data/test/stream/http/ready_test.rb +86 -0
- data/test/stream/http/request_test.rb +209 -0
- data/test/stream/http/sessions_test.rb +49 -0
- data/test/stream/http/start_test.rb +50 -0
- data/test/stream/parser_test.rb +122 -0
- data/test/stream/sasl_test.rb +195 -0
- data/test/stream/server/auth_test.rb +61 -0
- data/test/stream/server/outbound/auth_test.rb +75 -0
- data/test/stream/server/ready_test.rb +98 -0
- data/test/test_helper.rb +42 -0
- data/test/token_bucket_test.rb +44 -0
- data/test/user_test.rb +96 -0
- data/vines.gemspec +30 -0
- 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
|
data/test/error_test.rb
ADDED
@@ -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
|
data/test/router_test.rb
ADDED
@@ -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
|