vines 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/README +1 -1
  2. data/Rakefile +10 -10
  3. data/conf/certs/ca-bundle.crt +112 -378
  4. data/conf/config.rb +18 -9
  5. data/lib/vines.rb +8 -1
  6. data/lib/vines/command/cert.rb +2 -1
  7. data/lib/vines/command/init.rb +11 -0
  8. data/lib/vines/command/ldap.rb +6 -3
  9. data/lib/vines/command/schema.rb +1 -1
  10. data/lib/vines/config.rb +57 -146
  11. data/lib/vines/config/host.rb +85 -0
  12. data/lib/vines/config/port.rb +111 -0
  13. data/lib/vines/contact.rb +1 -1
  14. data/lib/vines/jid.rb +26 -4
  15. data/lib/vines/kit.rb +6 -0
  16. data/lib/vines/log.rb +24 -0
  17. data/lib/vines/router.rb +70 -38
  18. data/lib/vines/stanza.rb +45 -8
  19. data/lib/vines/stanza/iq.rb +3 -3
  20. data/lib/vines/stanza/iq/disco_info.rb +5 -1
  21. data/lib/vines/stanza/iq/disco_items.rb +3 -0
  22. data/lib/vines/stanza/iq/private_storage.rb +9 -5
  23. data/lib/vines/stanza/iq/roster.rb +11 -12
  24. data/lib/vines/stanza/iq/vcard.rb +4 -4
  25. data/lib/vines/stanza/iq/version.rb +25 -0
  26. data/lib/vines/stanza/message.rb +4 -5
  27. data/lib/vines/stanza/presence.rb +20 -18
  28. data/lib/vines/stanza/presence/probe.rb +3 -4
  29. data/lib/vines/stanza/presence/subscribe.rb +4 -3
  30. data/lib/vines/stanza/presence/subscribed.rb +6 -5
  31. data/lib/vines/stanza/presence/unsubscribe.rb +4 -4
  32. data/lib/vines/stanza/presence/unsubscribed.rb +4 -3
  33. data/lib/vines/storage/couchdb.rb +3 -3
  34. data/lib/vines/storage/ldap.rb +19 -8
  35. data/lib/vines/storage/local.rb +23 -12
  36. data/lib/vines/storage/redis.rb +3 -3
  37. data/lib/vines/storage/sql.rb +5 -5
  38. data/lib/vines/stream.rb +40 -6
  39. data/lib/vines/stream/client.rb +5 -6
  40. data/lib/vines/stream/client/auth.rb +3 -2
  41. data/lib/vines/stream/client/bind.rb +2 -2
  42. data/lib/vines/stream/client/bind_restart.rb +1 -2
  43. data/lib/vines/stream/client/ready.rb +2 -0
  44. data/lib/vines/stream/client/session.rb +13 -4
  45. data/lib/vines/stream/client/tls.rb +1 -0
  46. data/lib/vines/stream/component.rb +6 -5
  47. data/lib/vines/stream/component/ready.rb +5 -6
  48. data/lib/vines/stream/http.rb +10 -4
  49. data/lib/vines/stream/http/request.rb +23 -2
  50. data/lib/vines/stream/server.rb +13 -11
  51. data/lib/vines/stream/server/outbound/auth_result.rb +1 -0
  52. data/lib/vines/stream/server/outbound/tls_result.rb +1 -0
  53. data/lib/vines/stream/server/ready.rb +2 -2
  54. data/lib/vines/user.rb +2 -1
  55. data/lib/vines/version.rb +1 -1
  56. data/test/config/host_test.rb +292 -0
  57. data/test/config_test.rb +244 -103
  58. data/test/contact_test.rb +7 -1
  59. data/test/jid_test.rb +48 -0
  60. data/test/router_test.rb +16 -47
  61. data/test/stanza/iq/disco_info_test.rb +76 -0
  62. data/test/stanza/iq/disco_items_test.rb +47 -0
  63. data/test/stanza/iq/private_storage_test.rb +33 -10
  64. data/test/stanza/iq/roster_test.rb +15 -5
  65. data/test/stanza/iq/vcard_test.rb +8 -25
  66. data/test/stanza/iq/version_test.rb +62 -0
  67. data/test/stanza/iq_test.rb +13 -10
  68. data/test/stanza/message_test.rb +16 -24
  69. data/test/stanza/presence/probe_test.rb +52 -0
  70. data/test/stanza/presence/subscribe_test.rb +1 -5
  71. data/test/stanza_test.rb +77 -0
  72. data/test/stream/client/auth_test.rb +1 -0
  73. data/test/stream/client/ready_test.rb +2 -0
  74. data/test/stream/client/session_test.rb +7 -2
  75. data/test/stream/component/ready_test.rb +19 -36
  76. data/test/stream/http/request_test.rb +22 -2
  77. data/test/stream/server/ready_test.rb +14 -21
  78. data/web/404.html +9 -3
  79. data/web/chat/index.html +2 -2
  80. data/web/chat/javascripts/app.js +1 -1
  81. data/web/chat/stylesheets/chat.css +4 -9
  82. data/web/lib/coffeescripts/layout.coffee +2 -2
  83. data/web/{chat → lib}/coffeescripts/logout.coffee +0 -0
  84. data/web/lib/coffeescripts/notification.coffee +14 -0
  85. data/web/lib/coffeescripts/session.coffee +28 -24
  86. data/web/lib/coffeescripts/transfer.coffee +37 -34
  87. data/web/lib/javascripts/base.js +8 -8
  88. data/web/lib/javascripts/icons.js +3 -0
  89. data/web/lib/javascripts/jquery.js +4 -18
  90. data/web/lib/javascripts/layout.js +2 -2
  91. data/web/{chat → lib}/javascripts/logout.js +0 -0
  92. data/web/lib/javascripts/notification.js +26 -0
  93. data/web/lib/javascripts/session.js +20 -16
  94. data/web/lib/javascripts/transfer.js +45 -55
  95. data/web/lib/stylesheets/base.css +45 -9
  96. metadata +31 -15
