chef 12.4.3 → 12.5.0.alpha.1

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -2
  3. data/lib/chef.rb +1 -1
  4. data/lib/chef/application/solo.rb +1 -1
  5. data/lib/chef/application/windows_service_manager.rb +17 -12
  6. data/lib/chef/chef_class.rb +7 -0
  7. data/lib/chef/chef_fs/config.rb +22 -24
  8. data/lib/chef/chef_fs/file_pattern.rb +4 -15
  9. data/lib/chef/chef_fs/file_system/cookbook_dir.rb +1 -0
  10. data/lib/chef/chef_fs/knife.rb +35 -7
  11. data/lib/chef/chef_fs/path_utils.rb +65 -34
  12. data/lib/chef/constants.rb +27 -0
  13. data/lib/chef/delayed_evaluator.rb +21 -0
  14. data/lib/chef/dsl/recipe.rb +20 -2
  15. data/lib/chef/event_dispatch/base.rb +40 -16
  16. data/lib/chef/event_dispatch/dsl.rb +64 -0
  17. data/lib/chef/exceptions.rb +6 -1
  18. data/lib/chef/formatters/doc.rb +3 -1
  19. data/lib/chef/guard_interpreter/resource_guard_interpreter.rb +3 -1
  20. data/lib/chef/http/http_request.rb +1 -1
  21. data/lib/chef/knife/bootstrap/templates/chef-full.erb +1 -1
  22. data/lib/chef/knife/ssl_check.rb +3 -2
  23. data/lib/chef/knife/user_edit.rb +1 -2
  24. data/lib/chef/mixin/params_validate.rb +362 -135
  25. data/lib/chef/node.rb +19 -0
  26. data/lib/chef/platform/handler_map.rb +0 -5
  27. data/lib/chef/platform/rebooter.rb +1 -1
  28. data/lib/chef/property.rb +539 -0
  29. data/lib/chef/provider.rb +129 -12
  30. data/lib/chef/provider/deploy.rb +3 -5
  31. data/lib/chef/provider/lwrp_base.rb +1 -75
  32. data/lib/chef/provider/package.rb +1 -1
  33. data/lib/chef/provider/powershell_script.rb +32 -19
  34. data/lib/chef/provider/registry_key.rb +5 -5
  35. data/lib/chef/provider/service/macosx.rb +5 -1
  36. data/lib/chef/recipe.rb +1 -8
  37. data/lib/chef/resource.rb +499 -84
  38. data/lib/chef/resource/file/verification.rb +7 -1
  39. data/lib/chef/resource/lwrp_base.rb +1 -7
  40. data/lib/chef/run_context.rb +404 -83
  41. data/lib/chef/version.rb +1 -1
  42. data/lib/chef/win32/registry.rb +10 -2
  43. data/lib/chef/workstation_config_loader.rb +3 -158
  44. data/spec/data/run_context/cookbooks/include/recipes/default.rb +24 -0
  45. data/spec/data/run_context/cookbooks/include/recipes/includee.rb +3 -0
  46. data/spec/functional/rebooter_spec.rb +1 -1
  47. data/spec/functional/resource/{powershell_spec.rb → powershell_script_spec.rb} +3 -3
  48. data/spec/functional/win32/registry_helper_spec.rb +12 -0
  49. data/spec/functional/win32/service_manager_spec.rb +2 -2
  50. data/spec/integration/knife/chef_repo_path_spec.rb +13 -11
  51. data/spec/integration/recipes/recipe_dsl_spec.rb +0 -15
  52. data/spec/integration/recipes/resource_action_spec.rb +343 -0
  53. data/spec/spec_helper.rb +1 -0
  54. data/spec/support/shared/functional/win32_service.rb +2 -1
  55. data/spec/unit/application/solo_spec.rb +4 -3
  56. data/spec/unit/chef_class_spec.rb +23 -0
  57. data/spec/unit/chef_fs/path_util_spec.rb +108 -0
  58. data/spec/unit/event_dispatch/dsl_spec.rb +87 -0
  59. data/spec/unit/json_compat_spec.rb +4 -3
  60. data/spec/unit/knife/ssl_check_spec.rb +4 -0
  61. data/spec/unit/mixin/params_validate_spec.rb +4 -2
  62. data/spec/unit/node_spec.rb +7 -0
  63. data/spec/unit/property/state_spec.rb +506 -0
  64. data/spec/unit/property/validation_spec.rb +658 -0
  65. data/spec/unit/property_spec.rb +968 -0
  66. data/spec/unit/provider/{powershell_spec.rb → powershell_script_spec.rb} +0 -0
  67. data/spec/unit/provider/registry_key_spec.rb +12 -0
  68. data/spec/unit/provider/service/macosx_spec.rb +4 -4
  69. data/spec/unit/provider_spec.rb +1 -3
  70. data/spec/unit/recipe_spec.rb +0 -4
  71. data/spec/unit/registry_helper_spec.rb +15 -1
  72. data/spec/unit/resource/file/verification_spec.rb +33 -5
  73. data/spec/unit/resource/{powershell_spec.rb → powershell_script_spec.rb} +0 -0
  74. data/spec/unit/resource_spec.rb +2 -2
  75. data/spec/unit/run_context/child_run_context_spec.rb +133 -0
  76. data/spec/unit/run_context_spec.rb +7 -0
  77. metadata +25 -25
  78. data/spec/unit/workstation_config_loader_spec.rb +0 -283
