vines 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. data/README +5 -9
  2. data/Rakefile +11 -9
  3. data/conf/config.rb +30 -4
  4. data/lib/vines/cluster/connection.rb +26 -0
  5. data/lib/vines/cluster/publisher.rb +55 -0
  6. data/lib/vines/cluster/pubsub.rb +92 -0
  7. data/lib/vines/cluster/sessions.rb +125 -0
  8. data/lib/vines/cluster/subscriber.rb +108 -0
  9. data/lib/vines/cluster.rb +246 -0
  10. data/lib/vines/command/init.rb +21 -24
  11. data/lib/vines/config/host.rb +48 -8
  12. data/lib/vines/config/port.rb +5 -0
  13. data/lib/vines/config/pubsub.rb +108 -0
  14. data/lib/vines/config.rb +74 -20
  15. data/lib/vines/jid.rb +14 -0
  16. data/lib/vines/router.rb +69 -55
  17. data/lib/vines/stanza/iq/disco_info.rb +22 -9
  18. data/lib/vines/stanza/iq/disco_items.rb +6 -3
  19. data/lib/vines/stanza/iq/ping.rb +1 -1
  20. data/lib/vines/stanza/iq/private_storage.rb +4 -8
  21. data/lib/vines/stanza/iq/roster.rb +6 -14
  22. data/lib/vines/stanza/iq/session.rb +2 -7
  23. data/lib/vines/stanza/iq/vcard.rb +4 -6
  24. data/lib/vines/stanza/iq/version.rb +1 -1
  25. data/lib/vines/stanza/iq.rb +8 -10
  26. data/lib/vines/stanza/presence/subscribe.rb +3 -11
  27. data/lib/vines/stanza/presence/subscribed.rb +16 -29
  28. data/lib/vines/stanza/presence/unsubscribe.rb +3 -15
  29. data/lib/vines/stanza/presence/unsubscribed.rb +3 -16
  30. data/lib/vines/stanza/presence.rb +30 -0
  31. data/lib/vines/stanza/pubsub/create.rb +39 -0
  32. data/lib/vines/stanza/pubsub/delete.rb +41 -0
  33. data/lib/vines/stanza/pubsub/publish.rb +66 -0
  34. data/lib/vines/stanza/pubsub/subscribe.rb +44 -0
  35. data/lib/vines/stanza/pubsub/unsubscribe.rb +30 -0
  36. data/lib/vines/stanza/pubsub.rb +22 -0
  37. data/lib/vines/stanza.rb +72 -22
  38. data/lib/vines/storage/couchdb.rb +46 -65
  39. data/lib/vines/storage/local.rb +20 -14
  40. data/lib/vines/storage/mongodb.rb +132 -0
  41. data/lib/vines/storage/null.rb +39 -0
  42. data/lib/vines/storage/redis.rb +61 -68
  43. data/lib/vines/storage/sql.rb +73 -69
  44. data/lib/vines/storage.rb +1 -1
  45. data/lib/vines/stream/client/bind.rb +2 -2
  46. data/lib/vines/stream/client/session.rb +71 -16
  47. data/lib/vines/stream/component/handshake.rb +1 -0
  48. data/lib/vines/stream/component/ready.rb +2 -2
  49. data/lib/vines/stream/http/session.rb +2 -0
  50. data/lib/vines/stream/http.rb +0 -6
  51. data/lib/vines/stream/server/final_restart.rb +1 -0
  52. data/lib/vines/stream/server/outbound/final_features.rb +1 -0
  53. data/lib/vines/stream/server/ready.rb +6 -2
  54. data/lib/vines/stream/server.rb +4 -3
  55. data/lib/vines/stream.rb +10 -6
  56. data/lib/vines/version.rb +1 -1
  57. data/lib/vines.rb +48 -22
  58. data/test/cluster/publisher_test.rb +45 -0
  59. data/test/cluster/sessions_test.rb +54 -0
  60. data/test/cluster/subscriber_test.rb +94 -0
  61. data/test/config/host_test.rb +100 -21
  62. data/test/config/pubsub_test.rb +181 -0
  63. data/test/config_test.rb +225 -43
  64. data/test/jid_test.rb +7 -0
  65. data/test/router_test.rb +181 -9
  66. data/test/stanza/iq/disco_info_test.rb +8 -6
  67. data/test/stanza/iq/disco_items_test.rb +3 -3
  68. data/test/stanza/iq/private_storage_test.rb +8 -19
  69. data/test/stanza/iq/roster_test.rb +1 -1
  70. data/test/stanza/iq/session_test.rb +3 -6
  71. data/test/stanza/iq/vcard_test.rb +6 -2
  72. data/test/stanza/iq/version_test.rb +3 -2
  73. data/test/stanza/iq_test.rb +5 -5
  74. data/test/stanza/message_test.rb +3 -2
  75. data/test/stanza/presence/probe_test.rb +2 -1
  76. data/test/stanza/pubsub/create_test.rb +138 -0
  77. data/test/stanza/pubsub/delete_test.rb +142 -0
  78. data/test/stanza/pubsub/publish_test.rb +373 -0
  79. data/test/stanza/pubsub/subscribe_test.rb +186 -0
  80. data/test/stanza/pubsub/unsubscribe_test.rb +179 -0
  81. data/test/stanza_test.rb +2 -1
  82. data/test/storage/local_test.rb +26 -25
  83. data/test/storage/mock_mongo.rb +40 -0
  84. data/test/storage/mock_redis.rb +98 -0
  85. data/test/storage/mongodb_test.rb +81 -0
  86. data/test/storage/null_test.rb +30 -0
  87. data/test/storage/redis_test.rb +3 -36
  88. data/test/stream/component/handshake_test.rb +4 -0
  89. data/test/stream/component/ready_test.rb +2 -1
  90. data/test/stream/server/ready_test.rb +7 -1
  91. data/web/404.html +5 -3
  92. data/web/chat/coffeescripts/chat.coffee +9 -5
  93. data/web/chat/javascripts/app.js +1 -1
  94. data/web/chat/javascripts/chat.js +14 -8
  95. data/web/chat/stylesheets/chat.css +4 -1
  96. data/web/lib/coffeescripts/button.coffee +9 -5
  97. data/web/lib/coffeescripts/filter.coffee +1 -1
  98. data/web/lib/coffeescripts/login.coffee +14 -1
  99. data/web/lib/coffeescripts/session.coffee +8 -11
  100. data/web/lib/images/dark-gray.png +0 -0
  101. data/web/lib/images/light-gray.png +0 -0
  102. data/web/lib/images/logo-large.png +0 -0
  103. data/web/lib/images/logo-small.png +0 -0
  104. data/web/lib/images/white.png +0 -0
  105. data/web/lib/javascripts/base.js +9 -8
  106. data/web/lib/javascripts/button.js +20 -12
  107. data/web/lib/javascripts/filter.js +1 -1
  108. data/web/lib/javascripts/icons.js +7 -1
  109. data/web/lib/javascripts/jquery.js +4 -4
  110. data/web/lib/javascripts/login.js +16 -2
  111. data/web/lib/javascripts/raphael.js +5 -7
  112. data/web/lib/javascripts/session.js +10 -14
  113. data/web/lib/stylesheets/base.css +7 -11
  114. data/web/lib/stylesheets/login.css +31 -27
  115. metadata +100 -34
