diaspora-vines 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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/contact.rb +111 -0
  30. data/lib/vines/daemon.rb +78 -0
  31. data/lib/vines/error.rb +150 -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/contact_test.rb +102 -0
  125. data/test/error_test.rb +58 -0
  126. data/test/ext/nokogiri.rb +14 -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