@@ -18,6 +18,7 @@ module Vines
18
18
  if stream.encrypt?
19
19
  stream.write(PROCEED)
20
20
  stream.encrypt
21
+ stream.reset
21
22
  advance
22
23
  else
23
24
  stream.write(FAILURE)
@@ -7,17 +7,17 @@ module Vines
7
7
  # streams. This serves connected streams using the jabber:component:accept
8
8
  # namespace.
9
9
  class Component < Stream
10
- attr_reader :config, :remote_domain
10
+ attr_reader :remote_domain
11
11
 
12
12
  def initialize(config)
13
- @config = config
13
+ super
14
14
  @remote_domain = nil
15
15
  @stream_id = Kit.uuid
16
16
  advance(Start.new(self))
17
17
  end
18
18
 
19
19
  def max_stanza_size
20
- @config[:component].max_stanza_size
20
+ config[:component].max_stanza_size
21
21
  end
22
22
 
23
23
  def ready?
@@ -31,13 +31,14 @@ module Vines
31
31
  def start(node)
32
32
  @remote_domain = node['to']
33
33
  send_stream_header
34
- raise StreamErrors::HostUnknown unless @config[:component].password(@remote_domain)
34
+ raise StreamErrors::ImproperAddressing unless valid_address?(@remote_domain)
35
+ raise StreamErrors::HostUnknown unless config.component?(@remote_domain)
35
36
  raise StreamErrors::InvalidNamespace unless node.namespaces['xmlns'] == NAMESPACES[:component]
36
37
  raise StreamErrors::InvalidNamespace unless node.namespaces['xmlns:stream'] == NAMESPACES[:stream]
37
38
  end
38
39
 
39
40
  def secret
