vines 0.3.2 → 0.4.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.
- data/README +5 -9
- data/Rakefile +11 -9
- data/conf/config.rb +30 -4
- 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/init.rb +21 -24
- data/lib/vines/config/host.rb +48 -8
- data/lib/vines/config/port.rb +5 -0
- data/lib/vines/config/pubsub.rb +108 -0
- data/lib/vines/config.rb +74 -20
- data/lib/vines/jid.rb +14 -0
- data/lib/vines/router.rb +69 -55
- data/lib/vines/stanza/iq/disco_info.rb +22 -9
- data/lib/vines/stanza/iq/disco_items.rb +6 -3
- data/lib/vines/stanza/iq/ping.rb +1 -1
- data/lib/vines/stanza/iq/private_storage.rb +4 -8
- data/lib/vines/stanza/iq/roster.rb +6 -14
- data/lib/vines/stanza/iq/session.rb +2 -7
- data/lib/vines/stanza/iq/vcard.rb +4 -6
- data/lib/vines/stanza/iq/version.rb +1 -1
- data/lib/vines/stanza/iq.rb +8 -10
- data/lib/vines/stanza/presence/subscribe.rb +3 -11
- data/lib/vines/stanza/presence/subscribed.rb +16 -29
- data/lib/vines/stanza/presence/unsubscribe.rb +3 -15
- data/lib/vines/stanza/presence/unsubscribed.rb +3 -16
- data/lib/vines/stanza/presence.rb +30 -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 +72 -22
- data/lib/vines/storage/couchdb.rb +46 -65
- data/lib/vines/storage/local.rb +20 -14
- data/lib/vines/storage/mongodb.rb +132 -0
- data/lib/vines/storage/null.rb +39 -0
- data/lib/vines/storage/redis.rb +61 -68
- data/lib/vines/storage/sql.rb +73 -69
- data/lib/vines/storage.rb +1 -1
- data/lib/vines/stream/client/bind.rb +2 -2
- data/lib/vines/stream/client/session.rb +71 -16
- data/lib/vines/stream/component/handshake.rb +1 -0
- data/lib/vines/stream/component/ready.rb +2 -2
- data/lib/vines/stream/http/session.rb +2 -0
- data/lib/vines/stream/http.rb +0 -6
- data/lib/vines/stream/server/final_restart.rb +1 -0
- data/lib/vines/stream/server/outbound/final_features.rb +1 -0
- data/lib/vines/stream/server/ready.rb +6 -2
- data/lib/vines/stream/server.rb +4 -3
- data/lib/vines/stream.rb +10 -6
- data/lib/vines/version.rb +1 -1
- data/lib/vines.rb +48 -22
- data/test/cluster/publisher_test.rb +45 -0
- data/test/cluster/sessions_test.rb +54 -0
- data/test/cluster/subscriber_test.rb +94 -0
- data/test/config/host_test.rb +100 -21
- data/test/config/pubsub_test.rb +181 -0
- data/test/config_test.rb +225 -43
- data/test/jid_test.rb +7 -0
- data/test/router_test.rb +181 -9
- data/test/stanza/iq/disco_info_test.rb +8 -6
- data/test/stanza/iq/disco_items_test.rb +3 -3
- data/test/stanza/iq/private_storage_test.rb +8 -19
- data/test/stanza/iq/roster_test.rb +1 -1
- data/test/stanza/iq/session_test.rb +3 -6
- data/test/stanza/iq/vcard_test.rb +6 -2
- data/test/stanza/iq/version_test.rb +3 -2
- data/test/stanza/iq_test.rb +5 -5
- data/test/stanza/message_test.rb +3 -2
- data/test/stanza/presence/probe_test.rb +2 -1
- data/test/stanza/pubsub/create_test.rb +138 -0
- data/test/stanza/pubsub/delete_test.rb +142 -0
- data/test/stanza/pubsub/publish_test.rb +373 -0
- data/test/stanza/pubsub/subscribe_test.rb +186 -0
- data/test/stanza/pubsub/unsubscribe_test.rb +179 -0
- data/test/stanza_test.rb +2 -1
- data/test/storage/local_test.rb +26 -25
- data/test/storage/mock_mongo.rb +40 -0
- data/test/storage/mock_redis.rb +98 -0
- data/test/storage/mongodb_test.rb +81 -0
- data/test/storage/null_test.rb +30 -0
- data/test/storage/redis_test.rb +3 -36
- data/test/stream/component/handshake_test.rb +4 -0
- data/test/stream/component/ready_test.rb +2 -1
- data/test/stream/server/ready_test.rb +7 -1
- data/web/404.html +5 -3
- data/web/chat/coffeescripts/chat.coffee +9 -5
- data/web/chat/javascripts/app.js +1 -1
- data/web/chat/javascripts/chat.js +14 -8
- data/web/chat/stylesheets/chat.css +4 -1
- data/web/lib/coffeescripts/button.coffee +9 -5
- data/web/lib/coffeescripts/filter.coffee +1 -1
- data/web/lib/coffeescripts/login.coffee +14 -1
- data/web/lib/coffeescripts/session.coffee +8 -11
- data/web/lib/images/dark-gray.png +0 -0
- data/web/lib/images/light-gray.png +0 -0
- data/web/lib/images/logo-large.png +0 -0
- data/web/lib/images/logo-small.png +0 -0
- data/web/lib/images/white.png +0 -0
- data/web/lib/javascripts/base.js +9 -8
- data/web/lib/javascripts/button.js +20 -12
- data/web/lib/javascripts/filter.js +1 -1
- data/web/lib/javascripts/icons.js +7 -1
- data/web/lib/javascripts/jquery.js +4 -4
- data/web/lib/javascripts/login.js +16 -2
- data/web/lib/javascripts/raphael.js +5 -7
- data/web/lib/javascripts/session.js +10 -14
- data/web/lib/stylesheets/base.css +7 -11
- data/web/lib/stylesheets/login.css +31 -27
- metadata +100 -34
@@ -0,0 +1,186 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'tmpdir'
|
4
|
+
require 'vines'
|
5
|
+
require 'ext/nokogiri'
|
6
|
+
require 'minitest/autorun'
|
7
|
+
|
8
|
+
class SubscribePubSubTest < MiniTest::Unit::TestCase
|
9
|
+
def setup
|
10
|
+
@user = Vines::User.new(jid: 'alice@wonderland.lit/tea')
|
11
|
+
@config = Vines::Config.new do
|
12
|
+
host 'wonderland.lit' do
|
13
|
+
storage(:fs) { dir Dir.tmpdir }
|
14
|
+
pubsub 'games'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
@stream = MiniTest::Mock.new
|
18
|
+
@stream.expect(:config, @config)
|
19
|
+
@stream.expect(:user, @user)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_missing_to_address_raises
|
23
|
+
node = node(%q{
|
24
|
+
<iq type='set' id='42'>
|
25
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
26
|
+
<subscribe node='game_13' jid="alice@wonderland.lit/tea"/>
|
27
|
+
</pubsub>
|
28
|
+
</iq>
|
29
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
30
|
+
|
31
|
+
@stream.expect(:domain, 'wonderland.lit')
|
32
|
+
|
33
|
+
stanza = Vines::Stanza::PubSub::Subscribe.new(node, @stream)
|
34
|
+
assert_raises(Vines::StanzaErrors::FeatureNotImplemented) { stanza.process }
|
35
|
+
assert @stream.verify
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_server_domain_to_address_raises
|
39
|
+
node = node(%q{
|
40
|
+
<iq type='set' to='wonderland.lit' id='42'>
|
41
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
42
|
+
<subscribe node='game_13' jid="alice@wonderland.lit/tea"/>
|
43
|
+
</pubsub>
|
44
|
+
</iq>
|
45
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
46
|
+
|
47
|
+
stanza = Vines::Stanza::PubSub::Subscribe.new(node, @stream)
|
48
|
+
assert_raises(Vines::StanzaErrors::FeatureNotImplemented) { stanza.process }
|
49
|
+
assert @stream.verify
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_non_pubsub_to_address_routes
|
53
|
+
node = node(%q{
|
54
|
+
<iq type='set' to='bogus.wonderland.lit' id='42'>
|
55
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
56
|
+
<subscribe node='game_13' jid="alice@wonderland.lit/tea"/>
|
57
|
+
</pubsub>
|
58
|
+
</iq>
|
59
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
60
|
+
|
61
|
+
router = MiniTest::Mock.new
|
62
|
+
router.expect(:route, nil, [node])
|
63
|
+
|
64
|
+
@stream.expect(:router, router)
|
65
|
+
|
66
|
+
stanza = Vines::Stanza::PubSub::Subscribe.new(node, @stream)
|
67
|
+
stanza.process
|
68
|
+
assert @stream.verify
|
69
|
+
assert router.verify
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_multiple_subscribe_elements_raises
|
73
|
+
node = node(%q{
|
74
|
+
<iq type='set' to='games.wonderland.lit' id='42'>
|
75
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
76
|
+
<subscribe node='game_13' jid="alice@wonderland.lit/tea"/>
|
77
|
+
<subscribe node='game_14' jid="alice@wonderland.lit/tea"/>
|
78
|
+
</pubsub>
|
79
|
+
</iq>
|
80
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
81
|
+
|
82
|
+
stanza = Vines::Stanza::PubSub::Subscribe.new(node, @stream)
|
83
|
+
assert_raises(Vines::StanzaErrors::BadRequest) { stanza.process }
|
84
|
+
assert @stream.verify
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_subscribe_missing_node_raises
|
88
|
+
node = node(%q{
|
89
|
+
<iq type='set' to='games.wonderland.lit' id='42'>
|
90
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
91
|
+
<subscribe node='game_13' jid="alice@wonderland.lit/tea"/>
|
92
|
+
</pubsub>
|
93
|
+
</iq>
|
94
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
95
|
+
|
96
|
+
stanza = Vines::Stanza::PubSub::Subscribe.new(node, @stream)
|
97
|
+
assert_raises(Vines::StanzaErrors::ItemNotFound) { stanza.process }
|
98
|
+
assert @stream.verify
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_subscribe_twice_raises
|
102
|
+
node = node(%q{
|
103
|
+
<iq type='set' to='games.wonderland.lit' id='42'>
|
104
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
105
|
+
<subscribe node='game_13' jid="alice@wonderland.lit/tea"/>
|
106
|
+
</pubsub>
|
107
|
+
</iq>
|
108
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
109
|
+
|
110
|
+
stanza = Vines::Stanza::PubSub::Subscribe.new(node, @stream)
|
111
|
+
def stanza.mock_pubsub; @mock_pubsub; end
|
112
|
+
def stanza.pubsub
|
113
|
+
unless @mock_pubsub
|
114
|
+
@mock_pubsub = MiniTest::Mock.new
|
115
|
+
@mock_pubsub.expect(:node?, true, ['game_13'])
|
116
|
+
@mock_pubsub.expect(:subscribed?, true, ['game_13', 'alice@wonderland.lit/tea'])
|
117
|
+
end
|
118
|
+
@mock_pubsub
|
119
|
+
end
|
120
|
+
assert_raises(Vines::StanzaErrors::PolicyViolation) { stanza.process }
|
121
|
+
assert @stream.verify
|
122
|
+
assert stanza.mock_pubsub.verify
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_subscribe_illegal_jid_raises
|
126
|
+
node = node(%q{
|
127
|
+
<iq type='set' to='games.wonderland.lit' id='42'>
|
128
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
129
|
+
<subscribe node='game_13' jid="not_alice@wonderland.lit/tea"/>
|
130
|
+
</pubsub>
|
131
|
+
</iq>
|
132
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
133
|
+
|
134
|
+
stanza = Vines::Stanza::PubSub::Subscribe.new(node, @stream)
|
135
|
+
assert_raises(Vines::StanzaErrors::BadRequest) { stanza.process }
|
136
|
+
assert @stream.verify
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_good_stanza_processes
|
140
|
+
node = node(%q{
|
141
|
+
<iq type='set' to='games.wonderland.lit' id='42'>
|
142
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
143
|
+
<subscribe node='game_13' jid="alice@wonderland.lit/tea"/>
|
144
|
+
</pubsub>
|
145
|
+
</iq>
|
146
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
147
|
+
|
148
|
+
def @stream.nodes; @nodes; end
|
149
|
+
def @stream.write(node)
|
150
|
+
@nodes ||= []
|
151
|
+
@nodes << node
|
152
|
+
end
|
153
|
+
|
154
|
+
stanza = Vines::Stanza::PubSub::Subscribe.new(node, @stream)
|
155
|
+
def stanza.mock_pubsub; @mock_pubsub; end
|
156
|
+
def stanza.pubsub
|
157
|
+
unless @mock_pubsub
|
158
|
+
@mock_pubsub = MiniTest::Mock.new
|
159
|
+
@mock_pubsub.expect(:node?, true, ['game_13'])
|
160
|
+
@mock_pubsub.expect(:subscribed?, false, ['game_13', 'alice@wonderland.lit/tea'])
|
161
|
+
@mock_pubsub.expect(:subscribe, nil, ['game_13', 'alice@wonderland.lit/tea'])
|
162
|
+
end
|
163
|
+
@mock_pubsub
|
164
|
+
end
|
165
|
+
stanza.process
|
166
|
+
|
167
|
+
assert @stream.verify
|
168
|
+
assert stanza.mock_pubsub.verify
|
169
|
+
assert_equal 1, @stream.nodes.size
|
170
|
+
|
171
|
+
expected = node(%q{
|
172
|
+
<iq from="games.wonderland.lit" id="42" to="alice@wonderland.lit/tea" type="result">
|
173
|
+
<pubsub xmlns="http://jabber.org/protocol/pubsub">
|
174
|
+
<subscription node="game_13" jid="alice@wonderland.lit/tea" subscription="subscribed"/>
|
175
|
+
</pubsub>
|
176
|
+
</iq>
|
177
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
178
|
+
assert_equal expected, @stream.nodes[0]
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
def node(xml)
|
184
|
+
Nokogiri::XML(xml).root
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'tmpdir'
|
4
|
+
require 'vines'
|
5
|
+
require 'ext/nokogiri'
|
6
|
+
require 'minitest/autorun'
|
7
|
+
|
8
|
+
class UnsubscribePubSubTest < MiniTest::Unit::TestCase
|
9
|
+
def setup
|
10
|
+
@user = Vines::User.new(jid: 'alice@wonderland.lit/tea')
|
11
|
+
@config = Vines::Config.new do
|
12
|
+
host 'wonderland.lit' do
|
13
|
+
storage(:fs) { dir Dir.tmpdir }
|
14
|
+
pubsub 'games'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
@stream = MiniTest::Mock.new
|
18
|
+
@stream.expect(:config, @config)
|
19
|
+
@stream.expect(:user, @user)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_missing_to_address_raises
|
23
|
+
node = node(%q{
|
24
|
+
<iq type='set' id='42'>
|
25
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
26
|
+
<unsubscribe node='game_13' jid="alice@wonderland.lit/tea"/>
|
27
|
+
</pubsub>
|
28
|
+
</iq>
|
29
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
30
|
+
|
31
|
+
@stream.expect(:domain, 'wonderland.lit')
|
32
|
+
|
33
|
+
stanza = Vines::Stanza::PubSub::Unsubscribe.new(node, @stream)
|
34
|
+
assert_raises(Vines::StanzaErrors::FeatureNotImplemented) { stanza.process }
|
35
|
+
assert @stream.verify
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_server_domain_to_address_raises
|
39
|
+
node = node(%q{
|
40
|
+
<iq type='set' to='wonderland.lit' id='42'>
|
41
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
42
|
+
<unsubscribe node='game_13' jid="alice@wonderland.lit/tea"/>
|
43
|
+
</pubsub>
|
44
|
+
</iq>
|
45
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
46
|
+
|
47
|
+
stanza = Vines::Stanza::PubSub::Unsubscribe.new(node, @stream)
|
48
|
+
assert_raises(Vines::StanzaErrors::FeatureNotImplemented) { stanza.process }
|
49
|
+
assert @stream.verify
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_non_pubsub_to_address_routes
|
53
|
+
node = node(%q{
|
54
|
+
<iq type='set' to='bogus.wonderland.lit' id='42'>
|
55
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
56
|
+
<unsubscribe node='game_13' jid="alice@wonderland.lit/tea"/>
|
57
|
+
</pubsub>
|
58
|
+
</iq>
|
59
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
60
|
+
|
61
|
+
router = MiniTest::Mock.new
|
62
|
+
router.expect(:route, nil, [node])
|
63
|
+
@stream.expect(:router, router)
|
64
|
+
|
65
|
+
stanza = Vines::Stanza::PubSub::Unsubscribe.new(node, @stream)
|
66
|
+
stanza.process
|
67
|
+
assert @stream.verify
|
68
|
+
assert router.verify
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_multiple_unsubscribe_elements_raises
|
72
|
+
node = node(%q{
|
73
|
+
<iq type='set' to='games.wonderland.lit' id='42'>
|
74
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
75
|
+
<unsubscribe node='game_13' jid="alice@wonderland.lit/tea"/>
|
76
|
+
<unsubscribe node='game_14' jid="alice@wonderland.lit/tea"/>
|
77
|
+
</pubsub>
|
78
|
+
</iq>
|
79
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
80
|
+
|
81
|
+
stanza = Vines::Stanza::PubSub::Unsubscribe.new(node, @stream)
|
82
|
+
assert_raises(Vines::StanzaErrors::BadRequest) { stanza.process }
|
83
|
+
assert @stream.verify
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_unsubscribe_missing_node_raises
|
87
|
+
node = node(%q{
|
88
|
+
<iq type='set' to='games.wonderland.lit' id='42'>
|
89
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
90
|
+
<unsubscribe node='game_13' jid="alice@wonderland.lit/tea"/>
|
91
|
+
</pubsub>
|
92
|
+
</iq>
|
93
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
94
|
+
|
95
|
+
stanza = Vines::Stanza::PubSub::Unsubscribe.new(node, @stream)
|
96
|
+
assert_raises(Vines::StanzaErrors::ItemNotFound) { stanza.process }
|
97
|
+
assert @stream.verify
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_unsubscribe_without_subscription_raises
|
101
|
+
node = node(%q{
|
102
|
+
<iq type='set' to='games.wonderland.lit' id='42'>
|
103
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
104
|
+
<unsubscribe node='game_13' jid="alice@wonderland.lit/tea"/>
|
105
|
+
</pubsub>
|
106
|
+
</iq>
|
107
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
108
|
+
|
109
|
+
stanza = Vines::Stanza::PubSub::Unsubscribe.new(node, @stream)
|
110
|
+
def stanza.mock_pubsub; @mock_pubsub; end
|
111
|
+
def stanza.pubsub
|
112
|
+
unless @mock_pubsub
|
113
|
+
@mock_pubsub = MiniTest::Mock.new
|
114
|
+
@mock_pubsub.expect(:node?, true, ['game_13'])
|
115
|
+
@mock_pubsub.expect(:subscribed?, false, ['game_13', 'alice@wonderland.lit/tea'])
|
116
|
+
end
|
117
|
+
@mock_pubsub
|
118
|
+
end
|
119
|
+
assert_raises(Vines::StanzaErrors::UnexpectedRequest) { stanza.process }
|
120
|
+
assert @stream.verify
|
121
|
+
assert stanza.mock_pubsub.verify
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_unsubscribe_illegal_jid_raises
|
125
|
+
node = node(%q{
|
126
|
+
<iq type='set' to='games.wonderland.lit' id='42'>
|
127
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
128
|
+
<unsubscribe node='game_13' jid="not_alice@wonderland.lit/tea"/>
|
129
|
+
</pubsub>
|
130
|
+
</iq>
|
131
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
132
|
+
|
133
|
+
stanza = Vines::Stanza::PubSub::Unsubscribe.new(node, @stream)
|
134
|
+
assert_raises(Vines::StanzaErrors::Forbidden) { stanza.process }
|
135
|
+
assert @stream.verify
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_good_stanza_processes
|
139
|
+
node = node(%q{
|
140
|
+
<iq type='set' to='games.wonderland.lit' id='42'>
|
141
|
+
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
142
|
+
<unsubscribe node='game_13' jid="alice@wonderland.lit/tea"/>
|
143
|
+
</pubsub>
|
144
|
+
</iq>
|
145
|
+
}.strip.gsub(/\n|\s{2,}/, ''))
|
146
|
+
|
147
|
+
def @stream.nodes; @nodes; end
|
148
|
+
def @stream.write(node)
|
149
|
+
@nodes ||= []
|
150
|
+
@nodes << node
|
151
|
+
end
|
152
|
+
|
153
|
+
stanza = Vines::Stanza::PubSub::Unsubscribe.new(node, @stream)
|
154
|
+
def stanza.mock_pubsub; @mock_pubsub; end
|
155
|
+
def stanza.pubsub
|
156
|
+
unless @mock_pubsub
|
157
|
+
@mock_pubsub = MiniTest::Mock.new
|
158
|
+
@mock_pubsub.expect(:node?, true, ['game_13'])
|
159
|
+
@mock_pubsub.expect(:subscribed?, true, ['game_13', 'alice@wonderland.lit/tea'])
|
160
|
+
@mock_pubsub.expect(:unsubscribe, nil, ['game_13', 'alice@wonderland.lit/tea'])
|
161
|
+
end
|
162
|
+
@mock_pubsub
|
163
|
+
end
|
164
|
+
stanza.process
|
165
|
+
|
166
|
+
assert @stream.verify
|
167
|
+
assert stanza.mock_pubsub.verify
|
168
|
+
assert_equal 1, @stream.nodes.size
|
169
|
+
|
170
|
+
expected = node(%q{<iq from="games.wonderland.lit" id="42" to="alice@wonderland.lit/tea" type="result"/>})
|
171
|
+
assert_equal expected, @stream.nodes[0]
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
def node(xml)
|
177
|
+
Nokogiri::XML(xml).root
|
178
|
+
end
|
179
|
+
end
|
data/test/stanza_test.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
+
require 'tmpdir'
|
3
4
|
require 'vines'
|
4
5
|
require 'ext/nokogiri'
|
5
6
|
require 'minitest/autorun'
|
@@ -11,7 +12,7 @@ class StanzaTest < MiniTest::Unit::TestCase
|
|
11
12
|
@stream = MiniTest::Mock.new
|
12
13
|
@config = Vines::Config.new do
|
13
14
|
host 'wonderland.lit' do
|
14
|
-
storage(:fs) { dir
|
15
|
+
storage(:fs) { dir Dir.tmpdir }
|
15
16
|
end
|
16
17
|
end
|
17
18
|
end
|
data/test/storage/local_test.rb
CHANGED
@@ -1,27 +1,35 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
3
|
require 'storage_tests'
|
4
|
+
require 'tmpdir'
|
4
5
|
require 'vines'
|
5
6
|
require 'minitest/autorun'
|
6
7
|
|
7
8
|
class LocalTest < MiniTest::Unit::TestCase
|
8
9
|
include StorageTests
|
9
10
|
|
11
|
+
DIR = Dir.mktmpdir
|
12
|
+
|
10
13
|
def setup
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
:
|
18
|
-
:
|
14
|
+
Dir.mkdir(DIR) unless File.exists?(DIR)
|
15
|
+
%w[user vcard fragment].each do |d|
|
16
|
+
Dir.mkdir(File.join(DIR, d))
|
17
|
+
end
|
18
|
+
|
19
|
+
files = {
|
20
|
+
:empty => "#{DIR}/user/empty@wonderland.lit",
|
21
|
+
:no_pass => "#{DIR}/user/no_password@wonderland.lit",
|
22
|
+
:clear_pass => "#{DIR}/user/clear_password@wonderland.lit",
|
23
|
+
:bcrypt => "#{DIR}/user/bcrypt_password@wonderland.lit",
|
24
|
+
:full => "#{DIR}/user/full@wonderland.lit",
|
25
|
+
:vcard => "#{DIR}/vcard/full@wonderland.lit",
|
26
|
+
:fragment => "#{DIR}/fragment/full@wonderland.lit-#{FRAGMENT_ID}"
|
19
27
|
}
|
20
|
-
File.open(
|
21
|
-
File.open(
|
22
|
-
File.open(
|
23
|
-
File.open(
|
24
|
-
File.open(
|
28
|
+
File.open(files[:empty], 'w') {|f| f.write('') }
|
29
|
+
File.open(files[:no_pass], 'w') {|f| f.write('foo: bar') }
|
30
|
+
File.open(files[:clear_pass], 'w') {|f| f.write('password: secret') }
|
31
|
+
File.open(files[:bcrypt], 'w') {|f| f.write("password: #{BCrypt::Password.create('secret')}") }
|
32
|
+
File.open(files[:full], 'w') do |f|
|
25
33
|
f.puts("password: #{BCrypt::Password.create('secret')}")
|
26
34
|
f.puts("name: Tester")
|
27
35
|
f.puts("roster:")
|
@@ -32,29 +40,22 @@ class LocalTest < MiniTest::Unit::TestCase
|
|
32
40
|
f.puts(" name: Contact2")
|
33
41
|
f.puts(" groups: [Group3, Group4]")
|
34
42
|
end
|
35
|
-
File.open(
|
36
|
-
File.open(
|
43
|
+
File.open(files[:vcard], 'w') {|f| f.write(StorageTests::VCARD.to_xml) }
|
44
|
+
File.open(files[:fragment], 'w') {|f| f.write(StorageTests::FRAGMENT.to_xml) }
|
37
45
|
end
|
38
46
|
|
39
47
|
def teardown
|
40
|
-
|
41
|
-
[
|
42
|
-
"./save_user@domain.tld-#{FRAGMENT_ID}.fragment",
|
43
|
-
*misc,
|
44
|
-
*@files.values
|
45
|
-
].each do |f|
|
46
|
-
File.delete(f) if File.exist?(f)
|
47
|
-
end
|
48
|
+
FileUtils.remove_entry_secure(DIR)
|
48
49
|
end
|
49
50
|
|
50
51
|
def storage
|
51
|
-
Vines::Storage::Local.new { dir
|
52
|
+
Vines::Storage::Local.new { dir DIR }
|
52
53
|
end
|
53
54
|
|
54
55
|
def test_init
|
55
56
|
assert_raises(RuntimeError) { Vines::Storage::Local.new {} }
|
56
57
|
assert_raises(RuntimeError) { Vines::Storage::Local.new { dir 'bogus' } }
|
57
58
|
assert_raises(RuntimeError) { Vines::Storage::Local.new { dir '/sbin' } }
|
58
|
-
Vines::Storage::Local.new { dir
|
59
|
+
Vines::Storage::Local.new { dir DIR } # shouldn't raise an error
|
59
60
|
end
|
60
61
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# A mock implementation of the Mongo::Connection class.
|
4
|
+
class MockMongo
|
5
|
+
def initialize
|
6
|
+
@collections = collections
|
7
|
+
end
|
8
|
+
|
9
|
+
def collection(name)
|
10
|
+
@collections[name]
|
11
|
+
end
|
12
|
+
|
13
|
+
def clear
|
14
|
+
@collections = collections
|
15
|
+
end
|
16
|
+
|
17
|
+
def collections
|
18
|
+
{
|
19
|
+
users: MockCollection.new,
|
20
|
+
vcards: MockCollection.new,
|
21
|
+
fragments: MockCollection.new
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
class MockCollection
|
26
|
+
def initialize
|
27
|
+
@docs = {}
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_one(doc)
|
31
|
+
id = doc['_id'] || doc[:_id]
|
32
|
+
@docs[id]
|
33
|
+
end
|
34
|
+
|
35
|
+
def save(doc, opts={})
|
36
|
+
id = doc['_id'] || doc[:_id]
|
37
|
+
@docs[id] = doc
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# A mock redis storage implementation that saves data to an in-memory Hash.
|
4
|
+
class MockRedis
|
5
|
+
attr_reader :db
|
6
|
+
|
7
|
+
# Mimic em-hiredis behavior.
|
8
|
+
def self.defer(method)
|
9
|
+
old = "_deferred_#{method}"
|
10
|
+
alias_method old, method
|
11
|
+
define_method method do |*args, &block|
|
12
|
+
result = method(old).call(*args)
|
13
|
+
deferred = EM::DefaultDeferrable.new
|
14
|
+
deferred.callback(&block) if block
|
15
|
+
EM.next_tick { deferred.succeed(result) }
|
16
|
+
deferred
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@db = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def del(key)
|
25
|
+
@db.delete(key)
|
26
|
+
end
|
27
|
+
defer :del
|
28
|
+
|
29
|
+
def get(key)
|
30
|
+
@db[key]
|
31
|
+
end
|
32
|
+
defer :get
|
33
|
+
|
34
|
+
def set(key, value)
|
35
|
+
@db[key] = value
|
36
|
+
end
|
37
|
+
defer :set
|
38
|
+
|
39
|
+
def hget(key, field)
|
40
|
+
@db[key][field] rescue nil
|
41
|
+
end
|
42
|
+
defer :hget
|
43
|
+
|
44
|
+
def hdel(key, field)
|
45
|
+
@db[key].delete(field) rescue nil
|
46
|
+
end
|
47
|
+
defer :hdel
|
48
|
+
|
49
|
+
def hgetall(key)
|
50
|
+
(@db[key] || {}).map do |k, v|
|
51
|
+
[k, v]
|
52
|
+
end.flatten
|
53
|
+
end
|
54
|
+
defer :hgetall
|
55
|
+
|
56
|
+
def hset(key, field, value)
|
57
|
+
@db[key] ||= {}
|
58
|
+
@db[key][field] = value
|
59
|
+
end
|
60
|
+
defer :hset
|
61
|
+
|
62
|
+
def hmset(key, *args)
|
63
|
+
@db[key] = Hash[*args]
|
64
|
+
end
|
65
|
+
defer :hmset
|
66
|
+
|
67
|
+
def sadd(key, obj)
|
68
|
+
@db[key] ||= Set.new
|
69
|
+
@db[key] << obj
|
70
|
+
end
|
71
|
+
defer :sadd
|
72
|
+
|
73
|
+
def srem(key, obj)
|
74
|
+
@db[key].delete(obj) rescue nil
|
75
|
+
end
|
76
|
+
defer :srem
|
77
|
+
|
78
|
+
def smembers
|
79
|
+
@db[key].to_a rescue []
|
80
|
+
end
|
81
|
+
defer :smembers
|
82
|
+
|
83
|
+
def flushdb
|
84
|
+
@db.clear
|
85
|
+
end
|
86
|
+
defer :flushdb
|
87
|
+
|
88
|
+
def multi
|
89
|
+
@transaction = true
|
90
|
+
end
|
91
|
+
defer :multi
|
92
|
+
|
93
|
+
def exec
|
94
|
+
raise 'transaction must start with multi' unless @transaction
|
95
|
+
@transaction = false
|
96
|
+
end
|
97
|
+
defer :exec
|
98
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'mock_mongo'
|
4
|
+
require 'storage_tests'
|
5
|
+
require 'vines'
|
6
|
+
require 'minitest/autorun'
|
7
|
+
|
8
|
+
class MongoDBTest < MiniTest::Unit::TestCase
|
9
|
+
include StorageTests
|
10
|
+
|
11
|
+
MOCK_MONGO = MockMongo.new
|
12
|
+
|
13
|
+
def setup
|
14
|
+
EMLoop.new do
|
15
|
+
db = MOCK_MONGO
|
16
|
+
db.collection(:users).save({'_id' => 'empty@wonderland.lit'})
|
17
|
+
db.collection(:users).save({'_id' => 'no_password@wonderland.lit', 'foo' => 'bar'})
|
18
|
+
db.collection(:users).save({'_id' => 'clear_password@wonderland.lit', 'password' => 'secret'})
|
19
|
+
db.collection(:users).save({'_id' => 'bcrypt_password@wonderland.lit', 'password' => BCrypt::Password.create('secret')})
|
20
|
+
db.collection(:users).save({
|
21
|
+
'_id' => 'full@wonderland.lit',
|
22
|
+
'password' => BCrypt::Password.create('secret'),
|
23
|
+
'name' => 'Tester',
|
24
|
+
'roster' => {
|
25
|
+
'contact1@wonderland.lit' => {
|
26
|
+
'name' => 'Contact1',
|
27
|
+
'groups' => %w[Group1 Group2]
|
28
|
+
},
|
29
|
+
'contact2@wonderland.lit' => {
|
30
|
+
'name' => 'Contact2',
|
31
|
+
'groups' => %w[Group3 Group4]
|
32
|
+
}
|
33
|
+
}
|
34
|
+
})
|
35
|
+
db.collection(:vcards).save({'_id' => 'full@wonderland.lit', 'card' => VCARD.to_xml})
|
36
|
+
db.collection(:fragments).save({'_id' => "full@wonderland.lit:#{FRAGMENT_ID}", 'xml' => FRAGMENT.to_xml})
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def teardown
|
41
|
+
MOCK_MONGO.clear
|
42
|
+
end
|
43
|
+
|
44
|
+
def storage
|
45
|
+
storage = Vines::Storage::MongoDB.new do
|
46
|
+
host 'localhost', 27017
|
47
|
+
database 'xmpp_testcase'
|
48
|
+
end
|
49
|
+
def storage.db
|
50
|
+
MongoDBTest::MOCK_MONGO
|
51
|
+
end
|
52
|
+
storage
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_init
|
56
|
+
EMLoop.new do
|
57
|
+
# missing database
|
58
|
+
assert_raises(RuntimeError) { Vines::Storage::MongoDB.new {} }
|
59
|
+
# missing a host
|
60
|
+
assert_raises(RuntimeError) { Vines::Storage::MongoDB.new { database 'test' } }
|
61
|
+
# duplicate hosts
|
62
|
+
assert_raises(RuntimeError) do
|
63
|
+
Vines::Storage::MongoDB.new do
|
64
|
+
host 'localhost', 27017
|
65
|
+
host 'localhost', 27017
|
66
|
+
database 'test'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
# shouldn't raise an error
|
70
|
+
Vines::Storage::MongoDB.new do
|
71
|
+
host 'localhost', 27017
|
72
|
+
database 'test'
|
73
|
+
end
|
74
|
+
Vines::Storage::MongoDB.new do
|
75
|
+
host 'localhost', 27017
|
76
|
+
host 'localhost', 27018
|
77
|
+
database 'test'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|