vines 0.4.5 → 0.4.6

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 (89) hide show
  1. data/Gemfile +3 -0
  2. data/README.md +48 -0
  3. data/Rakefile +6 -58
  4. data/bin/vines +12 -2
  5. data/conf/certs/ca-bundle.crt +568 -39
  6. data/conf/config.rb +9 -42
  7. data/lib/vines.rb +1 -8
  8. data/lib/vines/command/cert.rb +4 -4
  9. data/lib/vines/command/init.rb +3 -3
  10. data/lib/vines/config.rb +9 -0
  11. data/lib/vines/kit.rb +2 -7
  12. data/lib/vines/storage/local.rb +50 -17
  13. data/lib/vines/store.rb +6 -4
  14. data/lib/vines/stream.rb +1 -1
  15. data/lib/vines/stream/http/session.rb +0 -0
  16. data/lib/vines/stream/http/sessions.rb +0 -0
  17. data/lib/vines/stream/parser.rb +3 -2
  18. data/lib/vines/token_bucket.rb +19 -10
  19. data/lib/vines/version.rb +1 -1
  20. data/test/cluster/publisher_test.rb +45 -33
  21. data/test/cluster/sessions_test.rb +32 -39
  22. data/test/cluster/subscriber_test.rb +93 -78
  23. data/test/config/host_test.rb +2 -4
  24. data/test/config/pubsub_test.rb +132 -126
  25. data/test/config_test.rb +2 -4
  26. data/test/contact_test.rb +80 -66
  27. data/test/error_test.rb +54 -55
  28. data/test/jid_test.rb +1 -2
  29. data/test/kit_test.rb +22 -17
  30. data/test/router_test.rb +187 -146
  31. data/test/stanza/iq/disco_info_test.rb +59 -59
  32. data/test/stanza/iq/disco_items_test.rb +36 -34
  33. data/test/stanza/iq/private_storage_test.rb +138 -143
  34. data/test/stanza/iq/roster_test.rb +198 -175
  35. data/test/stanza/iq/session_test.rb +17 -18
  36. data/test/stanza/iq/vcard_test.rb +117 -116
  37. data/test/stanza/iq/version_test.rb +47 -46
  38. data/test/stanza/iq_test.rb +53 -49
  39. data/test/stanza/message_test.rb +92 -89
  40. data/test/stanza/presence/probe_test.rb +2 -5
  41. data/test/stanza/presence/subscribe_test.rb +67 -54
  42. data/test/stanza/pubsub/create_test.rb +86 -108
  43. data/test/stanza/pubsub/delete_test.rb +141 -114
  44. data/test/stanza/pubsub/publish_test.rb +256 -320
  45. data/test/stanza/pubsub/subscribe_test.rb +169 -150
  46. data/test/stanza/pubsub/unsubscribe_test.rb +111 -142
  47. data/test/stanza_test.rb +61 -54
  48. data/test/storage/ldap_test.rb +1 -2
  49. data/test/storage/local_test.rb +3 -5
  50. data/test/storage/null_test.rb +3 -4
  51. data/test/storage/storage_tests.rb +1 -3
  52. data/test/storage_test.rb +1 -2
  53. data/test/store_test.rb +1 -2
  54. data/test/stream/client/auth_test.rb +61 -63
  55. data/test/stream/client/ready_test.rb +7 -8
  56. data/test/stream/client/session_test.rb +19 -18
  57. data/test/stream/component/handshake_test.rb +40 -37
  58. data/test/stream/component/ready_test.rb +76 -61
  59. data/test/stream/component/start_test.rb +7 -8
  60. data/test/stream/http/auth_test.rb +3 -4
  61. data/test/stream/http/ready_test.rb +52 -60
  62. data/test/stream/http/request_test.rb +1 -3
  63. data/test/stream/http/sessions_test.rb +2 -3
  64. data/test/stream/http/start_test.rb +3 -4
  65. data/test/stream/parser_test.rb +3 -4
  66. data/test/stream/sasl_test.rb +105 -86
  67. data/test/stream/server/auth_test.rb +40 -36
  68. data/test/stream/server/outbound/auth_test.rb +3 -4
  69. data/test/stream/server/ready_test.rb +51 -51
  70. data/test/test_helper.rb +42 -0
  71. data/test/token_bucket_test.rb +38 -18
  72. data/test/user_test.rb +79 -49
  73. data/vines.gemspec +33 -0
  74. data/web/chat/javascripts/app.js +1 -1
  75. data/web/lib/coffeescripts/layout.coffee +1 -1
  76. data/web/lib/javascripts/base.js +10 -10
  77. data/web/lib/javascripts/jquery.js +4 -4
  78. metadata +31 -128
  79. data/README +0 -35
  80. data/lib/vines/storage/couchdb.rb +0 -129
  81. data/lib/vines/storage/mongodb.rb +0 -132
  82. data/lib/vines/storage/redis.rb +0 -127
  83. data/lib/vines/storage/sql.rb +0 -220
  84. data/test/rake_test_loader.rb +0 -17
  85. data/test/storage/couchdb_test.rb +0 -107
  86. data/test/storage/mock_mongo.rb +0 -40
  87. data/test/storage/mongodb_test.rb +0 -81
  88. data/test/storage/redis_test.rb +0 -51
  89. data/test/storage/sql_test.rb +0 -62
