chef 12.4.3 → 12.5.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
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