40
- password = @config[:component].password(@remote_domain)
41
+ password = config.component_password(@remote_domain)
41
42
  Digest::SHA1.hexdigest(@stream_id + password)
42
43
  end
43
44
 
@@ -7,13 +7,12 @@ module Vines
7
7
  def node(node)
8
8
  stanza = to_stanza(node)
9
9
  raise StreamErrors::UnsupportedStanzaType unless stanza
10
- to = (node['to'] || '').strip
11
- from = JID.new(node['from'] || '')
12
- raise StreamErrors::ImproperAddressing if to.empty? || from.domain != stream.remote_domain
10
+ to, from = stanza.validate_to, stanza.validate_from
11
+ raise StreamErrors::ImproperAddressing unless to && from
12
+ raise StreamErrors::InvalidFrom unless from.domain == stream.remote_domain
13
+ stream.user = User.new(:jid => from)
13
14
  if stanza.local?
14
- stream.router.connected_resources(to).each do |recipient|
15
- recipient.write(node)
16
- end
15
+ stanza.process
17
16
  else
18
17
  stanza.route
19
18
  end
@@ -13,6 +13,11 @@ module Vines
13
13
  def post_init
14
14
  super
15
15
  router.delete(self)
16
+ end
17
+
18
+ # Override +Stream#create_parser+ to provide an HTTP parser rather than
19
+ # a Nokogiri XML parser.
20
+ def create_parser
16
21
  @parser = ::Http::Parser.new.tap do |p|
17
22
  body = ''
18
23
  p.on_body = proc {|data| body << data }
@@ -33,9 +38,9 @@ module Vines
33
38
  !!session
34
39
  end
35
40
 
36
- %w[max_stanza_size max_resources_per_account private_storage? bind root].each do |name|
41
+ %w[max_stanza_size max_resources_per_account bind root].each do |name|
37
42
  define_method name do |*args|
38
- @config[:http].send(name, *args)
43
+ config[:http].send(name, *args)
39
44
  end
40
45
  end
41
46
 
@@ -86,7 +91,8 @@ module Vines
86
91
 
87
92
  raise StreamErrors::UndefinedCondition.new('rid required') if rid.empty?
88
93
  raise StreamErrors::UnsupportedVersion unless version == '1.0'
89
- raise StreamErrors::HostUnknown unless @config.vhost?(domain)
94
+ raise StreamErrors::ImproperAddressing unless valid_address?(domain)
95
+ raise StreamErrors::HostUnknown unless config.vhost?(domain)
90
96
  raise StreamErrors::InvalidNamespace unless node.namespaces['xmlns'] == NAMESPACES[:http_bind]
91
97
 
92
98
  Sessions[@session.id] = @session
@@ -109,7 +115,7 @@ module Vines
109
115
  doc = Nokogiri::XML::Document.new