@@ -1,53 +1,46 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require 'vines'
4
- require 'ext/nokogiri'
3
+ require 'test_helper'
5
4
  require 'storage/storage_tests'
6
5
  require 'storage/mock_redis'
7
- require 'minitest/autorun'
8
6
 
9
- class ClusterSessionsTest < MiniTest::Unit::TestCase
10
- def setup
11
- @connection = MockRedis.new
12
- @cluster = MiniTest::Mock.new
13
- @cluster.expect(:connection, @connection)
14
- end
7
+ describe Vines::Cluster::Sessions do
8
+ subject { Vines::Cluster::Sessions.new(cluster) }
9
+ let(:connection) { MockRedis.new }
10
+ let(:cluster) { OpenStruct.new(id: 'abc', connection: connection) }
11
+ let(:jid1) { 'alice@wonderland.lit/tea' }
12
+ let(:jid2) { 'alice@wonderland.lit/cake' }
15
13
 
16
- def test_save
17
- StorageTests::EMLoop.new do
18
- @cluster.expect(:id, 'abc')
19
- jid1 = 'alice@wonderland.lit/tea'
20
- jid2 = 'alice@wonderland.lit/cake'
21
- sessions = Vines::Cluster::Sessions.new(@cluster)
22
- sessions.save(jid1, {available: true, interested: true})
23
- sessions.save(jid2, {available: false, interested: false})
24
- EM.next_tick do
25
- session1 = {node: 'abc', available: true, interested: true}
26
- session2 = {node: 'abc', available: false, interested: false}
27
- assert_equal 2, @connection.db["sessions:alice@wonderland.lit"].size
28
- assert_equal session1.to_json, @connection.db["sessions:alice@wonderland.lit"]['tea']
29
- assert_equal session2.to_json, @connection.db["sessions:alice@wonderland.lit"]['cake']
30
- assert_equal [jid1, jid2], @connection.db["cluster:nodes:abc"].to_a
31
- assert @cluster.verify
14
+ describe 'when saving to the cluster' do
15
+ it 'writes to a redis hash' do
16
+ StorageTests::EMLoop.new do
17
+ subject.save(jid1, {available: true, interested: true})
18
+ subject.save(jid2, {available: false, interested: false})
19
+ EM.next_tick do
20
+ session1 = {node: 'abc', available: true, interested: true}
21
+ session2 = {node: 'abc', available: false, interested: false}
22
+ connection.db["sessions:alice@wonderland.lit"].size.must_equal 2
23
+ connection.db["sessions:alice@wonderland.lit"]['tea'].must_equal session1.to_json
24
+ connection.db["sessions:alice@wonderland.lit"]['cake'].must_equal session2.to_json
25
+ connection.db["cluster:nodes:abc"].to_a.must_equal [jid1, jid2]
26
+ end
32
27
  end
