lygneo-vines 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (174) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/LICENSE +19 -0
  4. data/README.md +7 -0
  5. data/Rakefile +23 -0
  6. data/bin/vines +4 -0
  7. data/conf/certs/README +39 -0
  8. data/conf/certs/ca-bundle.crt +3895 -0
  9. data/conf/config.rb +42 -0
  10. data/lib/vines/cli.rb +132 -0
  11. data/lib/vines/cluster/connection.rb +26 -0
  12. data/lib/vines/cluster/publisher.rb +55 -0
  13. data/lib/vines/cluster/pubsub.rb +92 -0
  14. data/lib/vines/cluster/sessions.rb +125 -0
  15. data/lib/vines/cluster/subscriber.rb +108 -0
  16. data/lib/vines/cluster.rb +246 -0
  17. data/lib/vines/command/bcrypt.rb +12 -0
  18. data/lib/vines/command/cert.rb +50 -0
  19. data/lib/vines/command/init.rb +68 -0
  20. data/lib/vines/command/ldap.rb +38 -0
  21. data/lib/vines/command/restart.rb +12 -0
  22. data/lib/vines/command/schema.rb +24 -0
  23. data/lib/vines/command/start.rb +28 -0
  24. data/lib/vines/command/stop.rb +18 -0
  25. data/lib/vines/config/host.rb +125 -0
  26. data/lib/vines/config/port.rb +132 -0
  27. data/lib/vines/config/pubsub.rb +108 -0
  28. data/lib/vines/config.rb +223 -0
  29. data/lib/vines/daemon.rb +78 -0
  30. data/lib/vines/error.rb +150 -0
  31. data/lib/vines/follower.rb +111 -0
  32. data/lib/vines/jid.rb +95 -0
  33. data/lib/vines/kit.rb +23 -0
  34. data/lib/vines/log.rb +24 -0
  35. data/lib/vines/router.rb +179 -0
  36. data/lib/vines/stanza/iq/auth.rb +18 -0
  37. data/lib/vines/stanza/iq/disco_info.rb +45 -0
  38. data/lib/vines/stanza/iq/disco_items.rb +29 -0
  39. data/lib/vines/stanza/iq/error.rb +16 -0
  40. data/lib/vines/stanza/iq/ping.rb +16 -0
  41. data/lib/vines/stanza/iq/private_storage.rb +83 -0
  42. data/lib/vines/stanza/iq/query.rb +10 -0
  43. data/lib/vines/stanza/iq/result.rb +16 -0
  44. data/lib/vines/stanza/iq/roster.rb +140 -0
  45. data/lib/vines/stanza/iq/session.rb +17 -0
  46. data/lib/vines/stanza/iq/vcard.rb +56 -0
  47. data/lib/vines/stanza/iq/version.rb +25 -0
  48. data/lib/vines/stanza/iq.rb +48 -0
  49. data/lib/vines/stanza/message.rb +40 -0
  50. data/lib/vines/stanza/presence/error.rb +23 -0
  51. data/lib/vines/stanza/presence/probe.rb +37 -0
  52. data/lib/vines/stanza/presence/subscribe.rb +42 -0
  53. data/lib/vines/stanza/presence/subscribed.rb +51 -0
  54. data/lib/vines/stanza/presence/unavailable.rb +15 -0
  55. data/lib/vines/stanza/presence/unsubscribe.rb +38 -0
  56. data/lib/vines/stanza/presence/unsubscribed.rb +38 -0
  57. data/lib/vines/stanza/presence.rb +141 -0
  58. data/lib/vines/stanza/pubsub/create.rb +39 -0
  59. data/lib/vines/stanza/pubsub/delete.rb +41 -0
  60. data/lib/vines/stanza/pubsub/publish.rb +66 -0
  61. data/lib/vines/stanza/pubsub/subscribe.rb +44 -0
  62. data/lib/vines/stanza/pubsub/unsubscribe.rb +30 -0
  63. data/lib/vines/stanza/pubsub.rb +22 -0
  64. data/lib/vines/stanza.rb +175 -0
  65. data/lib/vines/storage/ldap.rb +71 -0
  66. data/lib/vines/storage/local.rb +139 -0
  67. data/lib/vines/storage/null.rb +39 -0
  68. data/lib/vines/storage/sql.rb +138 -0
  69. data/lib/vines/storage.rb +239 -0
  70. data/lib/vines/store.rb +110 -0
  71. data/lib/vines/stream/client/auth.rb +74 -0
  72. data/lib/vines/stream/client/auth_restart.rb +29 -0
  73. data/lib/vines/stream/client/bind.rb +72 -0
  74. data/lib/vines/stream/client/bind_restart.rb +24 -0
  75. data/lib/vines/stream/client/closed.rb +13 -0
  76. data/lib/vines/stream/client/ready.rb +17 -0
  77. data/lib/vines/stream/client/session.rb +210 -0
  78. data/lib/vines/stream/client/start.rb +27 -0
  79. data/lib/vines/stream/client/tls.rb +38 -0
  80. data/lib/vines/stream/client.rb +84 -0
  81. data/lib/vines/stream/component/handshake.rb +26 -0
  82. data/lib/vines/stream/component/ready.rb +23 -0
  83. data/lib/vines/stream/component/start.rb +19 -0
  84. data/lib/vines/stream/component.rb +58 -0
  85. data/lib/vines/stream/http/auth.rb +22 -0
  86. data/lib/vines/stream/http/bind.rb +32 -0
  87. data/lib/vines/stream/http/bind_restart.rb +37 -0
  88. data/lib/vines/stream/http/ready.rb +29 -0
  89. data/lib/vines/stream/http/request.rb +172 -0
  90. data/lib/vines/stream/http/session.rb +120 -0
  91. data/lib/vines/stream/http/sessions.rb +65 -0
  92. data/lib/vines/stream/http/start.rb +23 -0
  93. data/lib/vines/stream/http.rb +157 -0
  94. data/lib/vines/stream/parser.rb +79 -0
  95. data/lib/vines/stream/sasl.rb +128 -0
  96. data/lib/vines/stream/server/auth.rb +13 -0
  97. data/lib/vines/stream/server/auth_restart.rb +13 -0
  98. data/lib/vines/stream/server/final_restart.rb +21 -0
  99. data/lib/vines/stream/server/outbound/auth.rb +31 -0
  100. data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
  101. data/lib/vines/stream/server/outbound/auth_result.rb +32 -0
  102. data/lib/vines/stream/server/outbound/final_features.rb +28 -0
  103. data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
  104. data/lib/vines/stream/server/outbound/start.rb +20 -0
  105. data/lib/vines/stream/server/outbound/tls.rb +30 -0
  106. data/lib/vines/stream/server/outbound/tls_result.rb +34 -0
  107. data/lib/vines/stream/server/ready.rb +24 -0
  108. data/lib/vines/stream/server/start.rb +13 -0
  109. data/lib/vines/stream/server/tls.rb +13 -0
  110. data/lib/vines/stream/server.rb +150 -0
  111. data/lib/vines/stream/state.rb +60 -0
  112. data/lib/vines/stream.rb +247 -0
  113. data/lib/vines/token_bucket.rb +55 -0
  114. data/lib/vines/user.rb +123 -0
  115. data/lib/vines/version.rb +6 -0
  116. data/lib/vines/xmpp_server.rb +25 -0
  117. data/lib/vines.rb +203 -0
  118. data/test/cluster/publisher_test.rb +57 -0
  119. data/test/cluster/sessions_test.rb +47 -0
  120. data/test/cluster/subscriber_test.rb +109 -0
  121. data/test/config/host_test.rb +369 -0
  122. data/test/config/pubsub_test.rb +187 -0
  123. data/test/config_test.rb +732 -0
  124. data/test/error_test.rb +58 -0
  125. data/test/ext/nokogiri.rb +14 -0
  126. data/test/follower_test.rb +102 -0
  127. data/test/jid_test.rb +147 -0
  128. data/test/kit_test.rb +31 -0
  129. data/test/router_test.rb +243 -0
  130. data/test/stanza/iq/disco_info_test.rb +78 -0
  131. data/test/stanza/iq/disco_items_test.rb +49 -0
  132. data/test/stanza/iq/private_storage_test.rb +184 -0
  133. data/test/stanza/iq/roster_test.rb +229 -0
  134. data/test/stanza/iq/session_test.rb +25 -0
  135. data/test/stanza/iq/vcard_test.rb +146 -0
  136. data/test/stanza/iq/version_test.rb +64 -0
  137. data/test/stanza/iq_test.rb +70 -0
  138. data/test/stanza/message_test.rb +126 -0
  139. data/test/stanza/presence/probe_test.rb +50 -0
  140. data/test/stanza/presence/subscribe_test.rb +83 -0
  141. data/test/stanza/pubsub/create_test.rb +116 -0
  142. data/test/stanza/pubsub/delete_test.rb +169 -0
  143. data/test/stanza/pubsub/publish_test.rb +309 -0
  144. data/test/stanza/pubsub/subscribe_test.rb +205 -0
  145. data/test/stanza/pubsub/unsubscribe_test.rb +148 -0
  146. data/test/stanza_test.rb +85 -0
  147. data/test/storage/ldap_test.rb +201 -0
  148. data/test/storage/local_test.rb +59 -0
  149. data/test/storage/mock_redis.rb +97 -0
  150. data/test/storage/null_test.rb +29 -0
  151. data/test/storage/storage_tests.rb +182 -0
  152. data/test/storage_test.rb +85 -0
  153. data/test/store_test.rb +130 -0
  154. data/test/stream/client/auth_test.rb +137 -0
  155. data/test/stream/client/ready_test.rb +47 -0
  156. data/test/stream/client/session_test.rb +27 -0
  157. data/test/stream/component/handshake_test.rb +52 -0
  158. data/test/stream/component/ready_test.rb +103 -0
  159. data/test/stream/component/start_test.rb +39 -0
  160. data/test/stream/http/auth_test.rb +70 -0
  161. data/test/stream/http/ready_test.rb +86 -0
  162. data/test/stream/http/request_test.rb +209 -0
  163. data/test/stream/http/sessions_test.rb +49 -0
  164. data/test/stream/http/start_test.rb +50 -0
  165. data/test/stream/parser_test.rb +122 -0
  166. data/test/stream/sasl_test.rb +195 -0
  167. data/test/stream/server/auth_test.rb +61 -0
  168. data/test/stream/server/outbound/auth_test.rb +75 -0
  169. data/test/stream/server/ready_test.rb +98 -0
  170. data/test/test_helper.rb +42 -0
  171. data/test/token_bucket_test.rb +44 -0
  172. data/test/user_test.rb +96 -0
  173. data/vines.gemspec +30 -0
  174. metadata +387 -0