110
116
  node = doc.create_element('body',
111
117
  'charsets' => 'UTF-8',
112
- 'from' => @session.domain,
118
+ 'from' => @session.domain,
113
119
  'hold' => @session.hold,
114
120
  'inactivity' => @session.inactivity,
115
121
  'polling' => '5',
@@ -6,6 +6,7 @@ module Vines
6
6
  class Request
7
7
  BUF_SIZE = 1024
8
8
  MODIFIED = '%a, %d %b %Y %H:%M:%S GMT'.freeze
9
+ MOVED = 'Moved Permanently'.freeze
9
10
  NOT_FOUND = 'Not Found'.freeze
10
11
  NOT_MODIFIED = 'Not Modified'.freeze
11
12
  IF_MODIFIED = 'If-Modified-Since'.freeze
@@ -44,6 +45,13 @@ module Vines
44
45
  # to implement caching.
45
46
  def reply_with_file(dir)
46
47
  path = File.expand_path(File.join(dir, @path))
48
+
49
+ # redirect requests missing a slash so relative links work
50
+ if File.directory?(path) && !@path.end_with?('/')
51
+ send_status(301, MOVED, "Location: #{redirect_uri}")
52
+ return
53
+ end
54
+
47
55
  path = File.join(path, 'index.html') if File.directory?(path)
48
56
 
49
57
  if path.start_with?(dir) && File.exist?(path)
@@ -72,6 +80,18 @@ module Vines
72
80
 
73
81
  private
74
82
 
83
+ # Attempt to rebuild the full request URI from the Host header. If it
84
+ # wasn't sent by the client, just return the relative path that
85
+ # was requested. The Location response header must contain the fully
86
+ # qualified URI, but most browsers will accept relative paths as well.
87
+ def redirect_uri
88
+ host = headers['Host']
89
+ uri = "#{path}/"
90
+ uri = "#{uri}?#{query}" unless (query || '').empty?
91
+ uri = "http://#{host}#{uri}" if host
92
+ uri
93
+ end
94
+
75
95
  # Return true if the file has been modified since the client last
76
96
  # requested it with the If-Modified-Since header.
77
97
  def modified?(path)
@@ -82,10 +102,11 @@ module Vines
82
102
  File.mtime(path).utc.strftime(MODIFIED)
83
103
  end
84
104
 
85
- def send_status(status, message)
105
+ def send_status(status, message, *headers)
86
106
  header = [
87
107
  "HTTP/1.1 #{status} #{message}",
88
- "Connection: close"
108
+ "Connection: close",
109
+ *headers
89
110
  ].join("\r\n")
90
111
  @stream.stream_write("#{header}\r\n\r\n")
91
112
  @stream.close_connection_after_writing
@@ -23,7 +23,7 @@ module Vines
23
23
  end
24
24
  cb = proc do |srv|
25
25
  if srv.empty?
26
- srv << {:target => to, :port => 5269}
26
+ srv << {target: to, port: 5269}
27
27
  class << srv.first
28
28
  def method_missing(name); self[name]; end
29
29
  end
@@ -39,7 +39,7 @@ module Vines
39
39
  else
40
40
  begin
41
41
  rr = srv.shift
42
- opts = {:to => to, :from => from, :srv => srv, :callback => callback}
42
+ opts = {to: to, from: from, srv: srv, callback: callback}
43
43
  EM.connect(rr.target.to_s, rr.port, Server, config, opts)
44
44
  rescue Exception => e
45
45
  connect(config, to, from, srv, callback)
@@ -47,11 +47,12 @@ module Vines
47
47
  end
48
48
  end
49
49
 
50
- attr_reader :config, :domain
50
+ attr_reader :domain
51
51
  attr_accessor :remote_domain
52
52
 
53
53
  def initialize(config, options={})
54
- @config = config
54
+ super(config)
55
+ @connected = false
55
56
  @remote_domain = options[:to]
56
57
  @domain = options[:from]
57
58
  @srv = options[:srv]
@@ -67,7 +68,7 @@ module Vines
67
68
  end
68
69
 
69
70
  def max_stanza_size
70
- @config[:server].max_stanza_size
71
+ config[:server].max_stanza_size
71
72
  end
72
73
 
73
74
  def ssl_handshake_completed
@@ -80,16 +81,17 @@ module Vines
80
81
 
81
82
  def unbind
82
83
  super
83
- if @outbound && !ready?
84
- Server.connect(@config, @remote_domain, @domain, @srv, @callback)
84
+ if @outbound && !@connected
85
+ Server.connect(config, @remote_domain, @domain, @srv, @callback)
85
86
  end
86
87
  end
87
88
 
88
89
  def vhost?(domain)
89
- @config.vhost?(domain)
90
+ config.vhost?(domain)
90
91
  end
91
92
 
92
93
  def notify_connected
94
+ @connected = true
93
95
  if @callback
94
96
  @callback.call(self)
95
97
  @callback = nil
@@ -105,9 +107,9 @@ module Vines
105
107
  @domain, @remote_domain = %w[to from].map {|a| node[a] }
106
108
  send_stream_header
107
109
  raise StreamErrors::UnsupportedVersion unless node['version'] == '1.0'
108
- raise StreamErrors::ImproperAddressing if [@domain, @remote_domain].any? {|addr| (addr || '').strip.empty? }
109
- raise StreamErrors::HostUnknown unless @config.vhost?(@domain)
110
- raise StreamErrors::NotAuthorized unless @config.s2s?(@remote_domain)
110
+ raise StreamErrors::ImproperAddressing unless valid_address?(@domain) && valid_address?(@remote_domain)
111
+ raise StreamErrors::HostUnknown unless config.vhost?(@domain)
112
+ raise StreamErrors::NotAuthorized unless config.s2s?(@remote_domain)
111
113
  raise StreamErrors::InvalidNamespace unless node.namespaces['xmlns'] == NAMESPACES[:server]
112
114
  raise StreamErrors::InvalidNamespace unless node.namespaces['xmlns:stream'] == NAMESPACES[:stream]
113
115
  end
@@ -14,6 +14,7 @@ module Vines
14
14
  case node.name
15
15
  when 'success'
16
16
  stream.start(node)
17
+ stream.reset
17
18
  advance
18
19
  when 'failure'
19
20
  stream.close_connection
@@ -17,6 +17,7 @@ module Vines
17
17
  when 'proceed'
18
18
  stream.encrypt
19
19
  stream.start(node)
20
+ stream.reset
20
21
  advance
21
22
  when 'failure'
22
23
  stream.close_connection
@@ -7,8 +7,8 @@ module Vines
7
7
  def node(node)
8
8
  stanza = to_stanza(node)
9
9
  raise StreamErrors::UnsupportedStanzaType unless stanza
10
- to, from = %w[to from].map {|attr| JID.new(stanza[attr] || '') }
11
- raise StreamErrors::ImproperAddressing if [to, from].any? {|addr| (addr.domain || '').strip.empty? }
10
+ to, from = stanza.validate_to, stanza.validate_from
11
+ raise StreamErrors::ImproperAddressing unless to && from
12
12
  raise StreamErrors::InvalidFrom unless from.domain == stream.remote_domain
13
13
  raise StreamErrors::HostUnknown unless to.domain == stream.domain
14
14
  stream.user = User.new(:jid => from)
data/lib/vines/user.rb CHANGED
@@ -9,7 +9,8 @@ module Vines
9
9
 
10
10
  def initialize(args={})
11
11
  @jid = JID.new(args[:jid])
12
- raise ArgumentError, 'invalid jid' unless @jid.node && !@jid.domain.empty?
12
+ raise ArgumentError, 'invalid jid' if @jid.empty?
13
+
13
14
  @name = args[:name]
14
15
  @password = args[:password]
15
16
  @roster = args[:roster] || []
data/lib/vines/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module Vines
4
- VERSION = '0.2.1'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -0,0 +1,292 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'minitest/autorun'
5
+
6
+ class HostTest < MiniTest::Unit::TestCase
7
+ def test_missing_storage
8
+ assert_raises(RuntimeError) do
9
+ Vines::Config.new do
10
+ host 'wonderland.lit' do
11
+ # missing storage
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ def test_bad_storage
18
+ assert_raises(RuntimeError) do
19
+ Vines::Config.new do
20
+ host 'wonderland.lit' do
21
+ storage 'bogus' do
22
+ # no bogus storage implementation
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ def test_duplicate_storage
30
+ assert_raises(RuntimeError) do
31
+ Vines::Config.new do
32
+ host 'wonderland.lit' do
33
+ storage('fs') { dir '.' }
34
+ storage('fs') { dir '.' }
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def test_good_storage_raises_no_errors
41
+ config = Vines::Config.new do
42
+ host 'wonderland.lit' do
43
+ storage 'fs' do
44
+ dir '.'
45
+ end
46
+ end
47
+ end
48
+ refute_nil config.vhosts['wonderland.lit'].storage
49
+ end
50
+
51
+ def test_ldap_added_to_storage
52
+ config = Vines::Config.new do
53
+ host 'wonderland.lit' do
54
+ storage(:fs) { dir '.' }
55
+ # added after storage
56
+ ldap 'ldap.wonderland.lit', 1636 do
57
+ tls true
58
+ dn 'cn=Directory Manager'
59
+ password 'secr3t'
60
+ basedn 'dc=wonderland,dc=lit'
61
+ groupdn 'cn=chatters,dc=wonderland,dc=lit'
62
+ object_class 'person'
63
+ user_attr 'uid'
64
+ name_attr 'cn'
65
+ end
66
+ end
67
+
68
+ host 'verona.lit' do
69
+ ldap 'ldap.verona.lit', 1636 do
70
+ tls true
71
+ dn 'cn=Directory Manager'
72
+ password 'secr3t'
73
+ basedn 'dc=wonderland,dc=lit'
74
+ object_class 'person'
75
+ user_attr 'uid'
76
+ name_attr 'cn'
77
+ end
78
+ # added before storage
79
+ storage(:fs) { dir '.' }
80
+ end
81
+ end
82
+ %w[wonderland.lit verona.lit].each do |domain|
83
+ refute_nil config.vhosts[domain].storage.ldap
84
+ assert config.vhosts[domain].storage.ldap?
85
+ end
86
+ end
87
+
88
+ def test_empty_component_name_raises
89
+ assert_raises(RuntimeError) do
90
+ Vines::Config.new do
91
+ host 'wonderland.lit' do
92
+ storage(:fs) { dir '.' }
93
+ components '' => 'secr3t'
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ def test_nil_component_name_raises
100
+ assert_raises(RuntimeError) do
101
+ Vines::Config.new do
102
+ host 'wonderland.lit' do
103
+ storage(:fs) { dir '.' }
104
+ components nil => 'secr3t'
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ def test_empty_component_password_raises
111
+ assert_raises(RuntimeError) do
112
+ Vines::Config.new do
113
+ host 'wonderland.lit' do
114
+ storage(:fs) { dir '.' }
115
+ components 'tea' => ''
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ def test_nil_component_password_raises
122
+ assert_raises(RuntimeError) do
123
+ Vines::Config.new do
124
+ host 'wonderland.lit' do
125
+ storage(:fs) { dir '.' }
126
+ components 'tea' => nil
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ def test_duplicate_component_raises
133
+ assert_raises(RuntimeError) do
134
+ Vines::Config.new do
135
+ host 'wonderland.lit' do
136
+ storage(:fs) { dir '.' }
137
+ components 'tea' => 'one'
138
+ components 'TEA' => 'two'
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ def test_duplicate_component_in_one_call_raises
145
+ assert_raises(RuntimeError) do
146
+ Vines::Config.new do
147
+ host 'wonderland.lit' do
148
+ storage(:fs) { dir '.' }
149
+ components 'tea' => 'one', 'TEA' => 'two'
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ def test_duplicate_component_symbol_raises
156
+ assert_raises(RuntimeError) do
157
+ Vines::Config.new do
158
+ host 'wonderland.lit' do
159
+ storage(:fs) { dir '.' }
160
+ components 'tea' => 'one'
161
+ components :TEA => 'two'
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ def test_invalid_host_domain_raises
168
+ assert_raises(ArgumentError) do
169
+ Vines::Config.new do
170
+ host 'wonderland.lit ' do
171
+ storage(:fs) { dir '.' }
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ def test_invalid_jid_host_domain_raises
178
+ assert_raises(RuntimeError) do
179
+ Vines::Config.new do
180
+ host 'alice@wonderland.lit' do
181
+ storage(:fs) { dir '.' }
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ def test_invalid_component_domain_raises
188
+ assert_raises(ArgumentError) do
189
+ Vines::Config.new do
190
+ host 'wonderland.lit' do
191
+ storage(:fs) { dir '.' }
192
+ components 'exam ple' => 'one'
193
+ end
194
+ end
195
+ end
196
+ end
197
+
198
+ def test_invalid_jid_component_domain_raises
199
+ assert_raises(RuntimeError) do
200
+ Vines::Config.new do
201
+ host 'wonderland.lit' do
202
+ storage(:fs) { dir '.' }
203
+ components 'alice@example' => 'one'
204
+ end
205
+ end
206
+ end
207
+ end
208
+
209
+ def test_multi_subdomain_component_raises
210
+ assert_raises(RuntimeError) do
211
+ Vines::Config.new do
212
+ host 'wonderland.lit' do
213
+ storage(:fs) { dir '.' }
214
+ components 'exam.ple' => 'one'
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ def test_case_insensitive_component_name
221
+ config = Vines::Config.new do
222
+ host 'WONDERLAND.LIT' do
223
+ storage(:fs) { dir '.' }
224
+ components 'TEA' => 'secr3t', CAKE: 'Passw0rd'
225
+ end
226
+ end
227
+ host = config.vhosts['wonderland.lit']
228
+ refute_nil host
229
+ assert_equal 2, host.components.size
230
+ assert_equal host.components['tea.wonderland.lit'], 'secr3t'
231
+ assert_equal host.components['cake.wonderland.lit'], 'Passw0rd'
232
+ end
233
+
234
+ def test_component?
235
+ config = Vines::Config.new do
236
+ host 'wonderland.lit' do
237
+ storage(:fs) { dir '.' }
238
+ components 'tea' => 'secr3t', cake: 'passw0rd'
239
+ end
240
+ end
241
+ host = config.vhosts['wonderland.lit']
242
+ refute_nil host
243
+ refute host.component?(nil)
244
+ refute host.component?('tea')
245
+ refute host.component?(:cake)
246
+ assert host.component?('tea.wonderland.lit')
247
+ assert host.component?('cake.wonderland.lit')
248
+ assert_nil host.password(nil)
249
+ assert_nil host.password('bogus')
250
+ assert_equal 'secr3t', host.password('tea.wonderland.lit')
251
+ assert_equal 'passw0rd', host.password('cake.wonderland.lit')
252
+ expected = {'tea.wonderland.lit' => 'secr3t', 'cake.wonderland.lit' => 'passw0rd'}
253
+ assert_equal expected, host.components
254
+
255
+ refute config.component?(nil)
256
+ refute config.component?('tea')
257
+ refute config.component?('bogus')
258
+ assert config.component?('tea.wonderland.lit')
259
+ assert config.component?('cake.wonderland.lit')
260
+ assert config.component?('tea.wonderland.lit', 'cake.wonderland.lit')
261
+ refute config.component?('tea.wonderland.lit', 'bogus.wonderland.lit')
262
+
263
+ assert_nil config.component_password(nil)
264
+ assert_nil config.component_password('bogus')
265
+ assert_equal 'secr3t', config.component_password('tea.wonderland.lit')
266
+ assert_equal 'passw0rd', config.component_password('cake.wonderland.lit')
267
+ end
268
+
269
+ def test_default_private_storage_is_off
270
+ config = Vines::Config.new do
271
+ host 'wonderland.lit' do
272
+ storage(:fs) { dir '.' }
273
+ end
274
+ end
275
+ host = config.vhosts['wonderland.lit']
276
+ refute_nil host
277
+ refute host.private_storage?
278
+ end
279
+
280
+ def test_enable_private_storage
281
+ config = Vines::Config.new do
282
+ host 'wonderland.lit' do
283
+ private_storage true
284
+ storage(:fs) { dir '.' }
285
+ end
286
+ end
287
+ host = config.vhosts['wonderland.lit']
288
+ refute_nil host
289
+ assert host.private_storage?
290
+ assert config.private_storage?('wonderland.lit')
291
+ end
292
+ end