33
28
  end
34
29
  end
35
30
 
36
- def test_delete
37
- StorageTests::EMLoop.new do
38
- jid1 = 'alice@wonderland.lit/tea'
39
- jid2 = 'alice@wonderland.lit/cake'
40
- @connection.db["sessions:alice@wonderland.lit"] = {}
41
- @connection.db["sessions:alice@wonderland.lit"]['tea'] = {node: 'abc', available: true}.to_json
42
- @connection.db["sessions:alice@wonderland.lit"]['cake'] = {node: 'abc', available: true}.to_json
43
- @connection.db["cluster:nodes:abc"] = Set.new([jid1, jid2])
31
+ describe 'when deleting from the cluster' do
32
+ it 'removes from a redis hash' do
33
+ StorageTests::EMLoop.new do
34
+ connection.db["sessions:alice@wonderland.lit"] = {}
35
+ connection.db["sessions:alice@wonderland.lit"]['tea'] = {node: 'abc', available: true}.to_json
36
+ connection.db["sessions:alice@wonderland.lit"]['cake'] = {node: 'abc', available: true}.to_json
37
+ connection.db["cluster:nodes:abc"] = Set.new([jid1, jid2])
44
38
 
45
- sessions = Vines::Cluster::Sessions.new(@cluster)
46
- sessions.delete(jid1)
47
- EM.next_tick do
48
- assert_equal 1, @connection.db["sessions:alice@wonderland.lit"].size
49
- assert_equal [jid2], @connection.db["cluster:nodes:abc"].to_a
50
- assert @cluster.verify
39
+ subject.delete(jid1)
40
+ EM.next_tick do
41
+ connection.db["sessions:alice@wonderland.lit"].size.must_equal 1
42
+ connection.db["cluster:nodes:abc"].to_a.must_equal [jid2]
43
+ end
51
44
  end
52
45
  end
53
46
  end
@@ -1,94 +1,109 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require 'vines'
4
- require 'ext/nokogiri'
5
- require 'minitest/autorun'
6
-
7
- class ClusterSubscriberTest < MiniTest::Unit::TestCase
8
- def setup
9
- @connection = MiniTest::Mock.new
10
- @cluster = MiniTest::Mock.new
11
- @cluster.expect(:id, 'abc')
12
- end
13
-
14
- def test_subscribe
15
- @cluster.expect(:connect, @connection)
16
- @connection.expect(:subscribe, nil, ['cluster:nodes:all'])
17
- @connection.expect(:subscribe, nil, ['cluster:nodes:abc'])
18
- @connection.expect(:on, nil, [:message])
19
- subscriber = Vines::Cluster::Subscriber.new(@cluster)
20
- subscriber.subscribe
21
- assert @connection.verify
22
- assert @cluster.verify
23
- end
3
+ require 'test_helper'
24
4
 
25
- def test_heartbeat
26
- now = Time.now.to_i
27
- msg = {from: 'node-42', type: 'heartbeat', time: now}.to_json
28
- @cluster.expect(:poke, nil, ['node-42', now])
5
+ describe Vines::Cluster::Subscriber do
6
+ subject { Vines::Cluster::Subscriber.new(cluster) }
7
+ let(:connection) { MiniTest::Mock.new }
8
+ let(:cluster) { MiniTest::Mock.new }
9
+ let(:now) { Time.now.to_i }
29
10
 
