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.
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