@@ -0,0 +1,343 @@
1
+ require 'support/shared/integration/integration_helper'
2
+
3
+ describe "Resource.action" do
4
+ include IntegrationSupport
5
+
6
+ def converge(str=nil, file=nil, line=nil, &block)
7
+ if block
8
+ super(&block)
9
+ else
10
+ super() do
11
+ eval(str, nil, file, line)
12
+ end
13
+ end
14
+ end
15
+
16
+ shared_context "ActionJackson" do
17
+ it "The default action is the first declared action" do
18
+ converge <<-EOM, __FILE__, __LINE__+1
19
+ #{resource_dsl} 'hi' do
20
+ foo 'foo!'
21
+ end
22
+ EOM
23
+ expect(ActionJackson.ran_action).to eq :access_recipe_dsl
24
+ expect(ActionJackson.succeeded).to eq true
25
+ end
26
+
27
+ it "The action can access recipe DSL" do
28
+ converge <<-EOM, __FILE__, __LINE__+1
29
+ #{resource_dsl} 'hi' do
30
+ foo 'foo!'
31
+ action :access_recipe_dsl
32
+ end
33
+ EOM
34
+ expect(ActionJackson.ran_action).to eq :access_recipe_dsl
35
+ expect(ActionJackson.succeeded).to eq true
36
+ end
37
+
38
+ it "The action can access attributes" do
39
+ converge <<-EOM, __FILE__, __LINE__+1
40
+ #{resource_dsl} 'hi' do
41
+ foo 'foo!'
42
+ action :access_attribute
43
+ end
44
+ EOM
45
+ expect(ActionJackson.ran_action).to eq :access_attribute
46
+ expect(ActionJackson.succeeded).to eq 'foo!'
47
+ end
48
+
49
+ it "The action can access public methods" do
50
+ converge <<-EOM, __FILE__, __LINE__+1
51
+ #{resource_dsl} 'hi' do
52
+ foo 'foo!'
53
+ action :access_method
54
+ end
55
+ EOM
56
+ expect(ActionJackson.ran_action).to eq :access_method
57
+ expect(ActionJackson.succeeded).to eq 'foo_public!'
58
+ end
59
+
60
+ it "The action can access protected methods" do
61
+ converge <<-EOM, __FILE__, __LINE__+1
62
+ #{resource_dsl} 'hi' do
63
+ foo 'foo!'
64
+ action :access_protected_method
65
+ end
66
+ EOM
67
+ expect(ActionJackson.ran_action).to eq :access_protected_method
68
+ expect(ActionJackson.succeeded).to eq 'foo_protected!'
69
+ end
70
+
71
+ it "The action cannot access private methods" do
72
+ expect {
73
+ converge(<<-EOM, __FILE__, __LINE__+1)
74
+ #{resource_dsl} 'hi' do
75
+ foo 'foo!'
76
+ action :access_private_method
77
+ end
78
+ EOM
79
+ }.to raise_error(NameError)
80
+ expect(ActionJackson.ran_action).to eq :access_private_method
81
+ end
82
+
83
+ it "The action cannot access resource instance variables" do
84
+ converge <<-EOM, __FILE__, __LINE__+1
85
+ #{resource_dsl} 'hi' do
86
+ foo 'foo!'
87
+ action :access_instance_variable
88
+ end
89
+ EOM
90
+ expect(ActionJackson.ran_action).to eq :access_instance_variable
91
+ expect(ActionJackson.succeeded).to be_nil
92
+ end
93
+
94
+ it "The action does not compile until the prior resource has converged" do
95
+ converge <<-EOM, __FILE__, __LINE__+1
96
+ ruby_block 'wow' do
97
+ block do
98
+ ActionJackson.ruby_block_converged = 'ruby_block_converged!'
99
+ end
100
+ end
101
+
102
+ #{resource_dsl} 'hi' do
103
+ foo 'foo!'
104
+ action :access_class_method
105
+ end
106
+ EOM
107
+ expect(ActionJackson.ran_action).to eq :access_class_method
108
+ expect(ActionJackson.succeeded).to eq 'ruby_block_converged!'
109
+ end
110
+
111
+ it "The action's resources converge before the next resource converges" do
112
+ converge <<-EOM, __FILE__, __LINE__+1
113
+ #{resource_dsl} 'hi' do
114
+ foo 'foo!'
115
+ action :access_attribute
116
+ end
117
+
118
+ ruby_block 'wow' do
119
+ block do
120
+ ActionJackson.ruby_block_converged = ActionJackson.succeeded
121
+ end
122
+ end
123
+ EOM
124
+ expect(ActionJackson.ran_action).to eq :access_attribute
125
+ expect(ActionJackson.succeeded).to eq 'foo!'
126
+ expect(ActionJackson.ruby_block_converged).to eq 'foo!'
127
+ end
128
+ end
129
+
130
+ context "With resource 'action_jackson'" do
131
+ before(:context) {
132
+ class ActionJackson < Chef::Resource
133
+ use_automatic_resource_name
134
+ def foo(value=nil)
135
+ @foo = value if value
136
+ @foo
137
+ end
138
+ def blarghle(value=nil)
139
+ @blarghle = value if value
140
+ @blarghle
141
+ end
142
+
143
+ class <<self
144
+ attr_accessor :ran_action
145
+ attr_accessor :succeeded
146
+ attr_accessor :ruby_block_converged
147
+ end
148
+
149
+ public
150
+ def foo_public
151
+ 'foo_public!'
152
+ end
153
+ protected
154
+ def foo_protected
155
+ 'foo_protected!'
156
+ end
157
+ private
158
+ def foo_private
159
+ 'foo_private!'
160
+ end
161
+
162
+ public
163
+ action :access_recipe_dsl do
164
+ ActionJackson.ran_action = :access_recipe_dsl
165
+ ruby_block 'hi there' do
166
+ block do
167
+ ActionJackson.succeeded = true
168
+ end
169
+ end
170
+ end
171
+ action :access_attribute do
172
+ ActionJackson.ran_action = :access_attribute
173
+ ActionJackson.succeeded = foo
174
+ ActionJackson.succeeded += " #{blarghle}" if blarghle
175
+ ActionJackson.succeeded += " #{bar}" if respond_to?(:bar)
176
+ end
177
+ action :access_attribute2 do
178
+ ActionJackson.ran_action = :access_attribute2
179
+ ActionJackson.succeeded = foo
180
+ ActionJackson.succeeded += " #{blarghle}" if blarghle
181
+ ActionJackson.succeeded += " #{bar}" if respond_to?(:bar)
182
+ end
183
+ action :access_method do
184
+ ActionJackson.ran_action = :access_method
185
+ ActionJackson.succeeded = foo_public
186
+ end
187
+ action :access_protected_method do
188
+ ActionJackson.ran_action = :access_protected_method
189
+ ActionJackson.succeeded = foo_protected
190
+ end
191
+ action :access_private_method do
192
+ ActionJackson.ran_action = :access_private_method
193
+ ActionJackson.succeeded = foo_private
194
+ end
195
+ action :access_instance_variable do
196
+ ActionJackson.ran_action = :access_instance_variable
197
+ ActionJackson.succeeded = @foo
198
+ end
199
+ action :access_class_method do
200
+ ActionJackson.ran_action = :access_class_method
201
+ ActionJackson.succeeded = ActionJackson.ruby_block_converged
202
+ end
203
+ end
204
+ }
205
+ before(:each) {
206
+ ActionJackson.ran_action = :error
207
+ ActionJackson.succeeded = :error
208
+ ActionJackson.ruby_block_converged = :error
209
+ }
210
+
211
+ it_behaves_like "ActionJackson" do
212
+ let(:resource_dsl) { :action_jackson }
213
+ end
214
+
215
+ context "And 'action_jackgrandson' inheriting from ActionJackson and changing nothing" do
216
+ before(:context) {
217
+ class ActionJackgrandson < ActionJackson
218
+ use_automatic_resource_name
219
+ end
220
+ }
221
+
222
+ it_behaves_like "ActionJackson" do
223
+ let(:resource_dsl) { :action_jackgrandson }
224
+ end
225
+ end
226
+
227
+ context "And 'action_jackalope' inheriting from ActionJackson with an extra attribute and action" do
228
+ before(:context) {
229
+ class ActionJackalope < ActionJackson
230
+ use_automatic_resource_name
231
+
232
+ def foo(value=nil)
233
+ @foo = "#{value}alope" if value
234
+ @foo
235
+ end
236
+ def bar(value=nil)
237
+ @bar = "#{value}alope" if value
238
+ @bar
239
+ end
240
+ class <<self
241
+ attr_accessor :jackalope_ran
242
+ end
243
+ action :access_jackalope do
244
+ ActionJackalope.jackalope_ran = :access_jackalope
245
+ ActionJackalope.succeeded = "#{foo} #{blarghle} #{bar}"
246
+ end
247
+ action :access_attribute do
248
+ super()
249
+ ActionJackalope.jackalope_ran = :access_attribute
250
+ ActionJackalope.succeeded = ActionJackson.succeeded
251
+ end
252
+ end
253
+ }
254
+ before do
255
+ ActionJackalope.jackalope_ran = nil
256
+ end
257
+
258
+ context "action_jackson still behaves the same" do
259
+ it_behaves_like "ActionJackson" do
260
+ let(:resource_dsl) { :action_jackson }
261
+ end
262
+ end
263
+
264
+ it "The default action remains the same even though new actions were specified first" do
265
+ converge {
266
+ action_jackalope 'hi' do
267
+ foo 'foo!'
268
+ bar 'bar!'
269
+ end
270
+ }
271
+ expect(ActionJackson.ran_action).to eq :access_recipe_dsl
272
+ expect(ActionJackson.succeeded).to eq true
273
+ end
274
+
275
+ it "new actions run, and can access overridden, new, and overridden attributes" do
276
+ converge {
277
+ action_jackalope 'hi' do
278
+ foo 'foo!'
279
+ bar 'bar!'
280
+ blarghle 'blarghle!'
281
+ action :access_jackalope
282
+ end
283
+ }
284
+ expect(ActionJackalope.jackalope_ran).to eq :access_jackalope
285
+ expect(ActionJackalope.succeeded).to eq "foo!alope blarghle! bar!alope"
286
+ end
287
+
288
+ it "overridden actions run, call super, and can access overridden, new, and overridden attributes" do
289
+ converge {
290
+ action_jackalope 'hi' do
291
+ foo 'foo!'
292
+ bar 'bar!'
293
+ blarghle 'blarghle!'
294
+ action :access_attribute
295
+ end
296
+ }
297
+ expect(ActionJackson.ran_action).to eq :access_attribute
298
+ expect(ActionJackson.succeeded).to eq "foo!alope blarghle! bar!alope"
299
+ expect(ActionJackalope.jackalope_ran).to eq :access_attribute
300
+ expect(ActionJackalope.succeeded).to eq "foo!alope blarghle! bar!alope"
301
+ end
302
+
303
+ it "non-overridden actions run and can access overridden and non-overridden variables (but not necessarily new ones)" do
304
+ converge {
305
+ action_jackalope 'hi' do
306
+ foo 'foo!'
307
+ bar 'bar!'
308
+ blarghle 'blarghle!'
309
+ action :access_attribute2
310
+ end
311
+ }
312
+ expect(ActionJackson.ran_action).to eq :access_attribute2
313
+ expect(ActionJackson.succeeded).to eq("foo!alope blarghle! bar!alope").or(eq("foo!alope blarghle!"))
314
+ end
315
+ end
316
+ end
317
+
318
+ context "With a resource with no actions" do
319
+ before(:context) {
320
+ class NoActionJackson < Chef::Resource
321
+ use_automatic_resource_name
322
+
323
+ def foo(value=nil)
324
+ @foo = value if value
325
+ @foo
326
+ end
327
+
328
+ class <<self
329
+ attr_accessor :action_was
330
+ end
331
+ end
332
+ }
333
+ it "The default action is :nothing" do
334
+ converge {
335
+ no_action_jackson 'hi' do
336
+ foo 'foo!'
337
+ NoActionJackson.action_was = action
338
+ end
339
+ }
340
+ expect(NoActionJackson.action_was).to eq [:nothing]
341
+ end
342
+ end
343
+ end
@@ -118,6 +118,7 @@ RSpec.configure do |config|
118
118
  config.filter_run_excluding :volatile_from_verify => false