30
- subscriber = Vines::Cluster::Subscriber.new(@cluster)
31
- subscriber.send(:on_message, 'cluster:nodes:all', msg)
32
- assert @connection.verify
33
- assert @cluster.verify
11
+ before do
12
+ cluster.expect :id, 'abc'
34
13
  end
35
14
 
36
- def test_online
37
- now = Time.now.to_i
38
- msg = {from: 'node-42', type: 'online', time: now}.to_json
39
- @cluster.expect(:poke, nil, ['node-42', now])
40
-
41
- subscriber = Vines::Cluster::Subscriber.new(@cluster)
42
- subscriber.send(:on_message, 'cluster:nodes:all', msg)
43
- assert @connection.verify
44
- assert @cluster.verify
15
+ describe '#subscribe' do
16
+ before do
17
+ cluster.expect :connect, connection
18
+ connection.expect :subscribe, nil, ['cluster:nodes:all']
19
+ connection.expect :subscribe, nil, ['cluster:nodes:abc']
20
+ connection.expect :on, nil, [:message]
21
+ end
22
+
23
+ it 'subscribes to its own channel and the broadcast channel' do
24
+ subject.subscribe
25
+ connection.verify
26
+ cluster.verify
27
+ end
45
28
  end
46
29
 
47
- def test_offline
48
- now = Time.now.to_i
49
- msg = {from: 'node-42', type: 'offline', time: now}.to_json
50
- @cluster.expect(:delete_sessions, nil, ['node-42'])
51
-
52
- subscriber = Vines::Cluster::Subscriber.new(@cluster)
53
- subscriber.send(:on_message, 'cluster:nodes:all', msg)
54
- assert @connection.verify
55
- assert @cluster.verify
30
+ describe 'when receiving a heartbeat broadcast message' do
31
+ before do
32
+ cluster.expect :poke, nil, ['node-42', now]
33
+ end
34
+
35
+ it 'pokes the session manager for the broadcasting node' do
36
+ msg = {from: 'node-42', type: 'heartbeat', time: now}.to_json
37
+ subject.send(:on_message, 'cluster:nodes:all', msg)
38
+ connection.verify
39
+ cluster.verify
40
+ end
56
41
  end
57
42
 
58
- def test_route_stanza
59
- stanza = "<message to='alice@wonderland.lit/tea'>hello</message>"
60
- node = Nokogiri::XML(stanza).root rescue nil
61
- msg = {from: 'node-42', type: 'stanza', stanza: stanza}.to_json
62
-
63
- stream = MiniTest::Mock.new
64
- stream.expect(:write, nil, [node])
65
- @cluster.expect(:connected_resources, [stream], ['alice@wonderland.lit/tea'])
66
-
67
- subscriber = Vines::Cluster::Subscriber.new(@cluster)
68
- subscriber.send(:on_message, 'cluster:nodes:abc', msg)
69
- assert stream.verify
70
- assert @connection.verify
71
- assert @cluster.verify
43
+ describe 'when receiving an initial online broadcast message' do
44
+ before do
45
+ cluster.expect :poke, nil, ['node-42', now]
46
+ end
47
+
48
+ it 'pokes the session manager for the broadcasting node' do
49
+ msg = {from: 'node-42', type: 'online', time: now}.to_json
50
+ subject.send(:on_message, 'cluster:nodes:all', msg)
51
+ connection.verify
52
+ cluster.verify
53
+ end
72
54
  end
73
55
 
74
- def test_update_user
75
- alice = Vines::User.new(jid: 'alice@wonderland.lit/tea')
76
- msg = {from: 'node-42', type: 'user', jid: alice.jid.to_s}.to_json
77
-
78
- storage = MiniTest::Mock.new
79
- storage.expect(:find_user, alice, [alice.jid.bare])
80
-
81
- stream = MiniTest::Mock.new
82
- stream.expect(:user, alice)
56
+ describe 'when receiving an offline broadcast message' do
57
+ before do
58
+ cluster.expect :delete_sessions, nil, ['node-42']
59
+ end
60
+
61
+ it 'deletes the sessions for the broadcasting node' do
62
+ msg = {from: 'node-42', type: 'offline', time: now}.to_json
63
+ subject.send(:on_message, 'cluster:nodes:all', msg)
64
+ connection.verify
65
+ cluster.verify
66
+ end
67
+ end
83
68
 
