vines 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. data/README +5 -9
  2. data/Rakefile +11 -9
  3. data/conf/config.rb +30 -4
  4. data/lib/vines/cluster/connection.rb +26 -0
  5. data/lib/vines/cluster/publisher.rb +55 -0
  6. data/lib/vines/cluster/pubsub.rb +92 -0
  7. data/lib/vines/cluster/sessions.rb +125 -0
  8. data/lib/vines/cluster/subscriber.rb +108 -0
  9. data/lib/vines/cluster.rb +246 -0
  10. data/lib/vines/command/init.rb +21 -24
  11. data/lib/vines/config/host.rb +48 -8
  12. data/lib/vines/config/port.rb +5 -0
  13. data/lib/vines/config/pubsub.rb +108 -0
  14. data/lib/vines/config.rb +74 -20
  15. data/lib/vines/jid.rb +14 -0
  16. data/lib/vines/router.rb +69 -55
  17. data/lib/vines/stanza/iq/disco_info.rb +22 -9
  18. data/lib/vines/stanza/iq/disco_items.rb +6 -3
  19. data/lib/vines/stanza/iq/ping.rb +1 -1
  20. data/lib/vines/stanza/iq/private_storage.rb +4 -8
  21. data/lib/vines/stanza/iq/roster.rb +6 -14
  22. data/lib/vines/stanza/iq/session.rb +2 -7
  23. data/lib/vines/stanza/iq/vcard.rb +4 -6
  24. data/lib/vines/stanza/iq/version.rb +1 -1
  25. data/lib/vines/stanza/iq.rb +8 -10
  26. data/lib/vines/stanza/presence/subscribe.rb +3 -11
  27. data/lib/vines/stanza/presence/subscribed.rb +16 -29
  28. data/lib/vines/stanza/presence/unsubscribe.rb +3 -15
  29. data/lib/vines/stanza/presence/unsubscribed.rb +3 -16
  30. data/lib/vines/stanza/presence.rb +30 -0
  31. data/lib/vines/stanza/pubsub/create.rb +39 -0
  32. data/lib/vines/stanza/pubsub/delete.rb +41 -0
  33. data/lib/vines/stanza/pubsub/publish.rb +66 -0
  34. data/lib/vines/stanza/pubsub/subscribe.rb +44 -0
  35. data/lib/vines/stanza/pubsub/unsubscribe.rb +30 -0
  36. data/lib/vines/stanza/pubsub.rb +22 -0
  37. data/lib/vines/stanza.rb +72 -22
  38. data/lib/vines/storage/couchdb.rb +46 -65
  39. data/lib/vines/storage/local.rb +20 -14
  40. data/lib/vines/storage/mongodb.rb +132 -0
  41. data/lib/vines/storage/null.rb +39 -0
  42. data/lib/vines/storage/redis.rb +61 -68
  43. data/lib/vines/storage/sql.rb +73 -69
  44. data/lib/vines/storage.rb +1 -1
  45. data/lib/vines/stream/client/bind.rb +2 -2
  46. data/lib/vines/stream/client/session.rb +71 -16
  47. data/lib/vines/stream/component/handshake.rb +1 -0
  48. data/lib/vines/stream/component/ready.rb +2 -2
  49. data/lib/vines/stream/http/session.rb +2 -0
  50. data/lib/vines/stream/http.rb +0 -6
  51. data/lib/vines/stream/server/final_restart.rb +1 -0
  52. data/lib/vines/stream/server/outbound/final_features.rb +1 -0
  53. data/lib/vines/stream/server/ready.rb +6 -2
  54. data/lib/vines/stream/server.rb +4 -3
  55. data/lib/vines/stream.rb +10 -6
  56. data/lib/vines/version.rb +1 -1
  57. data/lib/vines.rb +48 -22
  58. data/test/cluster/publisher_test.rb +45 -0
  59. data/test/cluster/sessions_test.rb +54 -0
  60. data/test/cluster/subscriber_test.rb +94 -0
  61. data/test/config/host_test.rb +100 -21
  62. data/test/config/pubsub_test.rb +181 -0
  63. data/test/config_test.rb +225 -43
  64. data/test/jid_test.rb +7 -0
  65. data/test/router_test.rb +181 -9
  66. data/test/stanza/iq/disco_info_test.rb +8 -6
  67. data/test/stanza/iq/disco_items_test.rb +3 -3
  68. data/test/stanza/iq/private_storage_test.rb +8 -19
  69. data/test/stanza/iq/roster_test.rb +1 -1
  70. data/test/stanza/iq/session_test.rb +3 -6
  71. data/test/stanza/iq/vcard_test.rb +6 -2
  72. data/test/stanza/iq/version_test.rb +3 -2
  73. data/test/stanza/iq_test.rb +5 -5
  74. data/test/stanza/message_test.rb +3 -2
  75. data/test/stanza/presence/probe_test.rb +2 -1
  76. data/test/stanza/pubsub/create_test.rb +138 -0
  77. data/test/stanza/pubsub/delete_test.rb +142 -0
  78. data/test/stanza/pubsub/publish_test.rb +373 -0
  79. data/test/stanza/pubsub/subscribe_test.rb +186 -0
  80. data/test/stanza/pubsub/unsubscribe_test.rb +179 -0
  81. data/test/stanza_test.rb +2 -1
  82. data/test/storage/local_test.rb +26 -25
  83. data/test/storage/mock_mongo.rb +40 -0
  84. data/test/storage/mock_redis.rb +98 -0
  85. data/test/storage/mongodb_test.rb +81 -0
  86. data/test/storage/null_test.rb +30 -0
  87. data/test/storage/redis_test.rb +3 -36
  88. data/test/stream/component/handshake_test.rb +4 -0
  89. data/test/stream/component/ready_test.rb +2 -1
  90. data/test/stream/server/ready_test.rb +7 -1
  91. data/web/404.html +5 -3
  92. data/web/chat/coffeescripts/chat.coffee +9 -5
  93. data/web/chat/javascripts/app.js +1 -1
  94. data/web/chat/javascripts/chat.js +14 -8
  95. data/web/chat/stylesheets/chat.css +4 -1
  96. data/web/lib/coffeescripts/button.coffee +9 -5
  97. data/web/lib/coffeescripts/filter.coffee +1 -1
  98. data/web/lib/coffeescripts/login.coffee +14 -1
  99. data/web/lib/coffeescripts/session.coffee +8 -11
  100. data/web/lib/images/dark-gray.png +0 -0
  101. data/web/lib/images/light-gray.png +0 -0
  102. data/web/lib/images/logo-large.png +0 -0
  103. data/web/lib/images/logo-small.png +0 -0
  104. data/web/lib/images/white.png +0 -0
  105. data/web/lib/javascripts/base.js +9 -8
  106. data/web/lib/javascripts/button.js +20 -12
  107. data/web/lib/javascripts/filter.js +1 -1
  108. data/web/lib/javascripts/icons.js +7 -1
  109. data/web/lib/javascripts/jquery.js +4 -4
  110. data/web/lib/javascripts/login.js +16 -2
  111. data/web/lib/javascripts/raphael.js +5 -7
  112. data/web/lib/javascripts/session.js +10 -14
  113. data/web/lib/stylesheets/base.css +7 -11
  114. data/web/lib/stylesheets/login.css +31 -27
  115. 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
