vines 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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