84
- @cluster.expect(:storage, storage, ['wonderland.lit'])
85
- @cluster.expect(:connected_resources, [stream], [alice.jid.bare])
69
+ describe 'when receiving a stanza routed to my node' do
70
+ let(:stream) { MiniTest::Mock.new }
71
+ let(:stanza) { "<message to='alice@wonderland.lit/tea'>hello</message>" }
72
+ let(:xml) { Nokogiri::XML(stanza).root }
73
+
74
+ before do
75
+ stream.expect :write, nil, [xml]
76
+ cluster.expect :connected_resources, [stream], ['alice@wonderland.lit/tea']
77
+ end
78
+
79
+ it 'writes the stanza to the connected user streams' do
80
+ msg = {from: 'node-42', type: 'stanza', stanza: stanza}.to_json
81
+ subject.send(:on_message, 'cluster:nodes:abc', msg)
82
+ stream.verify
83
+ connection.verify
84
+ cluster.verify
85
+ end
86
+ end
86
87
 
87
- subscriber = Vines::Cluster::Subscriber.new(@cluster)
88
- subscriber.send(:on_message, 'cluster:nodes:abc', msg)
89
- assert storage.verify
90
- assert stream.verify
91
- assert @connection.verify
92
- assert @cluster.verify
88
+ describe 'when receiving a user update message to my node' do
89
+ let(:alice) { Vines::User.new(jid: 'alice@wonderland.lit/tea') }
90
+ let(:storage) { MiniTest::Mock.new }
91
+ let(:stream) { MiniTest::Mock.new }
92
+
93
+ before do
94
+ storage.expect :find_user, alice, [alice.jid.bare]
95
+ stream.expect :user, alice
96
+ cluster.expect :storage, storage, ['wonderland.lit']
97
+ cluster.expect :connected_resources, [stream], [alice.jid.bare]
98
+ end
99
+
100
+ it 'reloads the user from storage and updates their connected streams' do
101
+ msg = {from: 'node-42', type: 'user', jid: alice.jid.to_s}.to_json
102
+ subject.send(:on_message, 'cluster:nodes:abc', msg)
103
+ storage.verify
104
+ stream.verify
105
+ connection.verify
106
+ cluster.verify
107
+ end
93
108
  end
94
109
  end
@@ -1,10 +1,8 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require 'tmpdir'
4
- require 'vines'
5
- require 'minitest/autorun'
3
+ require 'test_helper'
6
4
 
7
- class HostTest < MiniTest::Unit::TestCase
5
+ describe Vines::Config::Host do
8
6
  def test_missing_storage
9
7
  assert_raises(RuntimeError) do
10
8
  Vines::Config.new do
@@ -1,181 +1,187 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require 'tmpdir'
4
- require 'vines'
5
- require 'ext/nokogiri'
6
- require 'minitest/autorun'
3
+ require 'test_helper'
7
4
 
8
- class ConfigPubSubTest < MiniTest::Unit::TestCase
9
- def setup
5
+ describe Vines::Config::PubSub do
6
+ subject { config.pubsub('topics.wonderland.lit') }
7
+ let(:alice) { Vines::JID.new('alice@wonderland.lit') }
8
+ let(:romeo) { Vines::JID.new('romeo@verona.lit') }
9
+ let(:config) do
10
10
  @config = Vines::Config.new do
11
11
  host 'wonderland.lit' do
12
12
  storage(:fs) { dir Dir.tmpdir }
13
13
  pubsub 'topics'
14
14
  end
15
15
  end
