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,49 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stanza::Iq::DiscoItems do
6
+ subject { Vines::Stanza::Iq::DiscoItems.new(xml, stream) }
7
+ let(:stream) { MiniTest::Mock.new }
8
+ let(:alice) { Vines::User.new(jid: 'alice@wonderland.lit/home') }
9
+ let(:config) do
10
+ Vines::Config.new do
11
+ host 'wonderland.lit' do
12
+ storage(:fs) { dir Dir.tmpdir }
13
+ components 'tea' => 'secr3t', 'cake' => 'passw0rd'
14
+ end
15
+ end
16
+ end
17
+
18
+ before do
19
+ class << stream
20
+ attr_accessor :config, :user
21
+ end
22
+ stream.config = config
23
+ stream.user = alice
24
+ end
25
+
26
+ describe 'when querying server items' do
27
+ let(:xml) do
28
+ query = %q{<query xmlns="http://jabber.org/protocol/disco#items"/>}
29
+ node(%Q{<iq id="42" to="wonderland.lit" type="get">#{query}</iq>})
30
+ end
31
+
32
+ let(:result) do
33
+ node(%q{
34
+ <iq from="wonderland.lit" id="42" to="alice@wonderland.lit/home" type="result">
35
+ <query xmlns="http://jabber.org/protocol/disco#items">
36
+ <item jid="cake.wonderland.lit"/>
37
+ <item jid="tea.wonderland.lit"/>
38
+ </query>
39
+ </iq>
40
+ })
41
+ end
42
+
43
+ it 'includes component domains in output' do
44
+ stream.expect :write, nil, [result]
45
+ subject.process
46
+ stream.verify
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,184 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stanza::Iq::PrivateStorage do
6
+ subject { Vines::Stanza::Iq::PrivateStorage.new(xml, stream) }
7
+ let(:alice) { Vines::User.new(jid: 'alice@wonderland.lit/tea') }
8
+ let(:storage) { MiniTest::Mock.new }
9
+ let(:stream) { MiniTest::Mock.new }
10
+ let(:config) do
11
+ Vines::Config.new do
12
+ host 'wonderland.lit' do
13
+ storage(:fs) { dir Dir.tmpdir }
14
+ private_storage true
15
+ end
16
+ end
17
+ end
18
+
19
+ before do
20
+ class << stream
21
+ attr_accessor :config, :domain, :user
22
+ end
23
+ stream.config = config
24
+ stream.user = alice
25
+ stream.domain = 'wonderland.lit'
26
+ end
27
+
28
+ describe 'when private storage feature is disabled' do
29
+ let(:xml) do
30
+ query = %q{<query xmlns="jabber:iq:private"><one xmlns="a"/></query>}
31
+ node(%Q{<iq id="42" type="get">#{query}</iq>})
32
+ end
33
+
34
+ before do
35
+ config.vhost('wonderland.lit').private_storage false
36
+ end
37
+
38
+ it 'raises a service-unavailable stanza error' do
39
+ -> { subject.process }.must_raise Vines::StanzaErrors::ServiceUnavailable
40
+ stream.verify
41
+ end
42
+ end
43
+
44
+ describe 'when retrieving a fragment for another user jid' do
45
+ let(:xml) do
46
+ query = %q{<query xmlns="jabber:iq:private"><one xmlns="a"/></query>}
47
+ node(%Q{<iq id="42" to="hatter@wonderland.lit" type="get">#{query}</iq>})
48
+ end
49
+
50
+ it 'raises a forbidden stanza error' do
51
+ -> { subject.process }.must_raise Vines::StanzaErrors::Forbidden
52
+ stream.verify
53
+ end
54
+ end
55
+
56
+ describe 'when get stanza contains zero child elements' do
57
+ let(:xml) do
58
+ query = %q{<query xmlns="jabber:iq:private"></query>}
59
+ node(%Q{<iq id="42" type="get">#{query}</iq>})
60
+ end
61
+
62
+ it 'raises a not-acceptable stanza error' do
63
+ -> { subject.process }.must_raise Vines::StanzaErrors::NotAcceptable
64
+ stream.verify
65
+ end
66
+ end
67
+
68
+ describe 'when get stanza contains more than one child element' do
69
+ let(:xml) do
70
+ query = %q{<query xmlns="jabber:iq:private"><one xmlns="a"/><two xmlns="b"/></query>}
71
+ node(%Q{<iq id="42" type="get">#{query}</iq>})
72
+ end
73
+
74
+ it 'raises a not-acceptable stanza error' do
75
+ -> { subject.process }.must_raise Vines::StanzaErrors::NotAcceptable
76
+ stream.verify
77
+ end
78
+ end
79
+
80
+ describe 'when get stanza is missing a namespace' do
81
+ let(:xml) do
82
+ query = %q{<query xmlns="jabber:iq:private"><one/></query>}
83
+ node = node(%Q{<iq id="42" type="get">#{query}</iq>})
84
+ end
85
+
86
+ it 'raises a not-acceptable stanza error' do
87
+ -> { subject.process }.must_raise Vines::StanzaErrors::NotAcceptable
88
+ stream.verify
89
+ end
90
+ end
91
+
92
+ describe 'when get stanza is missing fragment' do
93
+ let(:xml) do
94
+ query = %q{<query xmlns="jabber:iq:private"><one xmlns="a"/></query>}
95
+ node(%Q{<iq id="42" type="get">#{query}</iq>})
96
+ end
97
+
98
+ before do
99
+ storage.expect :find_fragment, nil, [alice.jid, xml.elements[0].elements[0]]
100
+ stream.expect :storage, storage, ['wonderland.lit']
101
+ end
102
+
103
+ it 'raises an item-not-found stanza error' do
104
+ -> { subject.process }.must_raise Vines::StanzaErrors::ItemNotFound
105
+ stream.verify
106
+ storage.verify
107
+ end
108
+ end
109
+
110
+ describe 'when get finds fragment successfully' do
111
+ let(:xml) do
112
+ query = %q{<query xmlns="jabber:iq:private"><one xmlns="a"/></query>}
113
+ node = node(%Q{<iq id="42" type="get">#{query}</iq>})
114
+ end
115
+
116
+ before do
117
+ data = %q{<one xmlns="a"><child>data</child></one>}
118
+ query = %Q{<query xmlns="jabber:iq:private">#{data}</query>}
119
+ expected = node(%Q{<iq from="#{alice.jid}" id="42" to="#{alice.jid}" type="result">#{query}</iq>})
120
+
121
+ storage.expect :find_fragment, node(data), [alice.jid, xml.elements[0].elements[0]]
122
+ stream.expect :storage, storage, ['wonderland.lit']
123
+ stream.expect :write, nil, [expected]
124
+ end
125
+
126
+ it 'writes a response to the stream' do
127
+ subject.process
128
+ stream.verify
129
+ storage.verify
130
+ end
131
+ end
132
+
133
+ describe 'when saving a fragment' do
134
+ let(:result) { node(%Q{<iq from="#{alice.jid}" id="42" to="#{alice.jid}" type="result"/>}) }
135
+
136
+ before do
137
+ storage.expect :save_fragment, nil, [alice.jid, xml.elements[0].elements[0]]
138
+ stream.expect :storage, storage, ['wonderland.lit']
139
+ stream.expect :write, nil, [result]
140
+ end
141
+
142
+ describe 'and stanza contains zero child elements' do
143
+ let(:xml) do
144
+ query = %q{<query xmlns="jabber:iq:private"></query>}
145
+ node(%Q{<iq id="42" type="set">#{query}</iq>})
146
+ end
147
+
148
+ it 'raises a not-acceptable stanza error' do
149
+ -> { subject.process }.must_raise Vines::StanzaErrors::NotAcceptable
150
+ end
151
+ end
152
+
153
+ describe 'and a single single fragment saves successfully' do
154
+ let(:xml) do
155
+ query = %q{<query xmlns="jabber:iq:private"><one xmlns="a"/></query>}
156
+ node(%Q{<iq id="42" type="set">#{query}</iq>})
157
+ end
158
+
159
+ it 'writes a result to the stream' do
160
+ subject.process
161
+ stream.verify
162
+ storage.verify
163
+ end
164
+ end
165
+
166
+ describe 'and two fragments save successfully' do
167
+ let(:xml) do
168
+ query = %q{<query xmlns="jabber:iq:private"><one xmlns="a"/><two xmlns="a"/></query>}
169
+ node(%Q{<iq id="42" type="set">#{query}</iq>})
170
+ end
171
+
172
+ before do
173
+ storage.expect :save_fragment, nil, [alice.jid, xml.elements[0].elements[1]]
174
+ stream.expect :storage, storage, ['wonderland.lit']
175
+ end
176
+
177
+ it 'writes a result to the stream' do
178
+ subject.process
179
+ stream.verify
180
+ storage.verify
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,229 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stanza::Iq::Roster do
6
+ subject { Vines::Stanza::Iq::Roster.new(xml, stream) }
7
+ let(:stream) { MiniTest::Mock.new }
8
+ let(:alice) { Vines::User.new(jid: 'alice@wonderland.lit/tea') }
9
+
10
+ before do
11
+ class << stream
12
+ attr_accessor :domain, :user
13
+ end
14
+ stream.user = alice
15
+ stream.domain = 'wonderland.lit'
16
+ end
17
+
18
+ describe 'when retrieving an empty roster' do
19
+ let(:xml) { node(%q{<iq id="42" type="get"><query xmlns='jabber:iq:roster'/></iq>}) }
20
+ let(:expected) { node(%q{<iq id="42" type="result"><query xmlns="jabber:iq:roster"/></iq>}) }
21
+
22
+ before do
23
+ stream.expect :write, nil, [expected]
24
+ stream.expect :requested_roster!, nil
25
+ end
26
+
27
+ it 'returns an empty stanza' do
28
+ subject.process
29
+ stream.verify
30
+ end
31
+ end
32
+
33
+ describe 'when retrieving a non-empty roster' do
34
+ let(:xml) { node(%q{<iq id="42" type="get"><query xmlns='jabber:iq:roster'/></iq>}) }
35
+ let(:expected) do
36
+ node(%q{
37
+ <iq id="42" type="result">
38
+ <query xmlns="jabber:iq:roster">
39
+ <item jid="cat@wonderland.lit" subscription="none">
40
+ <group>Cats</group>
41
+ <group>Friends</group>
42
+ </item>
43
+ <item jid="hatter@wonderland.lit" subscription="none"/>
44
+ </query>
45
+ </iq>})
46
+ end
47
+
48
+ before do
49
+ alice.roster << Vines::Contact.new(jid: 'hatter@wonderland.lit')
50
+ alice.roster << Vines::Contact.new(jid: 'cat@wonderland.lit', :groups => ['Friends', 'Cats'])
51
+
52
+ stream.expect :write, nil, [expected]
53
+ stream.expect :requested_roster!, nil
54
+ end
55
+
56
+ it 'sorts groups alphabetically' do
57
+ subject.process
58
+ stream.verify
59
+ end
60
+ end
61
+
62
+ describe 'when requesting a roster for another user' do
63
+ let(:xml) do
64
+ node(%q{
65
+ <iq id="42" type="get" to="romeo@verona.lit">
66
+ <query xmlns="jabber:iq:roster"/>
67
+ </iq>})
68
+ end
69
+
70
+ it 'raises a forbidden stanza error' do
71
+ -> { subject.process }.must_raise Vines::StanzaErrors::Forbidden
72
+ stream.verify
73
+ end
74
+ end
75
+
76
+ describe 'when saving a roster for another user' do
77
+ let(:xml) do
78
+ node(%q{
79
+ <iq id="42" type="set" to="romeo@verona.lit">
80
+ <query xmlns="jabber:iq:roster"/>
81
+ </iq>})
82
+ end
83
+
84
+ it 'raises a forbidden stanza error' do
85
+ -> { subject.process }.must_raise Vines::StanzaErrors::Forbidden
86
+ stream.verify
87
+ end
88
+ end
89
+
90
+ describe 'when saving a roster with no items' do
91
+ let(:xml) do
92
+ node(%q{
93
+ <iq id="42" type="set">
94
+ <query xmlns="jabber:iq:roster"/>
95
+ </iq>})
96
+ end
97
+
98
+ it 'raises a bad-request stanza error' do
99
+ -> { subject.process }.must_raise Vines::StanzaErrors::BadRequest
100
+ stream.verify
101
+ end
102
+ end
103
+
104
+ describe 'when updating a roster with more than one item' do
105
+ let(:xml) do
106
+ node(%q{
107
+ <iq id="42" type="set">
108
+ <query xmlns="jabber:iq:roster">
109
+ <item jid="hatter@wonderland.lit"/>
110
+ <item jid="cat@wonderland.lit"/>
111
+ </query>
112
+ </iq>})
113
+ end
114
+
115
+ it 'raises a bad-request stanza error' do
116
+ -> { subject.process }.must_raise Vines::StanzaErrors::BadRequest
117
+ stream.verify
118
+ end
119
+ end
120
+
121
+ describe 'when adding a roster item without a jid attribute' do
122
+ let(:xml) do
123
+ node(%q{
124
+ <iq id="42" type="set">
125
+ <query xmlns="jabber:iq:roster">
126
+ <item name="Mad Hatter"/>
127
+ </query>
128
+ </iq>})
129
+ end
130
+
131
+ it 'raises a bad-request stanza error' do
132
+ -> { subject.process }.must_raise Vines::StanzaErrors::BadRequest
133
+ stream.verify
134
+ end
135
+ end
136
+
137
+ describe 'when adding a roster item with duplicate groups' do
138
+ let(:xml) do
139
+ node(%q{
140
+ <iq id="42" type="set">
141
+ <query xmlns="jabber:iq:roster">
142
+ <item jid="hatter@wonderland.lit" name="Mad Hatter">
143
+ <group>Friends</group>
144
+ <group>Friends</group>
145
+ </item>
146
+ </query>
147
+ </iq>})
148
+ end
149
+
150
+ it 'raises a bad-request stanza error' do
151
+ -> { subject.process }.must_raise Vines::StanzaErrors::BadRequest
152
+ stream.verify
153
+ end
154
+ end
155
+
156
+ describe 'when adding a roster item with an empty group name' do
157
+ let(:xml) do
158
+ node(%q{
159
+ <iq id="42" type="set">
160
+ <query xmlns="jabber:iq:roster">
161
+ <item jid="hatter@wonderland.lit" name="Mad Hatter">
162
+ <group></group>
163
+ </item>
164
+ </query>
165
+ </iq>})
166
+ end
167
+
168
+ it 'raises a not-acceptable stanza error' do
169
+ -> { subject.process }.must_raise Vines::StanzaErrors::NotAcceptable
170
+ stream.verify
171
+ end
172
+ end
173
+
174
+ describe 'when saving a roster successfully' do
175
+ let(:xml) do
176
+ node(%q{
177
+ <iq id="42" type="set">
178
+ <query xmlns="jabber:iq:roster">
179
+ <item jid="hatter@wonderland.lit" name="Mad Hatter">
180
+ <group>Friends</group>
181
+ </item>
182
+ </query>
183
+ </iq>})
184
+ end
185
+
186
+ let(:expected) do
187
+ node(%q{
188
+ <iq to="alice@wonderland.lit/tea" type="set">
189
+ <query xmlns="jabber:iq:roster">
190
+ <item jid="hatter@wonderland.lit" name="Mad Hatter" subscription="none">
191
+ <group>Friends</group>
192
+ </item>
193
+ </query>
194
+ </iq>})
195
+ end
196
+
197
+ let(:storage) { MiniTest::Mock.new }
198
+ let(:recipient) { MiniTest::Mock.new }
199
+ let(:result) { node(%Q{<iq id="42" to="#{alice.jid}" type="result"/>}) }
200
+
201
+ before do
202
+ storage.expect :save_user, nil, [alice]
203
+
204
+ recipient.expect :user, alice
205
+ def recipient.nodes; @nodes; end
206
+ def recipient.write(node)
207
+ @nodes ||= []
208
+ @nodes << node
209
+ end
210
+
211
+ stream.expect :interested_resources, [recipient], [alice.jid]
212
+ stream.expect :update_user_streams, nil, [alice]
213
+ stream.expect :storage, storage, ['wonderland.lit']
214
+ stream.expect :write, nil, [result]
215
+ end
216
+
217
+ it 'sends a result to the sender' do
218
+ subject.process
219
+ stream.verify
220
+ storage.verify
221
+ end
222
+
223
+ it 'sends the new roster item to the interested streams' do
224
+ subject.process
225
+ recipient.nodes.first.remove_attribute('id') # id is random
226
+ recipient.nodes.first.must_equal expected
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stanza::Iq::Session do
6
+ subject { Vines::Stanza::Iq::Session.new(xml, stream) }
7
+ let(:stream) { MiniTest::Mock.new }
8
+ let(:alice) { Vines::User.new(jid: 'alice@wonderland.lit/tea') }
9
+
10
+ describe 'when session initiation is requested' do
11
+ let(:xml) { node(%q{<iq id="42" type="set"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>}) }
12
+ let(:result) { node(%q{<iq from="wonderland.lit" id="42" to="alice@wonderland.lit/tea" type="result"/>}) }
13
+
14
+ before do
15
+ stream.expect :domain, 'wonderland.lit'
16
+ stream.expect :user, alice
17
+ stream.expect :write, nil, [result]
18
+ end
19
+
20
+ it 'just returns a result to satisy older clients' do
21
+ subject.process
22
+ stream.verify
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,146 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stanza::Iq::Vcard do
6
+ subject { Vines::Stanza::Iq::Vcard.new(xml, stream) }
7
+ let(:alice) { Vines::User.new(jid: 'alice@wonderland.lit/tea') }
8
+ let(:stream) { MiniTest::Mock.new }
9
+ let(:storage) { MiniTest::Mock.new }
10
+ let(:config) do
11
+ Vines::Config.new do
12
+ host 'wonderland.lit' do
13
+ cross_domain_messages true
14
+ storage(:fs) { dir Dir.tmpdir }
15
+ end
16
+ end
17
+ end
18
+
19
+ before do
20
+ class << stream
21
+ attr_accessor :config, :domain, :user
22
+ end
23
+ stream.config = config
24
+ stream.domain = 'wonderland.lit'
25
+ stream.user = alice
26
+ end
27
+
28
+ describe 'when getting vcard' do
29
+ describe 'and addressed to a remote jid' do
30
+ let(:xml) { get('romeo@verona.lit') }
31
+ let(:router) { MiniTest::Mock.new }
32
+
33
+ before do
34
+ router.expect :route, nil, [xml]
35
+ stream.expect :router, router
36
+ end
37
+
38
+ it 'routes rather than handle locally' do
39
+ subject.process
40
+ stream.verify
41
+ router.verify
42
+ end
43
+ end
44
+
45
+ describe 'and missing to address' do
46
+ let(:xml) { get('') }
47
+ let(:card) { vcard('Alice') }
48
+ let(:expected) { result(alice.jid, '', card) }
49
+
50
+ before do
51
+ storage.expect :find_vcard, card, [alice.jid.bare]
52
+ stream.expect :storage, storage, ['wonderland.lit']
53
+ stream.expect :write, nil, [expected]
54
+ end
55
+
56
+ it 'sends vcard for authenticated jid' do
57
+ subject.process
58
+ stream.verify
59
+ storage.verify
60
+ end
61
+ end
62
+
63
+ describe 'for another user' do
64
+ let(:xml) { get(hatter) }
65
+ let(:card) { vcard('Hatter') }
66
+ let(:hatter) { Vines::JID.new('hatter@wonderland.lit') }
67
+ let(:expected) { result(alice.jid, hatter, card) }
68
+
69
+ before do
70
+ storage.expect :find_vcard, card, [hatter]
71
+ stream.expect :storage, storage, ['wonderland.lit']
72
+ stream.expect :write, nil, [expected]
73
+ end
74
+
75
+ it 'succeeds and returns vcard with from address' do
76
+ subject.process
77
+ stream.verify
78
+ storage.verify
79
+ end
80
+ end
81
+
82
+ describe 'for missing vcard' do
83
+ let(:xml) { get('') }
84
+
85
+ before do
86
+ storage.expect :find_vcard, nil, [alice.jid.bare]
87
+ stream.expect :storage, storage, ['wonderland.lit']
88
+ end
89
+
90
+ it 'returns an item-not-found stanza error' do
91
+ -> { subject.process }.must_raise Vines::StanzaErrors::ItemNotFound
92
+ stream.verify
93
+ storage.verify
94
+ end
95
+ end
96
+ end
97
+
98
+ describe 'when setting vcard' do
99
+ describe 'and addressed to another user' do
100
+ let(:xml) { set('hatter@wonderland.lit') }
101
+
102
+ it 'raises a forbidden stanza error' do
103
+ -> { subject.process }.must_raise Vines::StanzaErrors::Forbidden
104
+ stream.verify
105
+ end
106
+ end
107
+
108
+ describe 'and missing to address' do
109
+ let(:xml) { set('') }
110
+ let(:card) { vcard('Alice') }
111
+ let(:expected) { result(alice.jid) }
112
+
113
+ before do
114
+ storage.expect :save_vcard, nil, [alice.jid, card]
115
+ stream.expect :storage, storage, ['wonderland.lit']
116
+ stream.expect :write, nil, [expected]
117
+ end
118
+
119
+ it 'succeeds and returns an iq result' do
120
+ subject.process
121
+ stream.verify
122
+ storage.verify
123
+ end
124
+ end
125
+ end
126
+
127
+ private
128
+
129
+ def vcard(name)
130
+ node(%Q{<vCard xmlns="vcard-temp"><FN>#{name}</FN></vCard>})
131
+ end
132
+
133
+ def get(to)
134
+ card = '<vCard xmlns="vcard-temp"/>'
135
+ iq(id: 42, to: to, type: 'get', body: card)
136
+ end
137
+
138
+ def set(to)
139
+ card = '<vCard xmlns="vcard-temp"><FN>Alice</FN></vCard>'
140
+ iq(id: 42, to: to, type: 'set', body: card)
141
+ end
142
+
143
+ def result(to, from=nil, card=nil)
144
+ iq(from: from, id: 42, to: to, type: 'result', body: card)
145
+ end
146
+ end
@@ -0,0 +1,64 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stanza::Iq::Version do
6
+ subject { Vines::Stanza::Iq::Version.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
+ end
14
+ end
15
+ end
16
+
17
+ before do
18
+ class << stream
19
+ attr_accessor :config, :user
20
+ end
21
+ stream.config = config
22
+ stream.user = alice
23
+ end
24
+
25
+ describe 'when not addressed to the server' do
26
+ let(:router) { MiniTest::Mock.new }
27
+ let(:xml) { node(%q{<iq id="42" to="romeo@verona.lit" type="get"><query xmlns="jabber:iq:version"/></iq>}) }
28
+
29
+ before do
30
+ router.expect :route, nil, [xml]
31
+ stream.expect :router, router
32
+ end
33
+
34
+ it 'routes the stanza to the recipient jid' do
35
+ subject.process
36
+ stream.verify
37
+ router.verify
38
+ end
39
+ end
40
+
41
+ describe 'when missing a to address' do
42
+ let(:xml) { node(%q{<iq id="42" type="get"><query xmlns="jabber:iq:version"/></iq>}) }
43
+ let(:expected) do
44
+ node(%Q{
45
+ <iq from="wonderland.lit" id="42" to="alice@wonderland.lit/tea" type="result">
46
+ <query xmlns="jabber:iq:version">
47
+ <name>Vines</name>
48
+ <version>#{Vines::VERSION}</version>
49
+ </query>
50
+ </iq>})
51
+ end
52
+
53
+ before do
54
+ stream.expect :domain, 'wonderland.lit'
55
+ stream.expect :domain, 'wonderland.lit'
56
+ stream.expect :write, nil, [expected]
57
+ end
58
+
59
+ it 'returns a version result when missing a to jid' do
60
+ subject.process
61
+ stream.verify
62
+ end
63
+ end
64
+ end