119
119
 
120
120
  config.filter_run_excluding :skip_appveyor => true if ENV["APPVEYOR"]
121
+ config.filter_run_excluding :appveyor_only => true unless ENV["APPVEYOR"]
121
122
 
122
123
  config.filter_run_excluding :windows_only => true unless windows?
123
124
  config.filter_run_excluding :not_supported_on_mac_osx_106 => true if mac_osx_106?
@@ -46,7 +46,8 @@ shared_context "using Win32::Service" do
46
46
  :service_name => "spec-service",
47
47
  :service_display_name => "Spec Test Service",
48
48
  :service_description => "Service for testing Chef::Application::WindowsServiceManager.",
49
- :service_file_path => File.expand_path(File.join(File.dirname(__FILE__), '../../platforms/win32/spec_service.rb'))
49
+ :service_file_path => File.expand_path(File.join(File.dirname(__FILE__), '../../platforms/win32/spec_service.rb')),
50
+ :delayed_start => true
50
51
  }
51
52
  }
52
53
 
@@ -106,7 +106,8 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
106
106
  describe "when the recipe_url configuration option is specified" do
107
107
  let(:tarfile) { StringIO.new("remote_tarball_content") }
108
108
  let(:target_file) { StringIO.new }
109
-
109
+ let(:shellout) { double(run_command: nil, error!: nil, stdout: '') }
110
+
110
111
  before do