16
- @pubsub = @config.pubsub('topics.wonderland.lit')
17
16
  end
18
17
 
19
- def test_add_node
18
+ it 'adds and deletes a topic node' do
20
19
  topic = 'rhode_island_is_neither_a_road_nor_an_island'
21
- refute @pubsub.node?(topic)
22
- @pubsub.add_node(topic)
23
- assert @pubsub.node?(topic)
24
- @pubsub.delete_node(topic)
25
- refute @pubsub.node?(topic)
20
+ refute subject.node?(topic)
21
+ subject.add_node(topic)
22
+ assert subject.node?(topic)
23
+ subject.delete_node(topic)
24
+ refute subject.node?(topic)
26
25
  end
27
26
 
28
- def test_delete_missing_node
27
+ it 'ignores deleting a missing topic node' do
29
28
  topic = 'kittens_vs_puppies'
30
- refute @pubsub.node?(topic)
31
- @pubsub.delete_node(topic)
32
- refute @pubsub.node?(topic)
29
+ refute subject.node?(topic)
30
+ subject.delete_node(topic)
31
+ refute subject.node?(topic)
33
32
  end
34
33
 
35
- def test_subscribe_with_jid
34
+ it 'subscribes a jid to a node' do
36
35
  topic = 'with_jid'
37
36
  jid = Vines::JID.new('alice@wonderland.lit')
38
- @pubsub.add_node(topic)
39
- @pubsub.subscribe(topic, jid)
40
- assert @pubsub.subscribed?(topic, jid.to_s)
41
- assert @pubsub.subscribed?(topic, jid)
37
+ subject.add_node(topic)
38
+ subject.subscribe(topic, jid)
39
+ assert subject.subscribed?(topic, jid.to_s)
40
+ assert subject.subscribed?(topic, jid)
42
41
  end
43
42
 
44
- def test_subscribe_remote_jid_not_allowed
43
+ it 'does not allow remote jids to subscribe to a node by default' do
45
44
  topic = 'remote_jids_failed'
46
45
  jid = 'romeo@verona.lit'
47
- @pubsub.add_node(topic)
48
- @pubsub.subscribe(topic, jid)
49
- refute @pubsub.subscribed?(topic, jid)
46
+ subject.add_node(topic)
47
+ subject.subscribe(topic, jid)
48
+ refute subject.subscribed?(topic, jid)
50
49
  end
51
50
 
52
- def test_subscribe_remote_jid_is_allowed
51
+ it 'allows remote jid subscriptions when cross domain messages are enabled' do
53
52
  topic = 'remote_jids_allowed'
54
53
  jid = 'romeo@verona.lit'
55
- @config.vhost('wonderland.lit').cross_domain_messages true
56
- @pubsub.add_node(topic)
57
- @pubsub.subscribe(topic, jid)
58
- assert @pubsub.subscribed?(topic, jid)
54
+ config.vhost('wonderland.lit').cross_domain_messages true
55
+ subject.add_node(topic)
56
+ subject.subscribe(topic, jid)
57
+ assert subject.subscribed?(topic, jid)
59
58
  end
60
59
 
61
- def test_subscribe_missing_node
60
+ it 'ignores subscribing to a missing node' do
62
61
  topic = 'bogus'
63
62
  jid = 'alice@wonderland.lit'
64
- refute @pubsub.node?(topic)
65
- @pubsub.subscribe(topic, jid)
66
- refute @pubsub.node?(topic)
67
- refute @pubsub.subscribed?(topic, jid)
63
+ refute subject.node?(topic)
64
+ subject.subscribe(topic, jid)
65
+ refute subject.node?(topic)
66
+ refute subject.subscribed?(topic, jid)
68
67
  end
69
68
 
70
- def test_unsubscribe_deletes_node
69
+ it 'deletes the node after unsubscribing' do
71
70
  topic = 'delete_me'
72
71
  jid = 'alice@wonderland.lit/tea'