@@ -0,0 +1,309 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stanza::PubSub::Publish do
6
+ subject { Vines::Stanza::PubSub::Publish.new(xml, stream) }
7
+ let(:user) { Vines::User.new(jid: 'alice@wonderland.lit/tea') }
8
+ let(:stream) { MiniTest::Mock.new }
9
+ let(:config) do
10
+ Vines::Config.new do
11
+ host 'wonderland.lit' do
12
+ storage(:fs) { dir Dir.tmpdir }
13
+ pubsub 'games'
14
+ end
15
+ end
16
+ end
17
+
18
+ before do
19
+ class << stream
20
+ attr_accessor :config, :nodes, :user
21
+ def write(node)
22
+ @nodes ||= []
23
+ @nodes << node
24
+ end
25
+ end
26
+ stream.config = config
27
+ stream.user = user
28
+ end
29
+
30
+ describe 'when missing a to address' do
31
+ let(:xml) { publish('') }
32
+
33
+ it 'raises a feature-not-implemented stanza error' do
34
+ stream.expect(:domain, 'wonderland.lit')
35
+ -> { subject.process }.must_raise Vines::StanzaErrors::FeatureNotImplemented
36
+ stream.verify
37
+ end
38
+ end
39
+
40
+ describe 'when addressed to bare server domain' do
41
+ let(:xml) { publish('wonderland.lit') }
42
+
43
+ it 'raises a feature-not-implemented stanza error' do
44
+ -> { subject.process }.must_raise Vines::StanzaErrors::FeatureNotImplemented
45
+ stream.verify
46
+ end
47
+ end
48
+
49
+ describe 'when addressed to a non-pubsub component' do
50
+ let(:router) { MiniTest::Mock.new }
51
+ let(:xml) { publish('bogus.wonderland.lit') }
52
+
53
+ before do
54
+ router.expect :route, nil, [xml]
55
+ stream.expect :router, router
56
+ end
57
+
58
+ it 'routes rather than handle locally' do
59
+ subject.process
60
+ stream.verify
61
+ router.verify
62
+ end
63
+ end
64
+
65
+ describe 'when publishing to multiple nodes' do
66
+ let(:xml) do
67
+ node(%q{
68
+ <iq type='set' to='games.wonderland.lit' id='42'>
69
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
70
+ <publish node='game_13'>
71
+ <item id='item_42'>
72
+ <entry xmlns='http://www.w3.org/2005/Atom'>
73
+ <title>Test</title>
74
+ <summary>This is a summary.</summary>
75
+ </entry>
76
+ </item>
77
+ </publish>
78
+ <publish node='game_13'>
79
+ <item id='item_42'>
80
+ <entry xmlns='http://www.w3.org/2005/Atom'>
81
+ <title>Test</title>
82
+ <summary>This is a summary.</summary>
83
+ </entry>
84
+ </item>
85
+ </publish>
86
+ </pubsub>
87
+ </iq>
88
+ })
89
+ end
90
+
91
+ it 'raises a bad-request stanza error' do
92
+ -> { subject.process }.must_raise Vines::StanzaErrors::BadRequest
93
+ stream.verify
94
+ end
95
+ end
96
+
97
+ describe 'when publishing multiple items' do
98
+ let(:pubsub) { MiniTest::Mock.new }
99
+ let(:xml) do
100
+ node(%q{
101
+ <iq type='set' to='games.wonderland.lit' id='42'>
102
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
103
+ <publish node='game_13'>
104
+ <item id='item_42'>
105
+ <entry xmlns='http://www.w3.org/2005/Atom'>
106
+ <title>Test</title>
107
+ <summary>This is a summary.</summary>
108
+ </entry>
109
+ </item>
110
+ <item id="item_43">bad</item>
111
+ </publish>
112
+ </pubsub>
113
+ </iq>
114
+ })
115
+ end
116
+
117
+ it 'raises a bad-request stanza error' do
118
+ pubsub.expect :node?, true, ['game_13']
119
+ subject.stub :pubsub, pubsub do
120
+ -> { subject.process }.must_raise Vines::StanzaErrors::BadRequest
121
+ end
122
+ stream.verify
123
+ pubsub.verify
124
+ end
125
+ end
126
+
127
+ describe 'when publishing one item with multiple payloads' do
128
+ let(:pubsub) { MiniTest::Mock.new }
129
+ let(:xml) do
130
+ node(%q{
131
+ <iq type='set' to='games.wonderland.lit' id='42'>
132
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
133
+ <publish node='game_13'>
134
+ <item id='item_42'>
135
+ <entry xmlns='http://www.w3.org/2005/Atom'>
136
+ <title>Test</title>
137
+ <summary>This is a summary.</summary>
138
+ </entry>
139
+ <entry>bad</entry>
140
+ </item>
141
+ </publish>
142
+ </pubsub>
143
+ </iq>
144
+ })
145
+ end
146
+
147
+ it 'raises a bad-request stanza error' do
148
+ pubsub.expect :node?, true, ['game_13']
149
+ subject.stub :pubsub, pubsub do
150
+ -> { subject.process }.must_raise Vines::StanzaErrors::BadRequest
151
+ end
152
+ stream.verify
153
+ pubsub.verify
154
+ end
155
+ end
156
+
157
+ describe 'when publishing with no payload' do
158
+ let(:pubsub) { MiniTest::Mock.new }
159
+ let(:xml) do
160
+ node(%q{
161
+ <iq type='set' to='games.wonderland.lit' id='42'>
162
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
163
+ <publish node='game_13'>
164
+ <item id='item_42'>
165
+ </item>
166
+ </publish>
167
+ </pubsub>
168
+ </iq>
169
+ })
170
+ end
171
+
172
+ it 'raises a bad-request stanza error' do
173
+ pubsub.expect :node?, true, ['game_13']
174
+ subject.stub :pubsub, pubsub do
175
+ -> { subject.process }.must_raise Vines::StanzaErrors::BadRequest
176
+ end
177
+ stream.verify
178
+ pubsub.verify
179
+ end
180
+ end
181
+
182
+ describe 'when publishing to a missing node' do
183
+ let(:xml) { publish('games.wonderland.lit') }
184
+
185
+ it 'raises an item-not-found stanza error' do
186
+ -> { subject.process }.must_raise Vines::StanzaErrors::ItemNotFound
187
+ stream.verify
188
+ end
189
+ end
190
+
191
+ describe 'when publishing an item without an id' do
192
+ let(:pubsub) { MiniTest::Mock.new }
193
+ let(:xml) { publish('games.wonderland.lit', '') }
194
+ let(:broadcast) { message_broadcast('') }
195
+ let(:response) do
196
+ node(%q{
197
+ <iq from="games.wonderland.lit" id="42" to="alice@wonderland.lit/tea" type="result">
198
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
199
+ <publish node="game_13">
200
+ <item/>
201
+ </publish>
202
+ </pubsub>
203
+ </iq>})
204
+ end
205
+
206
+ before do
207
+ pubsub.expect :node?, true, ['game_13']
208
+ def pubsub.published; @published; end
209
+ def pubsub.publish(node, message)
210
+ @published ||= []
211
+ @published << [node, message]
212
+ end
213
+ end
214
+
215
+ it 'generates an item id in the response' do
216
+ subject.stub :pubsub, pubsub do
217
+ subject.process
218
+ end
219
+ stream.verify
220
+ pubsub.verify
221
+ stream.nodes.size.must_equal 1
222
+
223
+ # id is random
224
+ item = stream.nodes.first.xpath('ns:pubsub/ns:publish/ns:item',
225
+ 'ns' => 'http://jabber.org/protocol/pubsub').first
226
+ item['id'].wont_be_nil
227
+ item.remove_attribute('id')
228
+ stream.nodes.first.must_equal response
229
+ end
230
+
231
+ it 'broadcasts the message with the generated item id' do
232
+ subject.stub :pubsub, pubsub do
233
+ subject.process
234
+ end
235
+ stream.verify
236
+ pubsub.verify
237
+ stream.nodes.size.must_equal 1
238
+
239
+ published_node, published_message = *pubsub.published[0]
240
+ published_node.must_equal 'game_13'
241
+ # id is random
242
+ item = published_message.xpath('ns:event/ns:items/ns:item',
243
+ 'ns' => 'http://jabber.org/protocol/pubsub#event').first
244
+ item['id'].wont_be_nil
245
+ item.remove_attribute('id')
246
+ published_message.must_equal broadcast
247
+ end
248
+ end
249
+
250
+ describe 'when publishing a valid stanza' do
251
+ let(:pubsub) { MiniTest::Mock.new }
252
+ let(:xml) { publish('games.wonderland.lit') }
253
+ let(:response) { result(user.jid, 'games.wonderland.lit') }
254
+ let(:broadcast) { message_broadcast('item_42') }
255
+
256
+ it 'broadcasts and returns result to sender' do
257
+ pubsub.expect :node?, true, ['game_13']
258
+ pubsub.expect :publish, nil, ['game_13', broadcast]
259
+
260
+ subject.stub :pubsub, pubsub do
261
+ subject.process
262
+ end
263
+
264
+ stream.nodes.size.must_equal 1
265
+ stream.nodes.first.must_equal response
266
+ stream.verify
267
+ pubsub.verify
268
+ end
269
+ end
270
+
271
+ private
272
+
273
+ def message_broadcast(item_id)
274
+ item_id = (item_id.nil? || item_id.empty?) ? ' ' : " id='#{item_id}' "
275
+ node(%Q{
276
+ <message>
277
+ <event xmlns="http://jabber.org/protocol/pubsub#event">
278
+ <items node="game_13">
279
+ <item#{item_id}publisher="alice@wonderland.lit/tea">
280
+ <entry xmlns="http://www.w3.org/2005/Atom">
281
+ <title>Test</title>
282
+ <summary>This is a summary.</summary>
283
+ </entry>
284
+ </item>
285
+ </items>
286
+ </event>
287
+ </message>})
288
+ end
289
+
290
+ def publish(to, item_id='item_42')
291
+ item_id = "id='#{item_id}'" unless item_id.nil? || item_id.empty?
292
+ body = %Q{
293
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
294
+ <publish node='game_13'>
295
+ <item #{item_id}>
296
+ <entry xmlns='http://www.w3.org/2005/Atom'>
297
+ <title>Test</title>
298
+ <summary>This is a summary.</summary>
299
+ </entry>
300
+ </item>
301
+ </publish>
302
+ </pubsub>}
303
+ iq(type: 'set', to: to, id: 42, body: body)
304
+ end
305
+
306
+ def result(to, from)
307
+ iq(from: from, id: 42, to: to, type: 'result')
308
+ end
309
+ end
@@ -0,0 +1,205 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stanza::PubSub::Subscribe do
6
+ subject { Vines::Stanza::PubSub::Subscribe.new(xml, stream) }
7
+ let(:alice) { Vines::User.new(jid: 'alice@wonderland.lit/tea') }
8
+ let(:stream) { MiniTest::Mock.new }
9
+ let(:config) do
10
+ Vines::Config.new do
11
+ host 'wonderland.lit' do
12
+ storage(:fs) { dir Dir.tmpdir }
13
+ pubsub 'games'
14
+ end
15
+ end
16
+ end
17
+
18
+ before do
19
+ class << stream
20
+ attr_accessor :config, :domain, :nodes, :user
21
+ def write(node)
22
+ @nodes ||= []
23
+ @nodes << node
24
+ end
25
+ end
26
+ stream.config = config
27
+ stream.user = alice
28
+ stream.domain = 'wonderland.lit'
29
+ end
30
+
31
+ describe 'when missing a to address' do
32
+ let(:xml) do
33
+ node(%q{
34
+ <iq type='set' id='42'>
35
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
36
+ <subscribe node='game_13' jid="alice@wonderland.lit/tea"/>
37
+ </pubsub>
38
+ </iq>
39
+ })
40
+ end
41
+
42
+ it 'raises a feature-not-implemented stanza error' do
43
+ -> { subject.process }.must_raise Vines::StanzaErrors::FeatureNotImplemented
44
+ stream.verify
45
+ end
46
+ end
47
+
48
+ describe 'when addressed to a bare server domain' do
49
+ let(:xml) do
50
+ node(%q{
51
+ <iq type='set' to='wonderland.lit' id='42'>
52
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
53
+ <subscribe node='game_13' jid="alice@wonderland.lit/tea"/>
54
+ </pubsub>
55
+ </iq>
56
+ })
57
+ end
58
+
59
+ it 'raises a feature-not-implemented stanza error' do
60
+ -> { subject.process }.must_raise Vines::StanzaErrors::FeatureNotImplemented
61
+ stream.verify
62
+ end
63
+ end
64
+
65
+ describe 'when addressed to a non-pubsub address' do
66
+ let(:router) { MiniTest::Mock.new }
67
+ let(:xml) do
68
+ node(%q{
69
+ <iq type='set' to='bogus.wonderland.lit' id='42'>
70
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
71
+ <subscribe node='game_13' jid="alice@wonderland.lit/tea"/>
72
+ </pubsub>
73
+ </iq>
74
+ })
75
+ end
76
+
77
+ it 'routes rather than handle locally' do
78
+ router.expect :route, nil, [xml]
79
+ stream.expect :router, router
80
+
81
+ subject.process
82
+ stream.verify
83
+ router.verify
84
+ end
85
+ end
86
+
87
+ describe 'when stanza contains multiple subscribe elements' do
88
+ let(:xml) do
89
+ node(%q{
90
+ <iq type='set' to='games.wonderland.lit' id='42'>
91
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
92
+ <subscribe node='game_13' jid="alice@wonderland.lit/tea"/>
93
+ <subscribe node='game_14' jid="alice@wonderland.lit/tea"/>
94
+ </pubsub>
95
+ </iq>
96
+ })
97
+ end
98
+
99
+ it 'raises a bad-request stanza error' do
100
+ -> { subject.process }.must_raise Vines::StanzaErrors::BadRequest
101
+ stream.verify
102
+ end
103
+ end
104
+
105
+ describe 'when stanza is missing a subscribe element' do
106
+ let(:xml) do
107
+ node(%q{
108
+ <iq type='set' to='games.wonderland.lit' id='42'>
109
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
110
+ <subscribe node='game_13' jid="alice@wonderland.lit/tea"/>
111
+ </pubsub>
112
+ </iq>
113
+ })
114
+ end
115
+
116
+ it 'raises an item-not-found stanza error' do
117
+ -> { subject.process }.must_raise Vines::StanzaErrors::ItemNotFound
118
+ stream.verify
119
+ end
120
+ end
121
+
122
+ describe 'when attempting to subscribe to a node twice' do
123
+ let(:pubsub) { MiniTest::Mock.new }
124
+ let(:xml) do
125
+ node(%q{
126
+ <iq type='set' to='games.wonderland.lit' id='42'>
127
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
128
+ <subscribe node='game_13' jid="alice@wonderland.lit/tea"/>
129
+ </pubsub>
130
+ </iq>
131
+ })
132
+ end
133
+
134
+ before do
135
+ pubsub.expect :node?, true, ['game_13']
136
+ pubsub.expect :subscribed?, true, ['game_13', alice.jid]
137
+ end
138
+
139
+ it 'raises a policy-violation stanza error' do
140
+ subject.stub :pubsub, pubsub do
141
+ -> { subject.process }.must_raise Vines::StanzaErrors::PolicyViolation
142
+ end
143
+ stream.verify
144
+ pubsub.verify
145
+ end
146
+ end
147
+
148
+ describe 'when subscribing with an illegal jid' do
149
+ let(:xml) do
150
+ node(%q{
151
+ <iq type='set' to='games.wonderland.lit' id='42'>
152
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
153
+ <subscribe node='game_13' jid="not_alice@wonderland.lit/tea"/>
154
+ </pubsub>
155
+ </iq>
156
+ })
157
+ end
158
+
159
+ it 'raises a bad-request stanza error' do
160
+ -> { subject.process }.must_raise Vines::StanzaErrors::BadRequest
161
+ stream.verify
162
+ end
163
+ end
164
+
165
+ describe 'when subscribing with a valid stanza' do
166
+ let(:xml) do
167
+ node(%q{
168
+ <iq type='set' to='games.wonderland.lit' id='42'>
169
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
170
+ <subscribe node='game_13' jid="alice@wonderland.lit/tea"/>
171
+ </pubsub>
172
+ </iq>
173
+ })
174
+ end
175
+
176
+ let(:expected) do
177
+ node(%q{
178
+ <iq from="games.wonderland.lit" id="42" to="alice@wonderland.lit/tea" type="result">
179
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
180
+ <subscription node="game_13" jid="alice@wonderland.lit/tea" subscription="subscribed"/>
181
+ </pubsub>
182
+ </iq>
183
+ })
184
+ end
185
+
186
+ let(:pubsub) { MiniTest::Mock.new }
187
+
188
+ before do
189
+ pubsub.expect :node?, true, ['game_13']
190
+ pubsub.expect :subscribed?, false, ['game_13', alice.jid]
191
+ pubsub.expect :subscribe, nil, ['game_13', alice.jid]
192
+ end
193
+
194
+ it 'writes a result stanza to the stream' do
195
+ subject.stub :pubsub, pubsub do
196
+ subject.process
197
+ end
198
+
199
+ stream.verify
200
+ pubsub.verify
201
+ stream.nodes.size.must_equal 1
202
+ stream.nodes.first.must_equal expected
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,148 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stanza::PubSub::Unsubscribe do
6
+ subject { Vines::Stanza::PubSub::Unsubscribe.new(xml, stream) }
7
+ let(:user) { Vines::User.new(jid: 'alice@wonderland.lit/tea') }
8
+ let(:stream) { MiniTest::Mock.new }
9
+ let(:config) do
10
+ Vines::Config.new do
11
+ host 'wonderland.lit' do
12
+ storage(:fs) { dir Dir.tmpdir }
13
+ pubsub 'games'
14
+ end
15
+ end
16
+ end
17
+
18
+ before do
19
+ class << stream
20
+ attr_accessor :config, :nodes, :user
21
+ def write(node)
22
+ @nodes ||= []
23
+ @nodes << node
24
+ end
25
+ end
26
+ stream.config = config
27
+ stream.user = user
28
+ end
29
+
30
+ describe 'when missing a to address' do
31
+ let(:xml) { unsubscribe('') }
32
+
33
+ it 'raises a feature-not-implemented stanza error' do
34
+ stream.expect :domain, 'wonderland.lit'
35
+ -> { subject.process }.must_raise Vines::StanzaErrors::FeatureNotImplemented
36
+ stream.verify
37
+ end
38
+ end
39
+
40
+ describe 'when addressed to bare server domain' do
41
+ let(:xml) { unsubscribe('wonderland.lit') }
42
+
43
+ it 'raises a feature-not-implemented stanza error' do
44
+ -> { subject.process }.must_raise Vines::StanzaErrors::FeatureNotImplemented
45
+ stream.verify
46
+ end
47
+ end
48
+
49
+ describe 'when addressed to a non-pubsub component' do
50
+ let(:router) { MiniTest::Mock.new }
51
+ let(:xml) { unsubscribe('bogus.wonderland.lit') }
52
+
53
+ before do
54
+ router.expect :route, nil, [xml]
55
+ stream.expect :router, router
56
+ end
57
+
58
+ it 'routes rather than handle locally' do
59
+ subject.process
60
+ stream.verify
61
+ router.verify
62
+ end
63
+ end
64
+
65
+ describe 'when attempting to unsubscribe from multiple nodes' do
66
+ let(:xml) { unsubscribe('games.wonderland.lit', true) }
67
+
68
+ it 'raises a bad-request stanza error' do
69
+ -> { subject.process }.must_raise Vines::StanzaErrors::BadRequest
70
+ stream.verify
71
+ end
72
+ end
73
+
74
+ describe 'when unsubscribing from a missing node' do
75
+ let(:xml) { unsubscribe('games.wonderland.lit') }
76
+
77
+ it 'raises an item-not-found stanza error' do
78
+ -> { subject.process }.must_raise Vines::StanzaErrors::ItemNotFound
79
+ stream.verify
80
+ end
81
+ end
82
+
83
+ describe 'when unsubscribing without a subscription' do
84
+ let(:pubsub) { MiniTest::Mock.new }
85
+ let(:xml) { unsubscribe('games.wonderland.lit') }
86
+
87
+ before do
88
+ pubsub.expect :node?, true, ['game_13']
89
+ pubsub.expect :subscribed?, false, ['game_13', user.jid]
90
+ end
91
+
92
+ it 'raises an unexpected-request stanza error' do
93
+ subject.stub :pubsub, pubsub do
94
+ -> { subject.process }.must_raise Vines::StanzaErrors::UnexpectedRequest
95
+ end
96
+ stream.verify
97
+ pubsub.verify
98
+ end
99
+ end
100
+
101
+ describe 'when unsubscribing an illegal jid' do
102
+ let(:xml) { unsubscribe('games.wonderland.lit', false, 'not_alice@wonderland.lit/tea') }
103
+
104
+ it 'raises a forbidden stanza error' do
105
+ -> { subject.process }.must_raise Vines::StanzaErrors::Forbidden
106
+ stream.verify
107
+ end
108
+ end
109
+
110
+ describe 'when given a valid stanza' do
111
+ let(:pubsub) { MiniTest::Mock.new }
112
+ let(:xml) { unsubscribe('games.wonderland.lit') }
113
+ let(:expected) { result(user.jid, 'games.wonderland.lit') }
114
+
115
+ before do
116
+ pubsub.expect :node?, true, ['game_13']
117
+ pubsub.expect :subscribed?, true, ['game_13', user.jid]
118
+ pubsub.expect :unsubscribe, nil, ['game_13', user.jid]
119
+ end
120
+
121
+ it 'sends an iq result stanza to sender' do
122
+ subject.stub :pubsub, pubsub do
123
+ subject.process
124
+ end
125
+
126
+ stream.nodes.size.must_equal 1
127
+ stream.nodes.first.must_equal expected
128
+ stream.verify
129
+ pubsub.verify
130
+ end
131
+ end
132
+
133
+ private
134
+
135
+ def unsubscribe(to, multiple=false, jid=user.jid)
136
+ extra = "<unsubscribe node='game_14' jid='#{jid}'/>" if multiple
137
+ body = %Q{
138
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
139
+ <unsubscribe node='game_13' jid="#{jid}"/>
140
+ #{extra}
141
+ </pubsub>}
142
+ iq(type: 'set', to: to, id: 42, body: body)
143
+ end
144
+
145
+ def result(to, from)
146
+ iq(from: from, id: 42, to: to, type: 'result')
147
+ end
148
+ end