111
112
  Chef::Config[:cookbook_path] = "#{Dir.tmpdir}/chef-solo/cookbooks"
112
113
  Chef::Config[:recipe_url] = "http://junglist.gen.nz/recipes.tgz"
@@ -117,7 +118,7 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
117
118
  allow(app).to receive(:open).with("http://junglist.gen.nz/recipes.tgz").and_yield(tarfile)
118
119
  allow(File).to receive(:open).with("#{Dir.tmpdir}/chef-solo/recipes.tgz", "wb").and_yield(target_file)
119
120
 
120
- allow(Chef::Mixin::Command).to receive(:run_command).and_return(true)
121
+ allow(Mixlib::ShellOut).to receive(:new).and_return(shellout)
121
122
  end
122
123
 
123
124
  it "should create the recipes path based on the parent of the cookbook path" do
@@ -136,7 +137,7 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
136
137
  end
137
138
 
138
139
  it "should untar the target file to the parent of the cookbook path" do
139
- expect(Chef::Mixin::Command).to receive(:run_command).with({:command => "tar zxvf #{Dir.tmpdir}/chef-solo/recipes.tgz -C #{Dir.tmpdir}/chef-solo"}).and_return(true)
140
+ expect(Mixlib::ShellOut).to receive(:new).with("tar zxvf #{Dir.tmpdir}/chef-solo/recipes.tgz -C #{Dir.tmpdir}/chef-solo")
140
141
  app.reconfigure