73
- @pubsub.add_node(topic)
74
- @pubsub.subscribe(topic, jid)
75
- assert @pubsub.subscribed?(topic, jid)
76
- @pubsub.unsubscribe(topic, jid)
77
- refute @pubsub.subscribed?(topic, jid)
78
- refute @pubsub.node?(topic)
72
+ subject.add_node(topic)
73
+ subject.subscribe(topic, jid)
74
+ assert subject.subscribed?(topic, jid)
75
+ subject.unsubscribe(topic, jid)
76
+ refute subject.subscribed?(topic, jid)
77
+ refute subject.node?(topic)
79
78
  end
80
79
 
81
- def test_unsubscribe_all
80
+ it 'unsubscribes a jid from all topics' do
82
81
  topic = 'pirates_vs_ninjas'
83
82
  topic2 = 'pirates_vs_ninjas_2'
84
83
  jid = 'alice@wonderland.lit'
85
84
  jid2 = 'hatter@wonderland.lit'
86
- @pubsub.add_node(topic)
87
- @pubsub.add_node(topic2)
88
-
89
- @pubsub.subscribe(topic, jid)
90
- @pubsub.subscribe(topic, jid2)
91
- @pubsub.subscribe(topic2, jid)
92
- assert @pubsub.subscribed?(topic, jid)
93
- assert @pubsub.subscribed?(topic, jid2)
94
- assert @pubsub.subscribed?(topic2, jid)
95
-
96
- @pubsub.unsubscribe_all(jid)
97
- refute @pubsub.node?(topic2)
98
- refute @pubsub.subscribed?(topic, jid)
99
- refute @pubsub.subscribed?(topic2, jid)
100
- assert @pubsub.subscribed?(topic, jid2)
85
+ subject.add_node(topic)
86
+ subject.add_node(topic2)
87
+
88
+ subject.subscribe(topic, jid)
89
+ subject.subscribe(topic, jid2)
90
+ subject.subscribe(topic2, jid)
91
+ assert subject.subscribed?(topic, jid)
92
+ assert subject.subscribed?(topic, jid2)
93
+ assert subject.subscribed?(topic2, jid)
94
+
95
+ subject.unsubscribe_all(jid)
96
+ refute subject.node?(topic2)
97
+ refute subject.subscribed?(topic, jid)
98
+ refute subject.subscribed?(topic2, jid)
99
+ assert subject.subscribed?(topic, jid2)
101
100
  end
102
101
 
103
- def test_publish
104
- topic = 'pirates_vs_ninjas'
105
- alice = Vines::JID.new('alice@wonderland.lit')
106
- romeo = Vines::JID.new('romeo@verona.lit')
107
-
108
- @config.vhost('wonderland.lit').cross_domain_messages true
109
- def @config.router
110
- unless @mock_router
111
- @mock_router = MiniTest::Mock.new
112
- def @mock_router.nodes; @nodes; end
113
- def @mock_router.route(node)
102
+ describe 'when publishing a message to a topic node' do
103
+ let(:xml) do
104
+ node(%q{
105
+ <iq type='set' to='topics.wonderland.lit'>
106
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
107
+ <publish node='pirates_vs_ninjas'>
108
+ <item id='item_42'>
109
+ <entry xmlns='http://www.w3.org/2005/Atom'>
110
+ <title>Test</title>
111
+ <summary>This is a summary.</summary>
112
+ </entry>
113
+ </item>
114
+ </publish>
115
+ </pubsub>
116
+ </iq>})
117
+ end
118
+
119
+ let(:recipient) do
120
+ recipient = MiniTest::Mock.new
121
+ recipient.expect :user, Vines::User.new(jid: alice)
122
+ class << recipient
123
+ attr_accessor :nodes
124
+ def write(node)
114
125
  @nodes ||= []
115
126
  @nodes << node
116
127
  end
117
128
  end