@@ -0,0 +1,138 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'tmpdir'
4
+ require 'vines'
5
+ require 'ext/nokogiri'
6
+ require 'minitest/autorun'
7
+
8
+ class CreatePubSubTest < MiniTest::Unit::TestCase
9
+ def setup
10
+ @user = Vines::User.new(jid: 'alice@wonderland.lit/tea')
11
+ @config = Vines::Config.new do
12
+ host 'wonderland.lit' do
13
+ storage(:fs) { dir Dir.tmpdir }
14
+ pubsub 'games'
15
+ end
16
+ end
17
+ @stream = MiniTest::Mock.new
18
+ @stream.expect(:config, @config)
19
+ @stream.expect(:user, @user)
20
+ end
21
+
22
+ def test_missing_to_address_raises
23
+ node = node(%q{
24
+ <iq type='set' id='42'>
25
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
26
+ <create node='game_13'/>
27
+ </pubsub>
28
+ </iq>
29
+ }.strip.gsub(/\n|\s{2,}/, ''))
30
+
31
+ @stream.expect(:domain, 'wonderland.lit')
32
+
33
+ stanza = Vines::Stanza::PubSub::Create.new(node, @stream)
34
+ assert_raises(Vines::StanzaErrors::FeatureNotImplemented) { stanza.process }
35
+ assert @stream.verify
36
+ end
37
+
38
+ def test_server_domain_to_address_raises
39
+ node = node(%q{
40
+ <iq type='set' to='wonderland.lit' id='42'>
41
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
42
+ <create node='game_13'/>
43
+ </pubsub>
44
+ </iq>
45
+ }.strip.gsub(/\n|\s{2,}/, ''))
46
+
47
+ stanza = Vines::Stanza::PubSub::Create.new(node, @stream)
48
+ assert_raises(Vines::StanzaErrors::FeatureNotImplemented) { stanza.process }
49
+ assert @stream.verify
50
+ end
51
+
52
+ def test_non_pubsub_to_address_routes
53
+ node = node(%q{
54
+ <iq type='set' to='bogus.wonderland.lit' id='42'>
55
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
56
+ <create node='game_13'/>
57
+ </pubsub>
58
+ </iq>
59
+ }.strip.gsub(/\n|\s{2,}/, ''))
60
+
61
+ router = MiniTest::Mock.new
62
+ router.expect(:route, nil, [node])
63
+ @stream.expect(:router, router)
64
+
65
+ stanza = Vines::Stanza::PubSub::Create.new(node, @stream)
66
+ stanza.process
67
+ assert @stream.verify
68
+ assert router.verify
69
+ end
70
+
71
+ def test_multiple_create_elements_raises
72
+ node = node(%q{
73
+ <iq type='set' to='games.wonderland.lit' id='42'>
74
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
75
+ <create node='game_13'/>
76
+ <create node='game_14'/>
77
+ </pubsub>
78
+ </iq>
79
+ }.strip.gsub(/\n|\s{2,}/, ''))
80
+
81
+ stanza = Vines::Stanza::PubSub::Create.new(node, @stream)
82
+ assert_raises(Vines::StanzaErrors::BadRequest) { stanza.process }
83
+ assert @stream.verify
84
+ end
85
+
86
+ def test_create_duplicate_node_raises
87
+ node = node(%q{
88
+ <iq type='set' to='games.wonderland.lit' id='42'>
89
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
90
+ <create node='game_13'/>
91
+ </pubsub>
92
+ </iq>
93
+ }.strip.gsub(/\n|\s{2,}/, ''))
94
+
95
+ stanza = Vines::Stanza::PubSub::Create.new(node, @stream)
96
+ def stanza.pubsub
97
+ pubsub = MiniTest::Mock.new
98
+ pubsub.expect(:node?, true, ['game_13'])
99
+ pubsub
100
+ end
101
+ assert_raises(Vines::StanzaErrors::Conflict) { stanza.process }
102
+ assert @stream.verify
103
+ end
104
+
105
+ def test_good_stanza_processes
106
+ node = node(%q{
107
+ <iq type='set' to='games.wonderland.lit' id='42'>
108
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
109
+ <create node='game_13'/>
110
+ </pubsub>
111
+ </iq>
112
+ }.strip.gsub(/\n|\s{2,}/, ''))
113
+
114
+ def @stream.nodes; @nodes; end
115
+ def @stream.write(node)
116
+ @nodes ||= []
117
+ @nodes << node
118
+ end
119
+
120
+ stanza = Vines::Stanza::PubSub::Create.new(node, @stream)
121
+ stanza.process
122
+
123
+ assert @stream.verify
124
+ assert_equal 1, @stream.nodes.size
125
+
126
+ expected = %q{<iq from="games.wonderland.lit" id="42" to="alice@wonderland.lit/tea" type="result">}
127
+ expected << %q{<pubsub xmlns="http://jabber.org/protocol/pubsub"><create node="game_13"/></pubsub>}
128
+ expected << %q{</iq>}
129
+ expected = node(expected)
130
+ assert_equal expected, @stream.nodes[0]
131
+ end
132
+
133
+ private
134
+
135
+ def node(xml)
136
+ Nokogiri::XML(xml).root
137
+ end
138
+ end
@@ -0,0 +1,142 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'tmpdir'
4
+ require 'vines'
5
+ require 'ext/nokogiri'
6
+ require 'minitest/autorun'
7
+
8
+ class DeletePubSubTest < MiniTest::Unit::TestCase
9
+ def setup
10
+ @user = Vines::User.new(jid: 'alice@wonderland.lit/tea')
11
+ @config = Vines::Config.new do
12
+ host 'wonderland.lit' do
13
+ storage(:fs) { dir Dir.tmpdir }
14
+ pubsub 'games'
15
+ end
16
+ end
17
+ @stream = MiniTest::Mock.new
18
+ @stream.expect(:config, @config)
19
+ @stream.expect(:user, @user)
20
+ end
21
+
22
+ def test_missing_to_address_raises
23
+ node = node(%q{
24
+ <iq type='set' id='42'>
25
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
26
+ <delete node='game_13'/>
27
+ </pubsub>
28
+ </iq>
29
+ }.strip.gsub(/\n|\s{2,}/, ''))
30
+
31
+ @stream.expect(:domain, 'wonderland.lit')
32
+
33
+ stanza = Vines::Stanza::PubSub::Delete.new(node, @stream)
34
+ assert_raises(Vines::StanzaErrors::FeatureNotImplemented) { stanza.process }
35
+ assert @stream.verify
36
+ end
37
+
38
+ def test_server_domain_to_address_raises
39
+ node = node(%q{
40
+ <iq type='set' to='wonderland.lit' id='42'>
41
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
42
+ <delete node='game_13'/>
43
+ </pubsub>
44
+ </iq>
45
+ }.strip.gsub(/\n|\s{2,}/, ''))
46
+
47
+ stanza = Vines::Stanza::PubSub::Delete.new(node, @stream)
48
+ assert_raises(Vines::StanzaErrors::FeatureNotImplemented) { stanza.process }
49
+ assert @stream.verify
50
+ end
51
+
52
+ def test_non_pubsub_to_address_routes
53
+ node = node(%q{
54
+ <iq type='set' to='bogus.wonderland.lit' id='42'>
55
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
56
+ <delete node='game_13'/>
57
+ </pubsub>
58
+ </iq>
59
+ }.strip.gsub(/\n|\s{2,}/, ''))
60
+
61
+ router = MiniTest::Mock.new
62
+ router.expect(:route, nil, [node])
63
+ @stream.expect(:router, router)
64
+
65
+ stanza = Vines::Stanza::PubSub::Delete.new(node, @stream)
66
+ stanza.process
67
+ assert @stream.verify
68
+ assert router.verify
69
+ end
70
+
71
+ def test_multiple_delete_elements_raises
72
+ node = node(%q{
73
+ <iq type='set' to='games.wonderland.lit' id='42'>
74
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
75
+ <delete node='game_13'/>
76
+ <delete node='game_14'/>
77
+ </pubsub>
78
+ </iq>
79
+ }.strip.gsub(/\n|\s{2,}/, ''))
80
+
81
+ stanza = Vines::Stanza::PubSub::Delete.new(node, @stream)
82
+ assert_raises(Vines::StanzaErrors::BadRequest) { stanza.process }
83
+ assert @stream.verify
84
+ end
85
+
86
+ def test_delete_missing_node_raises
87
+ node = node(%q{
88
+ <iq type='set' to='games.wonderland.lit' id='42'>
89
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
90
+ <delete node='game_13'/>
91
+ </pubsub>
92
+ </iq>
93
+ }.strip.gsub(/\n|\s{2,}/, ''))
94
+
95
+ stanza = Vines::Stanza::PubSub::Delete.new(node, @stream)
96
+ assert_raises(Vines::StanzaErrors::ItemNotFound) { stanza.process }
97
+ assert @stream.verify
98
+ end
99
+
100
+ def test_good_stanza_processes
101
+ node = node(%q{
102
+ <iq type='set' to='games.wonderland.lit' id='42'>
103
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
104
+ <delete node='game_13'/>
105
+ </pubsub>
106
+ </iq>
107
+ }.strip.gsub(/\n|\s{2,}/, ''))
108
+
109
+ def @stream.nodes; @nodes; end
110
+ def @stream.write(node)
111
+ @nodes ||= []
112
+ @nodes << node
113
+ end
114
+
115
+ stanza = Vines::Stanza::PubSub::Delete.new(node, @stream)
116
+ def stanza.mock_pubsub; @mock_pubsub; end
117
+ def stanza.pubsub
118
+ unless @mock_pubsub
119
+ xml = %q{<message><event xmlns="http://jabber.org/protocol/pubsub#event"><delete node="game_13"/></event></message>}
120
+ @mock_pubsub = MiniTest::Mock.new
121
+ @mock_pubsub.expect(:node?, true, ['game_13'])
122
+ @mock_pubsub.expect(:publish, nil, ['game_13', Nokogiri::XML(xml).root])
123
+ @mock_pubsub.expect(:delete_node, nil, ['game_13'])
124
+ end
125
+ @mock_pubsub
126
+ end
127
+ stanza.process
128
+
129
+ assert @stream.verify
130
+ assert stanza.mock_pubsub.verify
131
+ assert_equal 1, @stream.nodes.size
132
+
133
+ expected = node(%q{<iq from="games.wonderland.lit" id="42" to="alice@wonderland.lit/tea" type="result"/>})
134
+ assert_equal expected, @stream.nodes[0]
135
+ end
136
+
137
+ private
138
+
139
+ def node(xml)
140
+ Nokogiri::XML(xml).root
141
+ end
142
+ end
@@ -0,0 +1,373 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'tmpdir'
4
+ require 'vines'
5
+ require 'ext/nokogiri'
6
+ require 'minitest/autorun'
7
+
8
+ class PublishPubSubTest < MiniTest::Unit::TestCase
9
+ def setup
10
+ @user = Vines::User.new(jid: 'alice@wonderland.lit/tea')
11
+ @config = Vines::Config.new do
12
+ host 'wonderland.lit' do
13
+ storage(:fs) { dir Dir.tmpdir }
14
+ pubsub 'games'
15
+ end
16
+ end
17
+ @stream = MiniTest::Mock.new
18
+ @stream.expect(:config, @config)
19
+ @stream.expect(:user, @user)
20
+ end
21
+
22
+ def test_missing_to_address_raises
23
+ node = node(%q{
24
+ <iq type='set' id='42'>
25
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
26
+ <publish node='game_13'>
27
+ <item id='item_42'>
28
+ <entry xmlns='http://www.w3.org/2005/Atom'>
29
+ <title>Test</title>
30
+ <summary>This is a summary.</summary>
31
+ </entry>
32
+ </item>
33
+ </publish>
34
+ </pubsub>
35
+ </iq>
36
+ }.strip.gsub(/\n|\s{2,}/, ''))
37
+
38
+ @stream.expect(:domain, 'wonderland.lit')
39
+
40
+ stanza = Vines::Stanza::PubSub::Publish.new(node, @stream)
41
+ assert_raises(Vines::StanzaErrors::FeatureNotImplemented) { stanza.process }
42
+ assert @stream.verify
43
+ end
44
+
45
+ def test_server_domain_to_address_raises
46
+ node = node(%q{
47
+ <iq type='set' to='wonderland.lit' id='42'>
48
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
49
+ <publish node='game_13'>
50
+ <item id='item_42'>
51
+ <entry xmlns='http://www.w3.org/2005/Atom'>
52
+ <title>Test</title>
53
+ <summary>This is a summary.</summary>
54
+ </entry>
55
+ </item>
56
+ </publish>
57
+ </pubsub>
58
+ </iq>
59
+ }.strip.gsub(/\n|\s{2,}/, ''))
60
+
61
+ stanza = Vines::Stanza::PubSub::Publish.new(node, @stream)
62
+ assert_raises(Vines::StanzaErrors::FeatureNotImplemented) { stanza.process }
63
+ assert @stream.verify
64
+ end
65
+
66
+ def test_non_pubsub_to_address_routes
67
+ node = node(%q{
68
+ <iq type='set' to='bogus.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
+ </pubsub>
79
+ </iq>
80
+ }.strip.gsub(/\n|\s{2,}/, ''))
81
+
82
+ router = MiniTest::Mock.new
83
+ router.expect(:route, nil, [node])
84
+ @stream.expect(:router, router)
85
+
86
+ stanza = Vines::Stanza::PubSub::Publish.new(node, @stream)
87
+ stanza.process
88
+ assert @stream.verify
89
+ assert router.verify
90
+ end
91
+
92
+ def test_multiple_publish_elements_raises
93
+ node = node(%q{
94
+ <iq type='set' to='games.wonderland.lit' id='42'>
95
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
96
+ <publish node='game_13'>
97
+ <item id='item_42'>
98
+ <entry xmlns='http://www.w3.org/2005/Atom'>
99
+ <title>Test</title>
100
+ <summary>This is a summary.</summary>
101
+ </entry>
102
+ </item>
103
+ </publish>
104
+ <publish node='game_13'>
105
+ <item id='item_42'>
106
+ <entry xmlns='http://www.w3.org/2005/Atom'>
107
+ <title>Test</title>
108
+ <summary>This is a summary.</summary>
109
+ </entry>
110
+ </item>
111
+ </publish>
112
+ </pubsub>
113
+ </iq>
114
+ }.strip.gsub(/\n|\s{2,}/, ''))
115
+
116
+ stanza = Vines::Stanza::PubSub::Publish.new(node, @stream)
117
+ assert_raises(Vines::StanzaErrors::BadRequest) { stanza.process }
118
+ assert @stream.verify
119
+ end
120
+
121
+ def test_multiple_item_elements_raises
122
+ node = node(%q{
123
+ <iq type='set' to='games.wonderland.lit' id='42'>
124
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
125
+ <publish node='game_13'>
126
+ <item id='item_42'>
127
+ <entry xmlns='http://www.w3.org/2005/Atom'>
128
+ <title>Test</title>
129
+ <summary>This is a summary.</summary>
130
+ </entry>
131
+ </item>
132
+ <item id="item_43">bad</item>
133
+ </publish>
134
+ </pubsub>
135
+ </iq>
136
+ }.strip.gsub(/\n|\s{2,}/, ''))
137
+
138
+ stanza = Vines::Stanza::PubSub::Publish.new(node, @stream)
139
+ def stanza.mock_pubsub; @mock_pubsub; end
140
+ def stanza.pubsub
141
+ unless @mock_pubsub
142
+ @mock_pubsub = MiniTest::Mock.new
143
+ @mock_pubsub.expect(:node?, true, ['game_13'])
144
+ end
145
+ @mock_pubsub
146
+ end
147
+ assert_raises(Vines::StanzaErrors::BadRequest) { stanza.process }
148
+ assert @stream.verify
149
+ assert stanza.mock_pubsub.verify
150
+ end
151
+
152
+ def test_multiple_payload_elements_raises
153
+ node = node(%q{
154
+ <iq type='set' to='games.wonderland.lit' id='42'>
155
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
156
+ <publish node='game_13'>
157
+ <item id='item_42'>
158
+ <entry xmlns='http://www.w3.org/2005/Atom'>
159
+ <title>Test</title>
160
+ <summary>This is a summary.</summary>
161
+ </entry>
162
+ <entry>bad</entry>
163
+ </item>
164
+ </publish>
165
+ </pubsub>
166
+ </iq>
167
+ }.strip.gsub(/\n|\s{2,}/, ''))
168
+
169
+ stanza = Vines::Stanza::PubSub::Publish.new(node, @stream)
170
+ def stanza.mock_pubsub; @mock_pubsub; end
171
+ def stanza.pubsub
172
+ unless @mock_pubsub
173
+ @mock_pubsub = MiniTest::Mock.new
174
+ @mock_pubsub.expect(:node?, true, ['game_13'])
175
+ end
176
+ @mock_pubsub
177
+ end
178
+ assert_raises(Vines::StanzaErrors::BadRequest) { stanza.process }
179
+ assert @stream.verify
180
+ assert stanza.mock_pubsub.verify
181
+ end
182
+
183
+ def test_no_payload_elements_raises
184
+ node = node(%q{
185
+ <iq type='set' to='games.wonderland.lit' id='42'>
186
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
187
+ <publish node='game_13'>
188
+ <item id='item_42'>
189
+ </item>
190
+ </publish>
191
+ </pubsub>
192
+ </iq>
193
+ }.strip.gsub(/\n|\s{2,}/, ''))
194
+
195
+ stanza = Vines::Stanza::PubSub::Publish.new(node, @stream)
196
+ def stanza.mock_pubsub; @mock_pubsub; end
197
+ def stanza.pubsub
198
+ unless @mock_pubsub
199
+ @mock_pubsub = MiniTest::Mock.new
200
+ @mock_pubsub.expect(:node?, true, ['game_13'])
201
+ end
202
+ @mock_pubsub
203
+ end
204
+
205
+ assert_raises(Vines::StanzaErrors::BadRequest) { stanza.process }
206
+ assert @stream.verify
207
+ assert stanza.mock_pubsub.verify
208
+ end
209
+
210
+ def test_publish_to_missing_node_raises
211
+ node = node(%q{
212
+ <iq type='set' to='games.wonderland.lit' id='42'>
213
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
214
+ <publish node='game_13'>
215
+ <item id='item_42'>
216
+ <entry xmlns='http://www.w3.org/2005/Atom'>
217
+ <title>Test</title>
218
+ <summary>This is a summary.</summary>
219
+ </entry>
220
+ </item>
221
+ </publish>
222
+ </pubsub>
223
+ </iq>
224
+ }.strip.gsub(/\n|\s{2,}/, ''))
225
+
226
+ stanza = Vines::Stanza::PubSub::Publish.new(node, @stream)
227
+ assert_raises(Vines::StanzaErrors::ItemNotFound) { stanza.process }
228
+ assert @stream.verify
229
+ end
230
+
231
+ def test_generate_item_id
232
+ node = node(%q{
233
+ <iq type='set' to='games.wonderland.lit' id='42'>
234
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
235
+ <publish node='game_13'>
236
+ <item>
237
+ <entry xmlns='http://www.w3.org/2005/Atom'>
238
+ <title>Test</title>
239
+ <summary>This is a summary.</summary>
240
+ </entry>
241
+ </item>
242
+ </publish>
243
+ </pubsub>
244
+ </iq>
245
+ }.strip.gsub(/\n|\s{2,}/, ''))
246
+
247
+ def @stream.nodes; @nodes; end
248
+ def @stream.write(node)
249
+ @nodes ||= []
250
+ @nodes << node
251
+ end
252
+
253
+ stanza = Vines::Stanza::PubSub::Publish.new(node, @stream)
254
+ def stanza.mock_pubsub; @mock_pubsub; end
255
+ def stanza.pubsub
256
+ unless @mock_pubsub
257
+ @mock_pubsub = MiniTest::Mock.new
258
+ @mock_pubsub.expect(:node?, true, ['game_13'])
259
+ def @mock_pubsub.published; @published; end
260
+ def @mock_pubsub.publish(node, message)
261
+ @published ||= []
262
+ @published << [node, message]
263
+ end
264
+ end
265
+ @mock_pubsub
266
+ end
267
+ stanza.process
268
+
269
+ assert @stream.verify
270
+ assert stanza.mock_pubsub.verify
271
+ assert_equal 1, @stream.nodes.size
272
+
273
+ # test result stanza contains generated item id
274
+ expected = node(%q{
275
+ <iq from="games.wonderland.lit" id="42" to="alice@wonderland.lit/tea" type="result">
276
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
277
+ <publish node="game_13">
278
+ <item/>
279
+ </publish>
280
+ </pubsub>
281
+ </iq>}.strip.gsub(/\n|\s{2,}/, ''))
282
+ # id is random
283
+ item = @stream.nodes[0].xpath('ns:pubsub/ns:publish/ns:item', 'ns' => 'http://jabber.org/protocol/pubsub').first
284
+ refute_nil item['id']
285
+ item.remove_attribute('id')
286
+ assert_equal expected, @stream.nodes[0]
287
+
288
+ # test published message has a generated item id
289
+ expected = node(%q{
290
+ <message>
291
+ <event xmlns="http://jabber.org/protocol/pubsub#event">
292
+ <items node="game_13">
293
+ <item publisher="alice@wonderland.lit/tea">
294
+ <entry xmlns="http://www.w3.org/2005/Atom">
295
+ <title>Test</title>
296
+ <summary>This is a summary.</summary>
297
+ </entry>
298
+ </item>
299
+ </items>
300
+ </event>
301
+ </message>
302
+ }.strip.gsub(/\n|\s{2,}/, ''))
303
+ published_node, published_message = *stanza.mock_pubsub.published[0]
304
+ assert_equal 'game_13', published_node
305
+ # id is random
306
+ item = published_message.xpath('ns:event/ns:items/ns:item', 'ns' => 'http://jabber.org/protocol/pubsub#event').first
307
+ refute_nil item['id']
308
+ item.remove_attribute('id')
309
+ assert_equal expected, published_message
310
+ end
311
+
312
+ def test_good_stanza_processes
313
+ node = node(%q{
314
+ <iq type='set' to='games.wonderland.lit' id='42'>
315
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
316
+ <publish node='game_13'>
317
+ <item id='item_42'>
318
+ <entry xmlns='http://www.w3.org/2005/Atom'>
319
+ <title>Test</title>
320
+ <summary>This is a summary.</summary>
321
+ </entry>
322
+ </item>
323
+ </publish>
324
+ </pubsub>
325
+ </iq>
326
+ }.strip.gsub(/\n|\s{2,}/, ''))
327
+
328
+ def @stream.nodes; @nodes; end
329
+ def @stream.write(node)
330
+ @nodes ||= []
331
+ @nodes << node
332
+ end
333
+
334
+ stanza = Vines::Stanza::PubSub::Publish.new(node, @stream)
335
+ def stanza.mock_pubsub; @mock_pubsub; end
336
+ def stanza.pubsub
337
+ unless @mock_pubsub
338
+ xml = %q{
339
+ <message>
340
+ <event xmlns="http://jabber.org/protocol/pubsub#event">
341
+ <items node="game_13">
342
+ <item id="item_42" publisher="alice@wonderland.lit/tea">
343
+ <entry xmlns="http://www.w3.org/2005/Atom">
344
+ <title>Test</title>
345
+ <summary>This is a summary.</summary>
346
+ </entry>
347
+ </item>
348
+ </items>
349
+ </event>
350
+ </message>
351
+ }.strip.gsub(/\n|\s{2,}/, '')
352
+ @mock_pubsub = MiniTest::Mock.new
353
+ @mock_pubsub.expect(:node?, true, ['game_13'])
354
+ @mock_pubsub.expect(:publish, nil, ['game_13', Nokogiri::XML(xml).root])
355
+ end
356
+ @mock_pubsub
357
+ end
358
+ stanza.process
359
+
360
+ assert @stream.verify
361
+ assert stanza.mock_pubsub.verify
362
+ assert_equal 1, @stream.nodes.size
363
+
364
+ expected = node(%q{<iq from="games.wonderland.lit" id="42" to="alice@wonderland.lit/tea" type="result"/>})
365
+ assert_equal expected, @stream.nodes[0]
366
+ end
367
+
368
+ private
369
+
370
+ def node(xml)
371
+ Nokogiri::XML(xml).root
372
+ end
373
+ end