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.
- data/Gemfile +3 -0
- data/README.md +48 -0
- data/Rakefile +6 -58
- data/bin/vines +12 -2
- data/conf/certs/ca-bundle.crt +568 -39
- data/conf/config.rb +9 -42
- data/lib/vines.rb +1 -8
- data/lib/vines/command/cert.rb +4 -4
- data/lib/vines/command/init.rb +3 -3
- data/lib/vines/config.rb +9 -0
- data/lib/vines/kit.rb +2 -7
- data/lib/vines/storage/local.rb +50 -17
- data/lib/vines/store.rb +6 -4
- data/lib/vines/stream.rb +1 -1
- data/lib/vines/stream/http/session.rb +0 -0
- data/lib/vines/stream/http/sessions.rb +0 -0
- data/lib/vines/stream/parser.rb +3 -2
- data/lib/vines/token_bucket.rb +19 -10
- data/lib/vines/version.rb +1 -1
- data/test/cluster/publisher_test.rb +45 -33
- data/test/cluster/sessions_test.rb +32 -39
- data/test/cluster/subscriber_test.rb +93 -78
- data/test/config/host_test.rb +2 -4
- data/test/config/pubsub_test.rb +132 -126
- data/test/config_test.rb +2 -4
- data/test/contact_test.rb +80 -66
- data/test/error_test.rb +54 -55
- data/test/jid_test.rb +1 -2
- data/test/kit_test.rb +22 -17
- data/test/router_test.rb +187 -146
- data/test/stanza/iq/disco_info_test.rb +59 -59
- data/test/stanza/iq/disco_items_test.rb +36 -34
- data/test/stanza/iq/private_storage_test.rb +138 -143
- data/test/stanza/iq/roster_test.rb +198 -175
- data/test/stanza/iq/session_test.rb +17 -18
- data/test/stanza/iq/vcard_test.rb +117 -116
- data/test/stanza/iq/version_test.rb +47 -46
- data/test/stanza/iq_test.rb +53 -49
- data/test/stanza/message_test.rb +92 -89
- data/test/stanza/presence/probe_test.rb +2 -5
- data/test/stanza/presence/subscribe_test.rb +67 -54
- data/test/stanza/pubsub/create_test.rb +86 -108
- data/test/stanza/pubsub/delete_test.rb +141 -114
- data/test/stanza/pubsub/publish_test.rb +256 -320
- data/test/stanza/pubsub/subscribe_test.rb +169 -150
- data/test/stanza/pubsub/unsubscribe_test.rb +111 -142
- data/test/stanza_test.rb +61 -54
- data/test/storage/ldap_test.rb +1 -2
- data/test/storage/local_test.rb +3 -5
- data/test/storage/null_test.rb +3 -4
- data/test/storage/storage_tests.rb +1 -3
- data/test/storage_test.rb +1 -2
- data/test/store_test.rb +1 -2
- data/test/stream/client/auth_test.rb +61 -63
- data/test/stream/client/ready_test.rb +7 -8
- data/test/stream/client/session_test.rb +19 -18
- data/test/stream/component/handshake_test.rb +40 -37
- data/test/stream/component/ready_test.rb +76 -61
- data/test/stream/component/start_test.rb +7 -8
- data/test/stream/http/auth_test.rb +3 -4
- data/test/stream/http/ready_test.rb +52 -60
- data/test/stream/http/request_test.rb +1 -3
- data/test/stream/http/sessions_test.rb +2 -3
- data/test/stream/http/start_test.rb +3 -4
- data/test/stream/parser_test.rb +3 -4
- data/test/stream/sasl_test.rb +105 -86
- data/test/stream/server/auth_test.rb +40 -36
- data/test/stream/server/outbound/auth_test.rb +3 -4
- data/test/stream/server/ready_test.rb +51 -51
- data/test/test_helper.rb +42 -0
- data/test/token_bucket_test.rb +38 -18
- data/test/user_test.rb +79 -49
- data/vines.gemspec +33 -0
- data/web/chat/javascripts/app.js +1 -1
- data/web/lib/coffeescripts/layout.coffee +1 -1
- data/web/lib/javascripts/base.js +10 -10
- data/web/lib/javascripts/jquery.js +4 -4
- metadata +31 -128
- data/README +0 -35
- data/lib/vines/storage/couchdb.rb +0 -129
- data/lib/vines/storage/mongodb.rb +0 -132
- data/lib/vines/storage/redis.rb +0 -127
- data/lib/vines/storage/sql.rb +0 -220
- data/test/rake_test_loader.rb +0 -17
- data/test/storage/couchdb_test.rb +0 -107
- data/test/storage/mock_mongo.rb +0 -40
- data/test/storage/mongodb_test.rb +0 -81
- data/test/storage/redis_test.rb +0 -51
- data/test/storage/sql_test.rb +0 -62
data/conf/config.rb
CHANGED
@@ -8,6 +8,11 @@ Vines::Config.configure do
|
|
8
8
|
# level logs all XML sent and received by the server.
|
9
9
|
log :info
|
10
10
|
|
11
|
+
# Set the directory in which to look for virtual hosts' TLS certificates.
|
12
|
+
# This is optional and defaults to the conf/certs directory created during
|
13
|
+
# `vines init`.
|
14
|
+
certs 'conf/certs'
|
15
|
+
|
11
16
|
# Each host element below is a virtual host domain name that this server will
|
12
17
|
# service. Hosts can share storage configurations or use separate databases.
|
13
18
|
# TLS encryption is mandatory so each host must have a <domain>.crt and
|
@@ -16,6 +21,10 @@ Vines::Config.configure do
|
|
16
21
|
# command. Change the example, 'wonderland.lit', domain name to your actual
|
17
22
|
# domain.
|
18
23
|
#
|
24
|
+
# The vines gem is distributed with a single 'fs' filesystem storage backend.
|
25
|
+
# Additional database support is provided by the vines-sql, vines-redis,
|
26
|
+
# vines-couchdb, and vines-mongodb gems.
|
27
|
+
#
|
19
28
|
# The private_storage attribute allows clients to store XML fragments
|
20
29
|
# on the server, using the XEP-0049 Private XML Storage feature.
|
21
30
|
#
|
@@ -125,45 +134,3 @@ Vines::Config.configure do
|
|
125
134
|
# password ''
|
126
135
|
#end
|
127
136
|
end
|
128
|
-
|
129
|
-
# Available storage implementations:
|
130
|
-
|
131
|
-
#storage 'fs' do
|
132
|
-
# dir 'data'
|
133
|
-
#end
|
134
|
-
|
135
|
-
#storage 'couchdb' do
|
136
|
-
# host 'localhost'
|
137
|
-
# port 6984
|
138
|
-
# database 'xmpp'
|
139
|
-
# tls true
|
140
|
-
# username ''
|
141
|
-
# password ''
|
142
|
-
#end
|
143
|
-
|
144
|
-
#storage 'mongodb' do
|
145
|
-
# host 'localhost', 27017
|
146
|
-
# host 'localhost', 27018 # optional, connects to replica set
|
147
|
-
# database 'xmpp'
|
148
|
-
# tls true
|
149
|
-
# username ''
|
150
|
-
# password ''
|
151
|
-
# pool 5
|
152
|
-
#end
|
153
|
-
|
154
|
-
#storage 'redis' do
|
155
|
-
# host 'localhost'
|
156
|
-
# port 6379
|
157
|
-
# database 0
|
158
|
-
# password ''
|
159
|
-
#end
|
160
|
-
|
161
|
-
#storage 'sql' do
|
162
|
-
# adapter 'postgresql'
|
163
|
-
# host 'localhost'
|
164
|
-
# port 5432
|
165
|
-
# database 'xmpp'
|
166
|
-
# username ''
|
167
|
-
# password ''
|
168
|
-
# pool 5
|
169
|
-
#end
|
data/lib/vines.rb
CHANGED
@@ -55,25 +55,22 @@ module Vines
|
|
55
55
|
end
|
56
56
|
|
57
57
|
%w[
|
58
|
-
active_record
|
59
58
|
base64
|
60
59
|
bcrypt
|
61
60
|
digest/sha1
|
62
|
-
em-http
|
63
61
|
em-hiredis
|
64
62
|
eventmachine
|
65
63
|
fiber
|
66
64
|
fileutils
|
67
65
|
http/parser
|
66
|
+
json
|
68
67
|
logger
|
69
|
-
mongo
|
70
68
|
net/ldap
|
71
69
|
nokogiri
|
72
70
|
openssl
|
73
71
|
resolv
|
74
72
|
set
|
75
73
|
socket
|
76
|
-
uri
|
77
74
|
yaml
|
78
75
|
|
79
76
|
vines/log
|
@@ -110,13 +107,9 @@ end
|
|
110
107
|
vines/stanza/pubsub/unsubscribe
|
111
108
|
|
112
109
|
vines/storage
|
113
|
-
vines/storage/couchdb
|
114
110
|
vines/storage/ldap
|
115
111
|
vines/storage/local
|
116
|
-
vines/storage/mongodb
|
117
112
|
vines/storage/null
|
118
|
-
vines/storage/redis
|
119
|
-
vines/storage/sql
|
120
113
|
|
121
114
|
vines/config
|
122
115
|
vines/config/host
|
data/lib/vines/command/cert.rb
CHANGED
@@ -5,8 +5,8 @@ module Vines
|
|
5
5
|
class Cert
|
6
6
|
def run(opts)
|
7
7
|
raise 'vines cert <domain>' unless opts[:args].size == 1
|
8
|
-
|
9
|
-
create_cert(opts[:args].first,
|
8
|
+
require opts[:config]
|
9
|
+
create_cert(opts[:args].first, Config.instance.certs)
|
10
10
|
end
|
11
11
|
|
12
12
|
def create_cert(domain, dir)
|
@@ -33,9 +33,9 @@ module Vines
|
|
33
33
|
|
34
34
|
cert.sign(key, OpenSSL::Digest::SHA1.new)
|
35
35
|
|
36
|
-
{'key' => key, 'crt' => cert}.each_pair do |ext, o|
|
36
|
+
{'key' => key, 'crt' => cert}.each_pair do |ext, o|
|
37
37
|
name = File.join(dir, "#{domain}.#{ext}")
|
38
|
-
File.open(name,
|
38
|
+
File.open(name, 'w:utf-8') {|f| f.write(o.to_pem) }
|
39
39
|
File.chmod(0600, name) if ext == 'key'
|
40
40
|
end
|
41
41
|
end
|
data/lib/vines/command/init.rb
CHANGED
@@ -36,8 +36,8 @@ module Vines
|
|
36
36
|
|
37
37
|
def update_config(domain, dir)
|
38
38
|
config = File.expand_path('conf/config.rb', dir)
|
39
|
-
text = File.read(config)
|
40
|
-
File.open(config, 'w') do |f|
|
39
|
+
text = File.read(config, encoding: 'utf-8')
|
40
|
+
File.open(config, 'w:utf-8') do |f|
|
41
41
|
f.write(text.gsub('wonderland.lit', domain))
|
42
42
|
end
|
43
43
|
end
|
@@ -65,4 +65,4 @@ module Vines
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
68
|
-
end
|
68
|
+
end
|
data/lib/vines/config.rb
CHANGED
@@ -21,11 +21,20 @@ module Vines
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def initialize(&block)
|
24
|
+
@certs = File.expand_path('conf/certs')
|
24
25
|
@vhosts, @ports, @cluster = {}, {}, nil
|
25
26
|
@null = Storage::Null.new
|
26
27
|
@router = Router.new(self)
|
27
28
|
instance_eval(&block)
|
28
29
|
raise "must define at least one virtual host" if @vhosts.empty?
|
30
|
+
|
31
|
+
unless @certs && File.directory?(@certs) && File.readable?(@certs)
|
32
|
+
raise 'Must provide a readable certs directory'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def certs(dir=nil)
|
37
|
+
dir ? @certs = File.expand_path(dir) : @certs
|
29
38
|
end
|
30
39
|
|
31
40
|
def host(*names, &block)
|
data/lib/vines/kit.rb
CHANGED
@@ -12,17 +12,12 @@ module Vines
|
|
12
12
|
# Generates a random uuid per rfc 4122 that's useful for including in
|
13
13
|
# stream, iq, and other xmpp stanzas.
|
14
14
|
def self.uuid
|
15
|
-
|
16
|
-
hex[12] = '4'
|
17
|
-
hex[16] = %w[8 9 a b][rand(4)]
|
18
|
-
hex.scan(/(\w{8})(\w{4})(\w{4})(\w{4})(\w{12})/).first.join('-')
|
15
|
+
SecureRandom.uuid
|
19
16
|
end
|
20
17
|
|
21
18
|
# Generates a random 128 character authentication token.
|
22
19
|
def self.auth_token
|
23
|
-
|
24
|
-
1024.times { hash << rand.to_s }
|
25
|
-
hash.hexdigest
|
20
|
+
SecureRandom.hex(64)
|
26
21
|
end
|
27
22
|
end
|
28
23
|
end
|
data/lib/vines/storage/local.rb
CHANGED
@@ -27,8 +27,8 @@ module Vines
|
|
27
27
|
|
28
28
|
def find_user(jid)
|
29
29
|
jid = JID.new(jid).bare.to_s
|
30
|
-
file =
|
31
|
-
record = YAML.
|
30
|
+
file = "user/#{jid}" unless jid.empty?
|
31
|
+
record = YAML.load(read(file)) rescue nil
|
32
32
|
return User.new(jid: jid).tap do |user|
|
33
33
|
user.name, user.password = record.values_at('name', 'password')
|
34
34
|
(record['roster'] || {}).each_pair do |jid, props|
|
@@ -47,43 +47,48 @@ module Vines
|
|
47
47
|
user.roster.each do |contact|
|
48
48
|
record['roster'][contact.jid.bare.to_s] = contact.to_h
|
49
49
|
end
|
50
|
-
save("user/#{user.jid.bare}")
|
51
|
-
YAML.dump(record, f)
|
52
|
-
end
|
50
|
+
save("user/#{user.jid.bare}", YAML.dump(record))
|
53
51
|
end
|
54
52
|
|
55
53
|
def find_vcard(jid)
|
56
54
|
jid = JID.new(jid).bare.to_s
|
57
55
|
return if jid.empty?
|
58
|
-
file =
|
59
|
-
Nokogiri::XML(
|
56
|
+
file = "vcard/#{jid}"
|
57
|
+
Nokogiri::XML(read(file)).root rescue nil
|
60
58
|
end
|
61
59
|
|
62
60
|
def save_vcard(jid, card)
|
63
61
|
jid = JID.new(jid).bare.to_s
|
64
62
|
return if jid.empty?
|
65
|
-
save("vcard/#{jid}")
|
66
|
-
f.write(card.to_xml)
|
67
|
-
end
|
63
|
+
save("vcard/#{jid}", card.to_xml)
|
68
64
|
end
|
69
65
|
|
70
66
|
def find_fragment(jid, node)
|
71
67
|
jid = JID.new(jid).bare.to_s
|
72
68
|
return if jid.empty?
|
73
|
-
file =
|
74
|
-
Nokogiri::XML(
|
69
|
+
file = 'fragment/%s' % fragment_id(jid, node)
|
70
|
+
Nokogiri::XML(read(file)).root rescue nil
|
75
71
|
end
|
76
72
|
|
77
73
|
def save_fragment(jid, node)
|
78
74
|
jid = JID.new(jid).bare.to_s
|
79
75
|
return if jid.empty?
|
80
|
-
|
81
|
-
|
82
|
-
end
|
76
|
+
file = 'fragment/%s' % fragment_id(jid, node)
|
77
|
+
save(file, node.to_xml)
|
83
78
|
end
|
84
79
|
|
85
80
|
private
|
86
81
|
|
82
|
+
# Resolves a relative file name into an absolute path inside the
|
83
|
+
# storage directory.
|
84
|
+
#
|
85
|
+
# file - A fully-qualified or relative file name String.
|
86
|
+
#
|
87
|
+
# Returns the fully-qualified file path String.
|
88
|
+
#
|
89
|
+
# Raises RuntimeError if the resolved path is outside of the storage
|
90
|
+
# directory. This prevents directory path traversals with maliciously
|
91
|
+
# crafted JIDs.
|
87
92
|
def absolute_path(file)
|
88
93
|
File.expand_path(file, @dir).tap do |absolute|
|
89
94
|
parent = File.dirname(File.dirname(absolute))
|
@@ -91,12 +96,40 @@ module Vines
|
|
91
96
|
end
|
92
97
|
end
|
93
98
|
|
94
|
-
|
99
|
+
# Read the file from the filesystem and return its contents as a String.
|
100
|
+
# All files are assumed to be encoded as UTF-8.
|
101
|
+
#
|
102
|
+
# file - A fully-qualified or relative file name String.
|
103
|
+
#
|
104
|
+
# Returns the file content as a UTF-8 encoded String.
|
105
|
+
def read(file)
|
106
|
+
file = absolute_path(file)
|
107
|
+
File.read(file, encoding: 'utf-8')
|
108
|
+
end
|
109
|
+
|
110
|
+
# Write the content to the file. Make sure to consistently encode files
|
111
|
+
# we read and write as UTF-8.
|
112
|
+
#
|
113
|
+
# file - A fully-qualified or relative file name String.
|
114
|
+
# content - The String to write.
|
115
|
+
#
|
116
|
+
# Returns nothing.
|
117
|
+
def save(file, content)
|
95
118
|
file = absolute_path(file)
|
96
|
-
File.open(file, 'w') {|f|
|
119
|
+
File.open(file, 'w:utf-8') {|f| f.write(content) }
|
97
120
|
File.chmod(0600, file)
|
98
121
|
end
|
99
122
|
|
123
|
+
# Generates a unique file id for the user's private XML fragment.
|
124
|
+
#
|
125
|
+
# Private XML fragment storage needs to uniquely identify fragment files
|
126
|
+
# on disk. We combine the user's JID with a SHA-1 hash of the element's
|
127
|
+
# name and namespace to avoid special characters in the file name.
|
128
|
+
#
|
129
|
+
# jid - A bare JID identifying the user who owns this fragment.
|
130
|
+
# node - A Nokogiri::XML::Node for the XML to be stored.
|
131
|
+
#
|
132
|
+
# Returns an id String suitable for use in a file name.
|
100
133
|
def fragment_id(jid, node)
|
101
134
|
id = Digest::SHA1.hexdigest("#{node.name}:#{node.namespace.href}")
|
102
135
|
"#{jid}-#{id}"
|
data/lib/vines/store.rb
CHANGED
@@ -43,10 +43,12 @@ module Vines
|
|
43
43
|
unless @@sources
|
44
44
|
pattern = /-{5}BEGIN CERTIFICATE-{5}\n.*?-{5}END CERTIFICATE-{5}\n/m
|
45
45
|
pairs = Dir[File.join(@dir, '*.crt')].map do |name|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
File.open(name, "r:UTF-8") do |f|
|
47
|
+
pems = f.read.scan(pattern)
|
48
|
+
certs = pems.map {|pem| OpenSSL::X509::Certificate.new(pem) }
|
49
|
+
certs.reject! {|cert| cert.not_after < Time.now }
|
50
|
+
[name, certs]
|
51
|
+
end
|
50
52
|
end
|
51
53
|
@@sources = Hash[pairs]
|
52
54
|
end
|
data/lib/vines/stream.rb
CHANGED
@@ -21,7 +21,7 @@ module Vines
|
|
21
21
|
@remote_addr, @local_addr = addresses
|
22
22
|
@user, @closed, @stanza_size = nil, false, 0
|
23
23
|
@bucket = TokenBucket.new(100, 10)
|
24
|
-
@store = Store.new(
|
24
|
+
@store = Store.new(@config.certs)
|
25
25
|
@nodes = EM::Queue.new
|
26
26
|
process_node_queue
|
27
27
|
create_parser
|
File without changes
|
File without changes
|
data/lib/vines/stream/parser.rb
CHANGED
@@ -66,12 +66,13 @@ module Vines
|
|
66
66
|
def node(name, attrs=[], prefix=nil, uri=nil, ns=[])
|
67
67
|
ignore = stream?(name, uri) ? [] : IGNORE
|
68
68
|
doc = @node ? @node.document : Document.new
|
69
|
-
doc.create_element(name) do |node|
|
69
|
+
node = doc.create_element(name) do |node|
|
70
70
|
attrs.each {|attr| node[attr.localname] = attr.value }
|
71
71
|
ns.each {|prefix, uri| node.add_namespace(prefix, uri) unless ignore.include?(uri) }
|
72
|
-
node.namespace = node.add_namespace(prefix, uri) unless ignore.include?(uri)
|
73
72
|
doc << node unless @node
|
74
73
|
end
|
74
|
+
node.namespace = node.add_namespace(prefix, uri) unless ignore.include?(uri)
|
75
|
+
node
|
75
76
|
end
|
76
77
|
end
|
77
78
|
end
|
data/lib/vines/token_bucket.rb
CHANGED
@@ -9,8 +9,12 @@ module Vines
|
|
9
9
|
# of operations.
|
10
10
|
class TokenBucket
|
11
11
|
|
12
|
-
# Create a full bucket with capacity number of tokens to be filled
|
12
|
+
# Create a full bucket with `capacity` number of tokens to be filled
|
13
13
|
# at the given rate of tokens/second.
|
14
|
+
#
|
15
|
+
# capacity - The Fixnum maximum number of tokens the bucket can hold.
|
16
|
+
# rate - The Fixnum number of tokens per second at which the bucket is
|
17
|
+
# refilled.
|
14
18
|
def initialize(capacity, rate)
|
15
19
|
raise ArgumentError.new('capacity must be > 0') unless capacity > 0
|
16
20
|
raise ArgumentError.new('rate must be > 0') unless rate > 0
|
@@ -20,24 +24,29 @@ module Vines
|
|
20
24
|
@timestamp = Time.new
|
21
25
|
end
|
22
26
|
|
23
|
-
#
|
27
|
+
# Remove tokens from the bucket if it's full enough. There's no way, or
|
28
|
+
# need, to add tokens to the bucket. It refills over time.
|
29
|
+
#
|
30
|
+
# tokens - The Fixnum number of tokens to attempt to take from the bucket.
|
31
|
+
#
|
32
|
+
# Returns true if the bucket contains enough tokens to take, false if the
|
33
|
+
# bucket isn't full enough to satisy the request.
|
24
34
|
def take(tokens)
|
25
35
|
raise ArgumentError.new('tokens must be > 0') unless tokens > 0
|
26
|
-
|
27
|
-
@tokens -= tokens
|
28
|
-
true
|
29
|
-
else
|
30
|
-
false
|
31
|
-
end
|
36
|
+
tokens <= fill ? @tokens -= tokens : false
|
32
37
|
end
|
33
38
|
|
34
39
|
private
|
35
40
|
|
41
|
+
# Add tokens to the bucket at the `rate` provided in the constructor. This
|
42
|
+
# fills the bucket slowly over time.
|
43
|
+
#
|
44
|
+
# Returns the Fixnum number of tokens left in the bucket.
|
36
45
|
def fill
|
37
46
|
if @tokens < @capacity
|
38
47
|
now = Time.new
|
39
|
-
|
40
|
-
@tokens =
|
48
|
+
@tokens += (@rate * (now - @timestamp)).round
|
49
|
+
@tokens = @capacity if @tokens > @capacity
|
41
50
|
@timestamp = now
|
42
51
|
end
|
43
52
|
@tokens
|
data/lib/vines/version.rb
CHANGED
@@ -1,45 +1,57 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
require '
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@cluster = MiniTest::Mock.new
|
10
|
-
@cluster.expect(:id, 'abc')
|
11
|
-
@cluster.expect(:connection, @connection)
|
12
|
-
end
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Vines::Cluster::Publisher do
|
6
|
+
subject { Vines::Cluster::Publisher.new(cluster) }
|
7
|
+
let(:connection) { MiniTest::Mock.new }
|
8
|
+
let(:cluster) { MiniTest::Mock.new }
|
13
9
|
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
before do
|
11
|
+
cluster.expect :id, 'abc'
|
12
|
+
cluster.expect :connection, connection
|
13
|
+
end
|
17
14
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
15
|
+
describe '#broadcast' do
|
16
|
+
before do
|
17
|
+
msg = {from: 'abc', type: 'online', time: Time.now.to_i}.to_json
|
18
|
+
connection.expect :publish, nil, ["cluster:nodes:all", msg]
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'publishes the message to every cluster node' do
|
22
|
+
subject.broadcast(:online)
|
23
|
+
connection.verify
|
24
|
+
cluster.verify
|
25
|
+
end
|
22
26
|
end
|
23
27
|
|
24
|
-
|
25
|
-
stanza
|
26
|
-
msg = {from: 'abc', type: 'stanza', stanza: stanza}.to_json
|
27
|
-
@connection.expect(:publish, nil, ["cluster:nodes:node-42", msg])
|
28
|
+
describe '#route' do
|
29
|
+
let(:stanza) { "<message>hello</message>" }
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
before do
|
32
|
+
msg = {from: 'abc', type: 'stanza', stanza: stanza}.to_json
|
33
|
+
connection.expect :publish, nil, ["cluster:nodes:node-42", msg]
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'publishes the message to just one cluster node' do
|
37
|
+
subject.route(stanza, "node-42")
|
38
|
+
connection.verify
|
39
|
+
cluster.verify
|
40
|
+
end
|
33
41
|
end
|
34
42
|
|
35
|
-
|
36
|
-
jid
|
37
|
-
|
38
|
-
|
43
|
+
describe '#update_user' do
|
44
|
+
let(:jid) { Vines::JID.new('alice@wonderland.lit') }
|
45
|
+
|
46
|
+
before do
|
47
|
+
msg = {from: 'abc', type: 'user', jid: jid.to_s}.to_json
|
48
|
+
connection.expect :publish, nil, ["cluster:nodes:node-42", msg]
|
49
|
+
end
|
39
50
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
51
|
+
it 'publishes the new user to just one cluster node' do
|
52
|
+
subject.update_user(jid, "node-42")
|
53
|
+
connection.verify
|
54
|
+
cluster.verify
|
55
|
+
end
|
44
56
|
end
|
45
57
|
end
|