118
- @mock_router
129
+ recipient
119
130
  end
120
131
 
121
- recipient = recipient(alice)
122
- @config.router.expect(:connected_resources, [recipient], [alice, 'topics.wonderland.lit'])
123
-
124
- @pubsub.add_node(topic)
125
- @pubsub.subscribe(topic, alice)
126
- @pubsub.subscribe(topic, romeo)
127
- assert @pubsub.subscribed?(topic, alice)
128
- assert @pubsub.subscribed?(topic, romeo)
129
-
130
- node = node(%q{
131
- <iq type='set' to='topics.wonderland.lit'>
132
- <pubsub xmlns='http://jabber.org/protocol/pubsub'>
133
- <publish node='pirates_vs_ninjas'>
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
- </item>
140
- </publish>
141
- </pubsub>
142
- </iq>
143
- }.strip.gsub(/\n|\s{2,}/, ''))
144
- expected = node.clone
145
-
146
- @pubsub.publish(topic, node)
147
-
148
- assert @config.router.verify
149
- assert recipient.verify
150
-
151
- # id is random
152
- refute_nil recipient.nodes[0]['id']
153
- refute_nil @config.router.nodes[0]['id']
154
- recipient.nodes[0].remove_attribute('id')
155
- @config.router.nodes[0].remove_attribute('id')
156
-
157
- expected['to'] = 'alice@wonderland.lit'
158
- expected['from'] = 'topics.wonderland.lit'
159
- assert_equal expected, recipient.nodes[0]
160
-
161
- expected['to'] = 'romeo@verona.lit'
162
- assert_equal expected, @config.router.nodes[0]
163
- end
132
+ before do
133
+ router = MiniTest::Mock.new
134
+ router.expect :connected_resources, [recipient], [alice, 'topics.wonderland.lit']
135
+ class << router
136
+ attr_accessor :nodes
137
+ def route(node)
138
+ @nodes ||= []
139
+ @nodes << node
140
+ end
141
+ end
142
+
143
+ class << config
144
+ attr_accessor :router
145
+ end
146
+ config.router = router
147
+ config.vhost('wonderland.lit').cross_domain_messages true
148
+
149
+ subject.add_node(topic)
150
+ subject.subscribe(topic, alice)
151
+ subject.subscribe(topic, romeo)
152
+ end
153
+
154
+ let(:topic) { 'pirates_vs_ninjas' }
155
+
156
+ it 'writes the message to local connected resource streams' do
157
+ expected = xml.clone
158
+ expected['to'] = 'alice@wonderland.lit'
159
+ expected['from'] = 'topics.wonderland.lit'
164
160
 
165
- private
161
+ subject.publish(topic, xml)
162
+ config.router.verify
163
+ recipient.verify
166
164
 
167
- def recipient(jid)
168
- recipient = MiniTest::Mock.new
169
- recipient.expect(:user, Vines::User.new(jid: jid))
170
- def recipient.nodes; @nodes; end
171
- def recipient.write(node)
172
- @nodes ||= []
173
- @nodes << node
165
+ # id is random
166
+ received = recipient.nodes.first
167
+ received['id'].wont_be_nil
168
+ received.remove_attribute('id')
169
+ received.must_equal expected
174
170
  end
175
- recipient
176
- end
177
171
 
178
- def node(xml)
179
- Nokogiri::XML(xml).root
172
+ it 'routes the message to remote jids' do
173
+ expected = xml.clone
174
+ expected['to'] = 'romeo@verona.lit'
175
+ expected['from'] = 'topics.wonderland.lit'
176
+
177
+ subject.publish(topic, xml)
178
+ config.router.verify
179
+
180
+ # id is random
181
+ routed = config.router.nodes.first
182
+ routed['id'].wont_be_nil
183
+ routed.remove_attribute('id')
184
+ routed.must_equal expected
185
+ end
180
186
  end
181
187
  end