vines 0.1.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 (119) hide show
  1. data/LICENSE +19 -0
  2. data/README +34 -0
  3. data/Rakefile +55 -0
  4. data/bin/vines +95 -0
  5. data/conf/certs/README +32 -0
  6. data/conf/certs/ca-bundle.crt +3987 -0
  7. data/conf/config.rb +114 -0
  8. data/lib/vines.rb +155 -0
  9. data/lib/vines/command/bcrypt.rb +12 -0
  10. data/lib/vines/command/cert.rb +49 -0
  11. data/lib/vines/command/init.rb +58 -0
  12. data/lib/vines/command/ldap.rb +35 -0
  13. data/lib/vines/command/restart.rb +12 -0
  14. data/lib/vines/command/schema.rb +24 -0
  15. data/lib/vines/command/start.rb +28 -0
  16. data/lib/vines/command/stop.rb +18 -0
  17. data/lib/vines/config.rb +191 -0
  18. data/lib/vines/contact.rb +99 -0
  19. data/lib/vines/daemon.rb +78 -0
  20. data/lib/vines/error.rb +150 -0
  21. data/lib/vines/jid.rb +56 -0
  22. data/lib/vines/kit.rb +23 -0
  23. data/lib/vines/router.rb +125 -0
  24. data/lib/vines/stanza.rb +55 -0
  25. data/lib/vines/stanza/iq.rb +50 -0
  26. data/lib/vines/stanza/iq/auth.rb +18 -0
  27. data/lib/vines/stanza/iq/disco_info.rb +25 -0
  28. data/lib/vines/stanza/iq/disco_items.rb +23 -0
  29. data/lib/vines/stanza/iq/error.rb +16 -0
  30. data/lib/vines/stanza/iq/ping.rb +16 -0
  31. data/lib/vines/stanza/iq/query.rb +10 -0
  32. data/lib/vines/stanza/iq/result.rb +16 -0
  33. data/lib/vines/stanza/iq/roster.rb +153 -0
  34. data/lib/vines/stanza/iq/session.rb +22 -0
  35. data/lib/vines/stanza/iq/vcard.rb +58 -0
  36. data/lib/vines/stanza/message.rb +41 -0
  37. data/lib/vines/stanza/presence.rb +119 -0
  38. data/lib/vines/stanza/presence/error.rb +23 -0
  39. data/lib/vines/stanza/presence/probe.rb +38 -0
  40. data/lib/vines/stanza/presence/subscribe.rb +66 -0
  41. data/lib/vines/stanza/presence/subscribed.rb +64 -0
  42. data/lib/vines/stanza/presence/unavailable.rb +15 -0
  43. data/lib/vines/stanza/presence/unsubscribe.rb +57 -0
  44. data/lib/vines/stanza/presence/unsubscribed.rb +50 -0
  45. data/lib/vines/storage.rb +216 -0
  46. data/lib/vines/storage/couchdb.rb +119 -0
  47. data/lib/vines/storage/ldap.rb +59 -0
  48. data/lib/vines/storage/local.rb +66 -0
  49. data/lib/vines/storage/redis.rb +108 -0
  50. data/lib/vines/storage/sql.rb +174 -0
  51. data/lib/vines/store.rb +51 -0
  52. data/lib/vines/stream.rb +198 -0
  53. data/lib/vines/stream/client.rb +131 -0
  54. data/lib/vines/stream/client/auth.rb +94 -0
  55. data/lib/vines/stream/client/auth_restart.rb +33 -0
  56. data/lib/vines/stream/client/bind.rb +58 -0
  57. data/lib/vines/stream/client/bind_restart.rb +25 -0
  58. data/lib/vines/stream/client/closed.rb +13 -0
  59. data/lib/vines/stream/client/ready.rb +15 -0
  60. data/lib/vines/stream/client/start.rb +27 -0
  61. data/lib/vines/stream/client/tls.rb +37 -0
  62. data/lib/vines/stream/component.rb +53 -0
  63. data/lib/vines/stream/component/handshake.rb +25 -0
  64. data/lib/vines/stream/component/ready.rb +24 -0
  65. data/lib/vines/stream/component/start.rb +19 -0
  66. data/lib/vines/stream/http.rb +111 -0
  67. data/lib/vines/stream/http/http_request.rb +22 -0
  68. data/lib/vines/stream/http/http_state.rb +139 -0
  69. data/lib/vines/stream/http/http_states.rb +53 -0
  70. data/lib/vines/stream/parser.rb +78 -0
  71. data/lib/vines/stream/server.rb +126 -0
  72. data/lib/vines/stream/server/auth.rb +13 -0
  73. data/lib/vines/stream/server/auth_restart.rb +19 -0
  74. data/lib/vines/stream/server/final_restart.rb +20 -0
  75. data/lib/vines/stream/server/outbound/auth.rb +31 -0
  76. data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
  77. data/lib/vines/stream/server/outbound/auth_result.rb +28 -0
  78. data/lib/vines/stream/server/outbound/final_features.rb +27 -0
  79. data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
  80. data/lib/vines/stream/server/outbound/start.rb +20 -0
  81. data/lib/vines/stream/server/outbound/tls.rb +30 -0
  82. data/lib/vines/stream/server/outbound/tls_result.rb +31 -0
  83. data/lib/vines/stream/server/ready.rb +20 -0
  84. data/lib/vines/stream/server/start.rb +13 -0
  85. data/lib/vines/stream/server/tls.rb +13 -0
  86. data/lib/vines/stream/state.rb +55 -0
  87. data/lib/vines/token_bucket.rb +46 -0
  88. data/lib/vines/user.rb +124 -0
  89. data/lib/vines/version.rb +5 -0
  90. data/lib/vines/xmpp_server.rb +25 -0
  91. data/test/config_test.rb +396 -0
  92. data/test/error_test.rb +59 -0
  93. data/test/ext/nokogiri.rb +14 -0
  94. data/test/jid_test.rb +71 -0
  95. data/test/kit_test.rb +21 -0
  96. data/test/router_test.rb +60 -0
  97. data/test/stanza/iq/roster_test.rb +198 -0
  98. data/test/stanza/iq/session_test.rb +30 -0
  99. data/test/stanza/iq/vcard_test.rb +159 -0
  100. data/test/stanza/message_test.rb +124 -0
  101. data/test/stanza/presence/subscribe_test.rb +75 -0
  102. data/test/storage/couchdb_test.rb +102 -0
  103. data/test/storage/ldap_test.rb +207 -0
  104. data/test/storage/local_test.rb +54 -0
  105. data/test/storage/redis_test.rb +75 -0
  106. data/test/storage/sql_test.rb +55 -0
  107. data/test/storage/storage_tests.rb +134 -0
  108. data/test/storage_test.rb +90 -0
  109. data/test/stream/client/auth_test.rb +127 -0
  110. data/test/stream/client/ready_test.rb +47 -0
  111. data/test/stream/component/handshake_test.rb +46 -0
  112. data/test/stream/component/ready_test.rb +105 -0
  113. data/test/stream/component/start_test.rb +41 -0
  114. data/test/stream/parser_test.rb +121 -0
  115. data/test/stream/server/outbound/auth_test.rb +77 -0
  116. data/test/stream/server/ready_test.rb +100 -0
  117. data/test/token_bucket_test.rb +24 -0
  118. data/test/user_test.rb +64 -0
  119. metadata +318 -0
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+
3
+ module Nokogiri
4
+ module XML
5
+ class Node
6
+ # Override equality testing so we can use MiniTest::Mock#expect with
7
+ # Nokogiri::XML::Node arguments. Node's default behavior considers
8
+ # all nodes unequal.
9
+ def ==(node)
10
+ self.to_s == node.to_s
11
+ end
12
+ end
13
+ end
14
+ end
data/test/jid_test.rb ADDED
@@ -0,0 +1,71 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'test/unit'
5
+
6
+ class JidTest < Test::Unit::TestCase
7
+ def test_nil_and_empty_jids
8
+ [nil, ''].each do |text|
9
+ assert_nothing_raised { Vines::JID.new(text) }
10
+ jid = Vines::JID.new(text)
11
+ assert_nil jid.node
12
+ assert_nil jid.resource
13
+ assert_equal '', jid.domain
14
+ assert_equal '', jid.to_s
15
+ assert_equal '', jid.bare.to_s
16
+ end
17
+ end
18
+
19
+ def test_jid_too_long_error
20
+ assert_nothing_raised { Vines::JID.new('n' * 1023) }
21
+ assert_raises(ArgumentError) { Vines::JID.new('n' * 1024) }
22
+ assert_raises(ArgumentError) { Vines::JID.new('n', 'd' * 1024) }
23
+ assert_raises(ArgumentError) { Vines::JID.new('n', 'd', 'r' * 1024) }
24
+ assert_nothing_raised { Vines::JID.new('n' * 1023, 'd' * 1023, 'r' * 1023) }
25
+ end
26
+
27
+ def test_domain_only
28
+ jid = Vines::JID.new('wonderland.lit')
29
+ assert_equal 'wonderland.lit', jid.to_s
30
+ assert_equal 'wonderland.lit', jid.domain
31
+ assert_nil jid.node
32
+ assert_nil jid.resource
33
+ assert_equal jid, jid.bare
34
+ end
35
+
36
+ def test_bare_jid
37
+ jid = Vines::JID.new('alice', 'wonderland.lit')
38
+ assert_equal 'alice@wonderland.lit', jid.to_s
39
+ assert_equal 'wonderland.lit', jid.domain
40
+ assert_equal 'alice', jid.node
41
+ assert_nil jid.resource
42
+ assert_equal jid, jid.bare
43
+ end
44
+
45
+ def test_parsed_bare_jid
46
+ jid = Vines::JID.new('alice@wonderland.lit')
47
+ assert_equal 'alice@wonderland.lit', jid.to_s
48
+ assert_equal 'wonderland.lit', jid.domain
49
+ assert_equal 'alice', jid.node
50
+ assert_nil jid.resource
51
+ assert_equal jid, jid.bare
52
+ end
53
+
54
+ def test_full_jid
55
+ jid = Vines::JID.new('alice', 'wonderland.lit', 'tea')
56
+ assert_equal 'alice@wonderland.lit/tea', jid.to_s
57
+ assert_equal 'wonderland.lit', jid.domain
58
+ assert_equal 'alice', jid.node
59
+ assert_equal 'tea', jid.resource
60
+ assert_not_equal jid, jid.bare
61
+ end
62
+
63
+ def test_parsed_full_jid
64
+ jid = Vines::JID.new('alice@wonderland.lit/tea')
65
+ assert_equal 'alice@wonderland.lit/tea', jid.to_s
66
+ assert_equal 'wonderland.lit', jid.domain
67
+ assert_equal 'alice', jid.node
68
+ assert_equal 'tea', jid.resource
69
+ assert_not_equal jid, jid.bare
70
+ end
71
+ end
data/test/kit_test.rb ADDED
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'test/unit'
5
+
6
+ class KitTest < Test::Unit::TestCase
7
+ def test_hmac
8
+ assert_equal(128, Vines::Kit.hmac('secret', 'username').length)
9
+ assert_equal(Vines::Kit.hmac('s1', 'u1'), Vines::Kit.hmac('s1', 'u1'))
10
+ assert_not_equal(Vines::Kit.hmac('s1', 'u1'), Vines::Kit.hmac('s2', 'u1'))
11
+ assert_not_equal(Vines::Kit.hmac('s1', 'u1'), Vines::Kit.hmac('s1', 'u2'))
12
+ end
13
+
14
+ def test_uuid
15
+ ids = Array.new(1000) { Vines::Kit.uuid }
16
+ assert(ids.all? {|id| !id.nil? })
17
+ assert(ids.all? {|id| id.length == 36 })
18
+ assert(ids.all? {|id| id.match(/\w{8}-\w{4}-[4]\w{3}-[89ab]\w{3}-\w{12}/) })
19
+ assert_equal(ids.length, ids.uniq.length)
20
+ end
21
+ end
@@ -0,0 +1,60 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'minitest/mock'
5
+ require 'test/unit'
6
+
7
+ class RouterTest < Test::Unit::TestCase
8
+ def setup
9
+ @router = Vines::Router.new
10
+ end
11
+
12
+ def test_non_routable_stanza_is_local
13
+ stanza = MiniTest::Mock.new
14
+ stanza.expect(:name, 'auth')
15
+ assert @router.local?(stanza)
16
+ assert stanza.verify
17
+ end
18
+
19
+ def test_stanza_missing_to_is_local
20
+ stanza = MiniTest::Mock.new
21
+ stanza.expect(:name, 'message')
22
+ stanza.expect(:[], nil, ['to'])
23
+ assert @router.local?(stanza)
24
+ assert stanza.verify
25
+ end
26
+
27
+ def test_stanza_with_local_jid_is_local
28
+ config = MiniTest::Mock.new
29
+ config.expect(:vhost?, true, ['wonderland.lit'])
30
+ stream = MiniTest::Mock.new
31
+ stream.expect(:config, config)
32
+ @router << stream
33
+
34
+ stanza = MiniTest::Mock.new
35
+ stanza.expect(:name, 'message')
36
+ stanza.expect(:[], 'alice@wonderland.lit', ['to'])
37
+ assert @router.local?(stanza)
38
+
39
+ assert stanza.verify
40
+ assert stream.verify
41
+ assert config.verify
42
+ end
43
+
44
+ def test_stanza_with_remote_jid_is_not_local
45
+ config = MiniTest::Mock.new
46
+ config.expect(:vhost?, false, ['wonderland.lit'])
47
+ stream = MiniTest::Mock.new
48
+ stream.expect(:config, config)
49
+ @router << stream
50
+
51
+ stanza = MiniTest::Mock.new
52
+ stanza.expect(:name, 'message')
53
+ stanza.expect(:[], 'alice@wonderland.lit', ['to'])
54
+ assert !@router.local?(stanza)
55
+
56
+ assert stanza.verify
57
+ assert stream.verify
58
+ assert config.verify
59
+ end
60
+ end
@@ -0,0 +1,198 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'ext/nokogiri'
5
+ require 'minitest/mock'
6
+ require 'test/unit'
7
+
8
+ class RosterTest < Test::Unit::TestCase
9
+ def setup
10
+ @stream = MiniTest::Mock.new
11
+ end
12
+
13
+ def test_roster_get_with_empty_roster
14
+ alice = Vines::User.new(:jid => 'alice@wonderland.lit')
15
+ expected = node(%q{<iq id="42" type="result"><query xmlns="jabber:iq:roster"/></iq>})
16
+ @stream.expect(:write, nil, [expected])
17
+ @stream.expect(:requested_roster!, nil)
18
+ @stream.expect(:user, alice)
19
+
20
+ node = node(%q{<iq id="42" type="get"><query xmlns='jabber:iq:roster'/></iq>})
21
+ stanza = Vines::Stanza::Iq::Roster.new(node, @stream)
22
+ stanza.process
23
+ assert @stream.verify
24
+ end
25
+
26
+ def test_roster_get_with_non_empty_roster
27
+ alice = Vines::User.new(:jid => 'alice@wonderland.lit')
28
+ alice.roster << Vines::Contact.new(:jid => 'hatter@wonderland.lit')
29
+ alice.roster << Vines::Contact.new(:jid => 'cat@wonderland.lit', :groups => ['Friends', 'Cats'])
30
+
31
+ expected = node(%q{
32
+ <iq id="42" type="result">
33
+ <query xmlns="jabber:iq:roster">
34
+ <item jid="cat@wonderland.lit" subscription="none">
35
+ <group>Cats</group>
36
+ <group>Friends</group>
37
+ </item>
38
+ <item jid="hatter@wonderland.lit" subscription="none"/>
39
+ </query>
40
+ </iq>}.strip.gsub(/\n|\s{2,}/, ''))
41
+
42
+ @stream.expect(:write, nil, [expected])
43
+ @stream.expect(:requested_roster!, nil)
44
+ @stream.expect(:user, alice)
45
+
46
+ node = node(%q{<iq id="42" type="get"><query xmlns='jabber:iq:roster'/></iq>})
47
+ stanza = Vines::Stanza::Iq::Roster.new(node, @stream)
48
+ stanza.process
49
+ assert @stream.verify
50
+ end
51
+
52
+ def test_roster_set_with_invalid_to_address
53
+ alice = Vines::User.new(:jid => 'alice@wonderland.lit/tea')
54
+ @stream.expect(:user, alice)
55
+
56
+ node = node(%q{
57
+ <iq id="42" type="set" to="romeo@verona.lit">
58
+ <query xmlns="jabber:iq:roster"/>
59
+ </iq>}.strip.gsub(/\n|\s{2,}/, ''))
60
+
61
+ stanza = Vines::Stanza::Iq::Roster.new(node, @stream)
62
+ assert_raise(Vines::StanzaErrors::Forbidden) { stanza.process }
63
+ assert @stream.verify
64
+ end
65
+
66
+ def test_roster_set_with_no_items
67
+ node = node(%q{
68
+ <iq id="42" type="set">
69
+ <query xmlns="jabber:iq:roster"/>
70
+ </iq>}.strip.gsub(/\n|\s{2,}/, ''))
71
+
72
+ stanza = Vines::Stanza::Iq::Roster.new(node, @stream)
73
+ assert_raise(Vines::StanzaErrors::BadRequest) { stanza.process }
74
+ assert @stream.verify
75
+ end
76
+
77
+ def test_roster_set_with_two_items
78
+ node = node(%q{
79
+ <iq id="42" type="set">
80
+ <query xmlns="jabber:iq:roster">
81
+ <item jid="hatter@wonderland.lit"/>
82
+ <item jid="cat@wonderland.lit"/>
83
+ </query>
84
+ </iq>}.strip.gsub(/\n|\s{2,}/, ''))
85
+
86
+ stanza = Vines::Stanza::Iq::Roster.new(node, @stream)
87
+ assert_raise(Vines::StanzaErrors::BadRequest) { stanza.process }
88
+ assert @stream.verify
89
+ end
90
+
91
+ def test_roster_set_missing_jid
92
+ node = node(%q{
93
+ <iq id="42" type="set">
94
+ <query xmlns="jabber:iq:roster">
95
+ <item name="Mad Hatter"/>
96
+ </query>
97
+ </iq>}.strip.gsub(/\n|\s{2,}/, ''))
98
+
99
+ stanza = Vines::Stanza::Iq::Roster.new(node, @stream)
100
+ assert_raise(Vines::StanzaErrors::BadRequest) { stanza.process }
101
+ assert @stream.verify
102
+ end
103
+
104
+ def test_roster_set_with_duplicate_groups
105
+ alice = Vines::User.new(:jid => 'alice@wonderland.lit/tea')
106
+ @stream.expect(:user, alice)
107
+
108
+ node = node(%q{
109
+ <iq id="42" type="set">
110
+ <query xmlns="jabber:iq:roster">
111
+ <item jid="hatter@wonderland.lit" name="Mad Hatter">
112
+ <group>Friends</group>
113
+ <group>Friends</group>
114
+ </item>
115
+ </query>
116
+ </iq>}.strip.gsub(/\n|\s{2,}/, ''))
117
+
118
+ stanza = Vines::Stanza::Iq::Roster.new(node, @stream)
119
+ assert_raise(Vines::StanzaErrors::BadRequest) { stanza.process }
120
+ assert @stream.verify
121
+ end
122
+
123
+ def test_roster_set_with_empty_group
124
+ alice = Vines::User.new(:jid => 'alice@wonderland.lit/tea')
125
+ @stream.expect(:user, alice)
126
+
127
+ node = node(%q{
128
+ <iq id="42" type="set">
129
+ <query xmlns="jabber:iq:roster">
130
+ <item jid="hatter@wonderland.lit" name="Mad Hatter">
131
+ <group></group>
132
+ </item>
133
+ </query>
134
+ </iq>}.strip.gsub(/\n|\s{2,}/, ''))
135
+
136
+ stanza = Vines::Stanza::Iq::Roster.new(node, @stream)
137
+ assert_raise(Vines::StanzaErrors::NotAcceptable) { stanza.process }
138
+ assert @stream.verify
139
+ end
140
+
141
+ def test_roster_set_sends_results
142
+ alice = Vines::User.new(:jid => 'alice@wonderland.lit/tea')
143
+ storage = MiniTest::Mock.new
144
+ storage.expect(:save_user, nil, [alice])
145
+
146
+ recipient = MiniTest::Mock.new
147
+ recipient.expect(:user, alice)
148
+ def recipient.nodes; @nodes; end
149
+ def recipient.write(node)
150
+ @nodes ||= []
151
+ @nodes << node
152
+ end
153
+
154
+ router = MiniTest::Mock.new
155
+ router.expect(:interested_resources, [recipient], [alice.jid])
156
+
157
+ @stream.expect(:user, alice)
158
+ @stream.expect(:update_user_streams, nil, [alice])
159
+ @stream.expect(:router, router)
160
+ @stream.expect(:domain, 'wonderland.lit')
161
+ @stream.expect(:storage, storage, ['wonderland.lit'])
162
+ expected = node(%q{<iq id="42" type="result"/>})
163
+ @stream.expect(:write, nil, [expected])
164
+
165
+ node = node(%q{
166
+ <iq id="42" type="set">
167
+ <query xmlns="jabber:iq:roster">
168
+ <item jid="hatter@wonderland.lit" name="Mad Hatter">
169
+ <group>Friends</group>
170
+ </item>
171
+ </query>
172
+ </iq>}.strip.gsub(/\n|\s{2,}/, ''))
173
+
174
+ stanza = Vines::Stanza::Iq::Roster.new(node, @stream)
175
+ stanza.process
176
+ assert_nothing_raised { stanza.process }
177
+ assert @stream.verify
178
+ assert storage.verify
179
+ assert router.verify
180
+
181
+ expected = node(%q{
182
+ <iq type="set" to="alice@wonderland.lit/tea">
183
+ <query xmlns="jabber:iq:roster">
184
+ <item jid="hatter@wonderland.lit" name="Mad Hatter" subscription="none">
185
+ <group>Friends</group>
186
+ </item>
187
+ </query>
188
+ </iq>}.strip.gsub(/\n|\s{2,}/, ''))
189
+ recipient.nodes[0].remove_attribute('id') # id is random
190
+ assert_equal expected, recipient.nodes[0]
191
+ end
192
+
193
+ private
194
+
195
+ def node(xml)
196
+ Nokogiri::XML(xml).root
197
+ end
198
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'ext/nokogiri'
5
+ require 'minitest/mock'
6
+ require 'test/unit'
7
+
8
+ class SessionTest < Test::Unit::TestCase
9
+ def test_session
10
+ stream = MiniTest::Mock.new
11
+ stream.expect(:domain, 'wonderland.lit')
12
+ expected = node(%q{<iq from="wonderland.lit" id="42" type="result"/>})
13
+ stream.expect(:write, nil, [expected])
14
+
15
+ node = node(
16
+ %q{<iq id="42" to="wonderland.lit" type="set">
17
+ <session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>
18
+ </iq>})
19
+
20
+ stanza = Vines::Stanza::Iq::Session.new(node, stream)
21
+ stanza.process
22
+ assert stream.verify
23
+ end
24
+
25
+ private
26
+
27
+ def node(xml)
28
+ Nokogiri::XML(xml).root
29
+ end
30
+ end
@@ -0,0 +1,159 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'ext/nokogiri'
5
+ require 'minitest/mock'
6
+ require 'test/unit'
7
+
8
+ class VcardTest < Test::Unit::TestCase
9
+ def setup
10
+ @stream = MiniTest::Mock.new
11
+ end
12
+
13
+ def test_vcard_get_on_remote_jid_routes
14
+ alice = Vines::User.new(:jid => 'alice@wonderland.lit/tea')
15
+ node = node(%q{<iq id="42" to="romeo@verona.lit" type="get"><vCard xmlns="vcard-temp"/></iq>})
16
+
17
+ router = MiniTest::Mock.new
18
+ router.expect(:local?, false, [node])
19
+ router.expect(:route, nil, [node])
20
+
21
+ @stream.expect(:user, alice)
22
+ @stream.expect(:router, router)
23
+
24
+ stanza = Vines::Stanza::Iq::Vcard.new(node, @stream)
25
+ stanza.process
26
+ assert @stream.verify
27
+ assert router.verify
28
+ end
29
+
30
+ def test_vcard_get_missing_to_address
31
+ alice = Vines::User.new(:jid => 'alice@wonderland.lit/tea')
32
+ node = node(%q{<iq id="42" type="get"><vCard xmlns="vcard-temp"/></iq>})
33
+
34
+ router = MiniTest::Mock.new
35
+ router.expect(:local?, true, [node])
36
+
37
+ card = node(%q{<vCard xmlns="vcard-temp"><FN>Alice in Wonderland</FN></vCard>})
38
+
39
+ storage = MiniTest::Mock.new
40
+ storage.expect(:find_vcard, card, [alice.jid.bare])
41
+
42
+ @stream.expect(:user, alice)
43
+ @stream.expect(:router, router)
44
+ @stream.expect(:domain, 'wonderland.lit')
45
+ @stream.expect(:storage, storage, ['wonderland.lit'])
46
+ expected = node(%q{
47
+ <iq id="42" to="alice@wonderland.lit/tea" type="result">
48
+ <vCard xmlns="vcard-temp">
49
+ <FN>Alice in Wonderland</FN>
50
+ </vCard>
51
+ </iq>}.strip.gsub(/\n|\s{2,}/, ''))
52
+ @stream.expect(:write, nil, [expected])
53
+
54
+ stanza = Vines::Stanza::Iq::Vcard.new(node, @stream)
55
+ stanza.process
56
+ assert @stream.verify
57
+ assert router.verify
58
+ assert storage.verify
59
+ end
60
+
61
+ def test_vcard_get_another_user_includes_from_address
62
+ alice = Vines::User.new(:jid => 'alice@wonderland.lit/tea')
63
+ node = node(%q{<iq id="42" to="hatter@wonderland.lit" type="get"><vCard xmlns="vcard-temp"/></iq>})
64
+
65
+ router = MiniTest::Mock.new
66
+ router.expect(:local?, true, [node])
67
+
68
+ card = node(%q{<vCard xmlns="vcard-temp"><FN>Mad Hatter</FN></vCard>})
69
+
70
+ storage = MiniTest::Mock.new
71
+ storage.expect(:find_vcard, card, [Vines::JID.new('hatter@wonderland.lit')])
72
+
73
+ @stream.expect(:user, alice)
74
+ @stream.expect(:router, router)
75
+ @stream.expect(:domain, 'wonderland.lit')
76
+ @stream.expect(:storage, storage, ['wonderland.lit'])
77
+ expected = node(%q{
78
+ <iq from="hatter@wonderland.lit" id="42" to="alice@wonderland.lit/tea" type="result">
79
+ <vCard xmlns="vcard-temp">
80
+ <FN>Mad Hatter</FN>
81
+ </vCard>
82
+ </iq>}.strip.gsub(/\n|\s{2,}/, ''))
83
+ @stream.expect(:write, nil, [expected])
84
+
85
+ stanza = Vines::Stanza::Iq::Vcard.new(node, @stream)
86
+ stanza.process
87
+ assert @stream.verify
88
+ assert router.verify
89
+ assert storage.verify
90
+ end
91
+
92
+ def test_missing_vcard_get_returns_item_not_found
93
+ alice = Vines::User.new(:jid => 'alice@wonderland.lit/tea')
94
+ node = node(%q{<iq id="42" type="get"><vCard xmlns="vcard-temp"/></iq>})
95
+
96
+ router = MiniTest::Mock.new
97
+ router.expect(:local?, true, [node])
98
+
99
+ storage = MiniTest::Mock.new
100
+ storage.expect(:find_vcard, nil, [alice.jid.bare])
101
+
102
+ @stream.expect(:user, alice)
103
+ @stream.expect(:router, router)
104
+ @stream.expect(:domain, 'wonderland.lit')
105
+ @stream.expect(:storage, storage, ['wonderland.lit'])
106
+
107
+ stanza = Vines::Stanza::Iq::Vcard.new(node, @stream)
108
+ assert_raise(Vines::StanzaErrors::ItemNotFound) { stanza.process }
109
+ assert @stream.verify
110
+ assert router.verify
111
+ assert storage.verify
112
+ end
113
+
114
+ def test_vcard_set_on_another_user_returns_forbidden
115
+ alice = Vines::User.new(:jid => 'alice@wonderland.lit/tea')
116
+ node = node(%q{<iq id="42" to="hatter@wonderland.lit" type="set"><vCard xmlns="vcard-temp"><FN>Alice</FN></vCard></iq>})
117
+
118
+ router = MiniTest::Mock.new
119
+ router.expect(:local?, true, [node])
120
+
121
+ @stream.expect(:user, alice)
122
+ @stream.expect(:router, router)
123
+
124
+ stanza = Vines::Stanza::Iq::Vcard.new(node, @stream)
125
+ assert_raise(Vines::StanzaErrors::Forbidden) { stanza.process }
126
+ assert @stream.verify
127
+ end
128
+
129
+ def test_vcard_set_returns_result
130
+ alice = Vines::User.new(:jid => 'alice@wonderland.lit/tea')
131
+ node = node(%q{<iq id="42" type="set"><vCard xmlns="vcard-temp"><FN>Alice</FN></vCard></iq>})
132
+ card = node(%q{<vCard xmlns="vcard-temp"><FN>Alice</FN></vCard>})
133
+
134
+ router = MiniTest::Mock.new
135
+ router.expect(:local?, true, [node])
136
+
137
+ storage = MiniTest::Mock.new
138
+ storage.expect(:save_vcard, nil, [alice.jid, card])
139
+
140
+ @stream.expect(:user, alice)
141
+ @stream.expect(:router, router)
142
+ @stream.expect(:domain, 'wonderland.lit')
143
+ @stream.expect(:storage, storage, ['wonderland.lit'])
144
+ expected = node(%q{<iq id="42" to="alice@wonderland.lit/tea" type="result"/>})
145
+ @stream.expect(:write, nil, [expected])
146
+
147
+ stanza = Vines::Stanza::Iq::Vcard.new(node, @stream)
148
+ stanza.process
149
+ assert @stream.verify
150
+ assert router.verify
151
+ assert storage.verify
152
+ end
153
+
154
+ private
155
+
156
+ def node(xml)
157
+ Nokogiri::XML(xml).root
158
+ end
159
+ end