141
142
  end
142
143
  end
@@ -88,4 +88,27 @@ describe "Chef class" do
88
88
  expect(Chef.node).to eql(node)
89
89
  end
90
90
  end
91
+
92
+ context '#event_handler' do
93
+ it 'adds a new handler' do
94
+ x = 1
95
+ Chef.event_handler do
96
+ on :converge_start do
97
+ x = 2
98
+ end
99
+ end
100
+ expect(Chef::Config[:event_handlers]).to_not be_empty
101
+ Chef::Config[:event_handlers].first.send(:converge_start)
102
+ expect(x).to eq(2)
103
+ end
104
+
105
+ it 'raise error if unknown event type is passed' do
106
+ expect do
107
+ Chef.event_handler do
108
+ on :yolo do
109
+ end
110
+ end
111
+ end.to raise_error(Chef::Exceptions::InvalidEventType)
112
+ end
113
+ end
91
114
  end
@@ -0,0 +1,108 @@
1
+ #
2
+ # Author:: Kartik Null Cating-Subramanian (<ksubramanian@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'spec_helper'
20
+ require 'chef/chef_fs/path_utils'
21
+
22
+ describe Chef::ChefFS::PathUtils do
23
+ context 'invoking join' do
24
+ it 'joins well-behaved distinct path elements' do
25
+ expect(Chef::ChefFS::PathUtils.join('a', 'b', 'c')).to eq('a/b/c')
26
+ end
27
+
28
+ it 'strips extraneous slashes in the middle of paths' do
29
+ expect(Chef::ChefFS::PathUtils.join('a/', '/b', '/c/')).to eq('a/b/c')
30
+ expect(Chef::ChefFS::PathUtils.join('a/', '/b', '///c/')).to eq('a/b/c')
31
+ end
32
+
33
+ it 'preserves the whether the first element was absolute or not' do
34
+ expect(Chef::ChefFS::PathUtils.join('/a/', '/b', 'c/')).to eq('/a/b/c')
35
+ expect(Chef::ChefFS::PathUtils.join('///a/', '/b', 'c/')).to eq('/a/b/c')
36
+ end
37
+ end
38
+
39
+ context 'invoking is_absolute?' do
40
+ it 'confirms that paths starting with / are absolute' do
41
+ expect(Chef::ChefFS::PathUtils.is_absolute?('/foo/bar/baz')).to be true
42
+ expect(Chef::ChefFS::PathUtils.is_absolute?('/foo')).to be true
43
+ end
44
+
45
+ it 'confirms that paths starting with // are absolute even though that looks like some windows network path' do
46
+ expect(Chef::ChefFS::PathUtils.is_absolute?('//foo/bar/baz')).to be true
47
+ end
48
+
49
+ it 'confirms that root is indeed absolute' do
50
+ expect(Chef::ChefFS::PathUtils.is_absolute?('/')).to be true
51
+ end
52
+
53
+ it 'confirms that paths starting without / are relative' do
54
+ expect(Chef::ChefFS::PathUtils.is_absolute?('foo/bar/baz')).to be false
55
+ expect(Chef::ChefFS::PathUtils.is_absolute?('a')).to be false
56
+ end
57
+
58
+ it 'returns false for an empty path.' do
59
+ expect(Chef::ChefFS::PathUtils.is_absolute?('')).to be false
60
+ end
61
+ end
62
+
63
+ context 'invoking realest_path' do
64
+ let(:good_path) { File.dirname(__FILE__) }
65
+ let(:parent_path) { File.dirname(good_path) }
66
+
67
+ it 'handles paths with no wildcards or globs' do
68
+ expect(Chef::ChefFS::PathUtils.realest_path(good_path)).to eq(File.expand_path(good_path))
69
+ end
70
+
71
+ it 'handles paths with .. and .' do
72
+ expect(Chef::ChefFS::PathUtils.realest_path(good_path+'/../.')).to eq(File.expand_path(parent_path))
73
+ end
74
+
75
+ it 'handles paths with *' do
76
+ expect(Chef::ChefFS::PathUtils.realest_path(good_path + '/*/foo')).to eq(File.expand_path(good_path + '/*/foo'))
77
+ end
78
+
79
+ it 'handles directories that do not exist' do
80
+ expect(Chef::ChefFS::PathUtils.realest_path(good_path + '/something/or/other')).to eq(File.expand_path(good_path + '/something/or/other'))
81
+ end
82
+
83
+ it 'handles root correctly' do
84
+ if Chef::Platform.windows?
85
+ expect(Chef::ChefFS::PathUtils.realest_path('C:/')).to eq('C:/')
86
+ else
87
+ expect(Chef::ChefFS::PathUtils.realest_path('/')).to eq('/')
88
+ end
89
+ end
90
+ end
91
+
92
+ context 'invoking descendant_path' do
93
+ it 'handles paths with various casing on windows' do
94
+ allow(Chef::ChefFS).to receive(:windows?) { true }
95
+ expect(Chef::ChefFS::PathUtils.descendant_path('C:/ab/b/c', 'C:/AB/B')).to eq('c')
96
+ expect(Chef::ChefFS::PathUtils.descendant_path('C:/ab/b/c', 'c:/ab/B')).to eq('c')
97
+ end
98
+
99
+ it 'returns nil if the path does not have the given ancestor' do
100
+ expect(Chef::ChefFS::PathUtils.descendant_path('/D/E/F', '/A/B/C')).to be_nil
101
+ expect(Chef::ChefFS::PathUtils.descendant_path('/A/B/D', '/A/B/C')).to be_nil
102
+ end
103
+
104
+ it 'returns blank if the ancestor equals the path' do
105
+ expect(Chef::ChefFS::PathUtils.descendant_path('/A/B/D', '/A/B/D')).to eq('')
106
+ end
107
+ end
108
+ end