@@ -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
- @files = {
12
- :empty => './empty@wonderland.lit.user',
13
- :no_pass => './no_password@wonderland.lit.user',
14
- :clear_pass => './clear_password@wonderland.lit.user',
15
- :bcrypt => './bcrypt_password@wonderland.lit.user',
16
- :full => './full@wonderland.lit.user',
17
- :vcard => './full@wonderland.lit.vcard',
18
- :fragment => "./full@wonderland.lit-#{FRAGMENT_ID}.fragment"
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(@files[:empty], 'w') {|f| f.write('') }
21
- File.open(@files[:no_pass], 'w') {|f| f.write('foo: bar') }
22
- File.open(@files[:clear_pass], 'w') {|f| f.write('password: secret') }
23
- File.open(@files[:bcrypt], 'w') {|f| f.write("password: #{BCrypt::Password.create('secret')}") }
24
- File.open(@files[:full], 'w') do |f|
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(@files[:vcard], 'w') {|f| f.write(StorageTests::VCARD.to_xml) }
36
- File.open(@files[:fragment], 'w') {|f| f.write(StorageTests::FRAGMENT.to_xml) }
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
- misc = %w[user vcard].map {|ext| "./save_user@domain.tld.#{ext}" }
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 '.' } # shouldn't raise an error
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