chef 12.15.19 → 12.16.42

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -1
  3. data/VERSION +1 -1
  4. data/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml +3 -1
  5. data/acceptance/Gemfile.lock +14 -14
  6. data/acceptance/data-collector/test/integration/default/serverspec/default_spec.rb +3 -11
  7. data/distro/common/html/knife_bootstrap.html +1 -1
  8. data/distro/common/man/man1/README.md +2 -2
  9. data/distro/common/man/man1/knife-client.1 +1 -1
  10. data/lib/chef/application.rb +7 -15
  11. data/lib/chef/application/client.rb +2 -2
  12. data/lib/chef/application/solo.rb +1 -1
  13. data/lib/chef/chef_class.rb +1 -0
  14. data/lib/chef/chef_fs/file_system/chef_server/cookbook_file.rb +3 -7
  15. data/lib/chef/chef_fs/file_system/chef_server/versioned_cookbook_dir.rb +1 -1
  16. data/lib/chef/data_collector.rb +83 -9
  17. data/lib/chef/data_collector/messages.rb +2 -1
  18. data/lib/chef/dsl/core.rb +1 -1
  19. data/lib/chef/dsl/declare_resource.rb +10 -4
  20. data/lib/chef/dsl/method_missing.rb +1 -1
  21. data/lib/chef/dsl/recipe.rb +1 -1
  22. data/lib/chef/dsl/universal.rb +1 -1
  23. data/lib/chef/event_dispatch/base.rb +3 -0
  24. data/lib/chef/http.rb +3 -4
  25. data/lib/chef/knife.rb +20 -2
  26. data/lib/chef/knife/core/generic_presenter.rb +18 -4
  27. data/lib/chef/knife/node_show.rb +0 -5
  28. data/lib/chef/knife/osc_user_show.rb +0 -1
  29. data/lib/chef/knife/ssl_fetch.rb +9 -5
  30. data/lib/chef/mixin/powershell_out.rb +1 -1
  31. data/lib/chef/mixin/shell_out.rb +1 -1
  32. data/lib/chef/node.rb +1 -5
  33. data/lib/chef/node/attribute.rb +70 -98
  34. data/lib/chef/node/attribute_collections.rb +28 -19
  35. data/lib/chef/node/common_api.rb +0 -6
  36. data/lib/chef/node/immutable_collections.rb +16 -79
  37. data/lib/chef/node/mixin/deep_merge_cache.rb +61 -0
  38. data/lib/chef/node/mixin/immutablize_array.rb +67 -0
  39. data/lib/chef/node/mixin/immutablize_hash.rb +54 -0
  40. data/lib/chef/node/mixin/state_tracking.rb +93 -0
  41. data/lib/chef/property.rb +4 -4
  42. data/lib/chef/provider/cron.rb +1 -1
  43. data/lib/chef/provider/group/suse.rb +23 -4
  44. data/lib/chef/provider/package.rb +43 -5
  45. data/lib/chef/provider/package/apt.rb +20 -0
  46. data/lib/chef/provider/package/windows/exe.rb +4 -3
  47. data/lib/chef/provider/package/windows/msi.rb +4 -3
  48. data/lib/chef/provider/package/yum.rb +20 -0
  49. data/lib/chef/provider/package/zypper.rb +20 -0
  50. data/lib/chef/provider/ruby_block.rb +1 -1
  51. data/lib/chef/provider/service/upstart.rb +25 -9
  52. data/lib/chef/provider/user.rb +4 -6
  53. data/lib/chef/provider/user/dscl.rb +8 -3
  54. data/lib/chef/provider/user/solaris.rb +5 -12
  55. data/lib/chef/resource.rb +19 -0
  56. data/lib/chef/resource/file.rb +1 -1
  57. data/lib/chef/resource/package.rb +1 -1
  58. data/lib/chef/resource/scm.rb +1 -7
  59. data/lib/chef/resource/yum_repository.rb +1 -1
  60. data/lib/chef/rest.rb +1 -0
  61. data/lib/chef/run_context.rb +12 -0
  62. data/lib/chef/version.rb +1 -1
  63. data/spec/data/trusted_certs/example_no_cn.crt +36 -0
  64. data/spec/functional/resource/group_spec.rb +1 -0
  65. data/spec/functional/resource/user/useradd_spec.rb +4 -2
  66. data/spec/integration/knife/data_bag_create_spec.rb +0 -3
  67. data/spec/integration/knife/environment_show_spec.rb +24 -4
  68. data/spec/integration/knife/node_environment_set_spec.rb +4 -1
  69. data/spec/integration/recipes/accumulator_spec.rb +232 -0
  70. data/spec/integration/recipes/resource_action_spec.rb +1 -1
  71. data/spec/spec_helper.rb +2 -2
  72. data/spec/support/shared/context/client.rb +12 -3
  73. data/spec/support/shared/integration/app_server_support.rb +1 -1
  74. data/spec/support/shared/integration/knife_support.rb +4 -1
  75. data/spec/unit/data_collector/messages_spec.rb +2 -0
  76. data/spec/unit/data_collector_spec.rb +158 -21
  77. data/spec/unit/http_spec.rb +1 -1
  78. data/spec/unit/knife/core/gem_glob_loader_spec.rb +1 -1
  79. data/spec/unit/knife/core/ui_spec.rb +10 -0
  80. data/spec/unit/knife/ssl_fetch_spec.rb +38 -0
  81. data/spec/unit/knife_spec.rb +31 -0
  82. data/spec/unit/mixin/powershell_out_spec.rb +25 -1
  83. data/spec/unit/node/attribute_spec.rb +46 -1
  84. data/spec/unit/node/vivid_mash_spec.rb +27 -89
  85. data/spec/unit/node_spec.rb +134 -3
  86. data/spec/unit/provider/deploy_spec.rb +1 -1
  87. data/spec/unit/provider/group/suse_spec.rb +90 -0
  88. data/spec/unit/provider/package/apt_spec.rb +22 -0
  89. data/spec/unit/provider/package/windows/msi_spec.rb +13 -4
  90. data/spec/unit/provider/package/windows_spec.rb +3 -3
  91. data/spec/unit/provider/package/yum_spec.rb +18 -0
  92. data/spec/unit/provider/package/zypper_spec.rb +64 -0
  93. data/spec/unit/provider/package_spec.rb +58 -0
  94. data/spec/unit/provider/remote_file/content_spec.rb +1 -1
  95. data/spec/unit/provider/service/upstart_service_spec.rb +13 -6
  96. data/spec/unit/provider/user/solaris_spec.rb +36 -9
  97. data/spec/unit/provider/user_spec.rb +6 -0
  98. data/spec/unit/resource/apt_repository_spec.rb +1 -1
  99. metadata +12 -5
@@ -74,7 +74,7 @@ describe Chef::HTTP do
74
74
  expect(http.create_url("///api/endpoint?url=http://foo.bar")).to eql(URI.parse("http://www.getchef.com/organization/org/api/endpoint?url=http://foo.bar"))
75
75
  end
76
76
 
77
- # As per: https://github.com/opscode/chef/issues/2500
77
+ # As per: https://github.com/chef/chef/issues/2500
78
78
  it "should treat scheme part of the URI in a case-insensitive manner" do
79
79
  http = Chef::HTTP.allocate # Calling Chef::HTTP::new sets @url, don't want that.
80
80
  expect { http.create_url("HTTP://www1.chef.io/") }.not_to raise_error
@@ -78,7 +78,7 @@ describe Chef::Knife::SubcommandLoader::GemGlobLoader do
78
78
  expect(loader.site_subcommands).to include(expected_command)
79
79
  end
80
80
 
81
- # https://github.com/opscode/chef-dk/issues/227
81
+ # https://github.com/chef/chef-dk/issues/227
82
82
  #
83
83
  # `knife` in ChefDK isn't from a gem install, it's directly run from a clone
84
84
  # of the source, but there can be one or more versions of chef also installed
@@ -28,6 +28,7 @@ describe Chef::Knife::UI do
28
28
  :verbosity => 0,
29
29
  :yes => nil,
30
30
  :format => "summary",
31
+ :field_separator => ".",
31
32
  }
32
33
  @ui = Chef::Knife::UI.new(@out, @err, @in, @config)
33
34
  Chef::Config[:treat_deprecation_warnings_as_errors] = false
@@ -410,6 +411,15 @@ EOM
410
411
  @ui.config[:attribute] = non_existing_path
411
412
  expect(@ui.format_for_display(input)).to eq({ "sample-data-bag-item" => { non_existing_path => nil } })
412
413
  end
414
+
415
+ describe "when --field-separator is passed" do
416
+ it "honors that separator" do
417
+ input = { "keys" => { "with spaces" => { "open" => { "doors" => { "with many.dots" => "when asked" } } } } }
418
+ @ui.config[:field_separator] = ";"
419
+ @ui.config[:attribute] = "keys;with spaces;open;doors;with many.dots"
420
+ expect(@ui.format_for_display(input)).to eq({ nil => { "keys;with spaces;open;doors;with many.dots" => "when asked" } })
421
+ end
422
+ end
413
423
  end
414
424
 
415
425
  describe "with --run-list passed" do
@@ -108,6 +108,24 @@ E
108
108
 
109
109
  end
110
110
 
111
+ describe "#cn_of" do
112
+ let(:certificate) { double("Certificate", subject: subject) }
113
+
114
+ describe "when the certificate has a common name" do
115
+ let(:subject) { [["CN", "common name"]] }
116
+ it "returns the common name" do
117
+ expect(ssl_fetch.cn_of(certificate)).to eq("common name")
118
+ end
119
+ end
120
+
121
+ describe "when the certificate does not have a common name" do
122
+ let(:subject) { [] }
123
+ it "returns nil" do
124
+ expect(ssl_fetch.cn_of(certificate)).to eq(nil)
125
+ end
126
+ end
127
+ end
128
+
111
129
  describe "fetching the remote cert chain" do
112
130
 
113
131
  let(:name_args) { %w{https://foo.example.com:8443} }
@@ -180,5 +198,25 @@ ERROR_TEXT
180
198
 
181
199
  end
182
200
 
201
+ describe "when the certificate does not have a CN" do
202
+ let(:self_signed_crt_path) { File.join(CHEF_SPEC_DATA, "trusted_certs", "example_no_cn.crt") }
203
+ let(:self_signed_crt) { OpenSSL::X509::Certificate.new(File.read(self_signed_crt_path)) }
204
+
205
+ before do
206
+ expect(ssl_fetch).to receive(:proxified_socket).with("foo.example.com", 8443).and_return(tcp_socket)
207
+ expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_fetch.noverify_peer_ssl_context).and_return(ssl_socket)
208
+ expect(ssl_socket).to receive(:connect)
209
+ expect(ssl_socket).to receive(:peer_cert_chain).and_return([self_signed_crt])
210
+ expect(Time).to receive(:new).and_return(1)
211
+ end
212
+
213
+ it "fetches the certificate and writes it to a file in the trusted_certs_dir" do
214
+ run
215
+ stored_cert_path = File.join(trusted_certs_dir, "foo.example.com_1.crt")
216
+ expect(File).to exist(stored_cert_path)
217
+ expect(File.read(stored_cert_path)).to eq(File.read(self_signed_crt_path))
218
+ end
219
+ end
220
+
183
221
  end
184
222
  end
@@ -349,6 +349,37 @@ describe Chef::Knife do
349
349
  expect { knife.run_with_pretty_exceptions }.to raise_error(Exception)
350
350
  end
351
351
  end
352
+
353
+ describe "setting arbitrary configuration with --config-option" do
354
+
355
+ let(:stdout) { StringIO.new }
356
+
357
+ let(:stderr) { StringIO.new }
358
+
359
+ let(:stdin) { StringIO.new }
360
+
361
+ let(:ui) { Chef::Knife::UI.new(stdout, stderr, stdin, disable_editing: true) }
362
+
363
+ let(:subcommand) do
364
+ KnifeSpecs::TestYourself.options = Chef::Application::Knife.options.merge(KnifeSpecs::TestYourself.options)
365
+ KnifeSpecs::TestYourself.new(%w{--config-option badly_formatted_arg}).tap do |cmd|
366
+ cmd.ui = ui
367
+ end
368
+ end
369
+
370
+ it "sets arbitrary configuration via --config-option" do
371
+ Chef::Knife.run(%w{test yourself --config-option arbitrary_config_thing=hello}, Chef::Application::Knife.options)
372
+ expect(Chef::Config[:arbitrary_config_thing]).to eq("hello")
373
+ end
374
+
375
+ it "handles errors in arbitrary configuration" do
376
+ expect(subcommand).to receive(:exit).with(1)
377
+ subcommand.configure_chef
378
+ expect(stderr.string).to include("ERROR: Unparsable config option \"badly_formatted_arg\"")
379
+ expect(stdout.string).to include(subcommand.opt_parser.to_s)
380
+ end
381
+ end
382
+
352
383
  end
353
384
 
354
385
  describe "when first created" do
@@ -18,7 +18,7 @@
18
18
  require "spec_helper"
19
19
  require "chef/mixin/powershell_out"
20
20
 
21
- describe Chef::Mixin::PowershellOut do
21
+ describe Chef::Mixin::PowershellOut, :windows_only do
22
22
  let(:shell_out_class) { Class.new { include Chef::Mixin::PowershellOut } }
23
23
  subject(:object) { shell_out_class.new }
24
24
  let(:architecture) { "something" }
@@ -44,6 +44,18 @@ describe Chef::Mixin::PowershellOut do
44
44
  ).and_return(ret)
45
45
  expect(object.powershell_out("Get-Process", timeout: 600)).to eql(ret)
46
46
  end
47
+
48
+ context "when double quote is passed in the powershell command" do
49
+ it "passes if double quote is appended with single escape" do
50
+ result = object.powershell_out("Write-Verbose \"Some String\" -Verbose")
51
+ expect(result.stderr).to be == ""
52
+ expect(result.stdout).to be == "VERBOSE: Some String\n"
53
+ end
54
+
55
+ it "suppresses error if double quote is passed with double escape characters" do
56
+ expect { object.powershell_out("Write-Verbose \\\"Some String\\\" -Verbose") }.not_to raise_error
57
+ end
58
+ end
47
59
  end
48
60
 
49
61
  describe "#powershell_out!" do
@@ -66,5 +78,17 @@ describe Chef::Mixin::PowershellOut do
66
78
  expect(mixlib_shellout).to receive(:error!)
67
79
  expect(object.powershell_out!("Get-Process", timeout: 600)).to eql(mixlib_shellout)
68
80
  end
81
+
82
+ context "when double quote is passed in the powershell command" do
83
+ it "passes if double quote is appended with single escape" do
84
+ result = object.powershell_out!("Write-Verbose \"Some String\" -Verbose")
85
+ expect(result.stderr).to be == ""
86
+ expect(result.stdout).to be == "VERBOSE: Some String\n"
87
+ end
88
+
89
+ it "raises error if double quote is passed with double escape characters" do
90
+ expect { object.powershell_out!("Write-Verbose \\\"Some String\\\" -Verbose") }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
91
+ end
92
+ end
69
93
  end
70
94
  end
@@ -21,7 +21,11 @@ require "spec_helper"
21
21
  require "chef/node/attribute"
22
22
 
23
23
  describe Chef::Node::Attribute do
24
+ let(:events) { instance_double(Chef::EventDispatch::Dispatcher) }
25
+ let(:run_context) { instance_double(Chef::RunContext, :events => events) }
26
+ let(:node) { instance_double(Chef::Node, :run_context => run_context) }
24
27
  before(:each) do
28
+ allow(events).to receive(:attribute_changed)
25
29
  @attribute_hash =
26
30
  { "dmi" => {},
27
31
  "command" => { "ps" => "ps -ef" },
@@ -166,7 +170,7 @@ describe Chef::Node::Attribute do
166
170
  },
167
171
  }
168
172
  @automatic_hash = { "week" => "friday" }
169
- @attributes = Chef::Node::Attribute.new(@attribute_hash, @default_hash, @override_hash, @automatic_hash)
173
+ @attributes = Chef::Node::Attribute.new(@attribute_hash, @default_hash, @override_hash, @automatic_hash, node)
170
174
  end
171
175
 
172
176
  describe "initialize" do
@@ -1196,4 +1200,45 @@ describe Chef::Node::Attribute do
1196
1200
  expect(@attributes["foo"]["baz"]["bar"]).to be true
1197
1201
  end
1198
1202
  end
1203
+
1204
+ describe "node state" do
1205
+ it "sets __root__ correctly" do
1206
+ @attributes.default["foo"]["bar"]["baz"] = "quux"
1207
+ expect(@attributes["foo"].__root__).to eql(@attributes)
1208
+ expect(@attributes["foo"]["bar"].__root__).to eql(@attributes)
1209
+ expect(@attributes.default["foo"].__root__).to eql(@attributes)
1210
+ expect(@attributes.default["foo"]["bar"].__root__).to eql(@attributes)
1211
+ end
1212
+
1213
+ it "sets __node__ correctly" do
1214
+ @attributes.default["foo"]["bar"]["baz"] = "quux"
1215
+ expect(@attributes["foo"].__node__).to eql(node)
1216
+ expect(@attributes["foo"]["bar"].__node__).to eql(node)
1217
+ expect(@attributes.default["foo"].__node__).to eql(node)
1218
+ expect(@attributes.default["foo"]["bar"].__node__).to eql(node)
1219
+ end
1220
+
1221
+ it "sets __path__ correctly" do
1222
+ @attributes.default["foo"]["bar"]["baz"] = "quux"
1223
+ expect(@attributes["foo"].__path__).to eql(["foo"])
1224
+ expect(@attributes["foo"]["bar"].__path__).to eql(%w{foo bar})
1225
+ expect(@attributes.default["foo"].__path__).to eql(["foo"])
1226
+ expect(@attributes.default["foo"]["bar"].__path__).to eql(%w{foo bar})
1227
+ end
1228
+
1229
+ it "sets __precedence__ correctly" do
1230
+ @attributes.default["foo"]["bar"]["baz"] = "quux"
1231
+ expect(@attributes["foo"].__precedence__).to eql(:merged)
1232
+ expect(@attributes["foo"]["bar"].__precedence__).to eql(:merged)
1233
+ expect(@attributes.default["foo"].__precedence__).to eql(:default)
1234
+ expect(@attributes.default["foo"]["bar"].__precedence__).to eql(:default)
1235
+ end
1236
+
1237
+ it "notifies on attribute changes" do
1238
+ expect(events).to receive(:attribute_changed).with(:default, ["foo"], {})
1239
+ expect(events).to receive(:attribute_changed).with(:default, %w{foo bar}, {})
1240
+ expect(events).to receive(:attribute_changed).with(:default, %w{foo bar baz}, "quux")
1241
+ @attributes.default["foo"]["bar"]["baz"] = "quux"
1242
+ end
1243
+ end
1199
1244
  end
@@ -19,36 +19,46 @@ require "spec_helper"
19
19
  require "chef/node/attribute_collections"
20
20
 
21
21
  describe Chef::Node::VividMash do
22
- class Root
23
- attr_accessor :top_level_breadcrumb
24
- end
25
-
26
- let(:root) { Root.new }
22
+ let(:root) { instance_double(Chef::Node::Attribute) }
27
23
 
28
24
  let(:vivid) do
29
- expect(root).to receive(:reset_cache).at_least(:once).with(nil)
30
- Chef::Node::VividMash.new(root,
31
- { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }
25
+ Chef::Node::VividMash.new(
26
+ { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil },
27
+ root
32
28
  )
33
29
  end
34
30
 
35
- def with_breadcrumb(key)
36
- expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original
37
- expect(root).to receive(:top_level_breadcrumb=).with(key).at_least(:once).and_call_original
31
+ context "without a root node" do
32
+ let(:vivid) do
33
+ Chef::Node::VividMash.new(
34
+ { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }
35
+ )
36
+ end
37
+
38
+ it "sets the root to the root object" do
39
+ expect(vivid["one"]["two"].__root__).to eql(vivid)
40
+ end
41
+
42
+ it "does not send reset cache" do
43
+ # if we setup the expectation here then the object winds up responding to :reset_cache and then it fails...
44
+ # expect(vivid).not_to receive(:reset_cache)
45
+ # but even so we expect to blow up here with NoMethodError if we screw up and send :reset_cache to a root VividMash
46
+ vivid["one"]["foo"] = "bar"
47
+ end
38
48
  end
39
49
 
40
50
  context "#[]=" do
41
51
  it "deep converts values through arrays" do
42
- allow(root).to receive(:reset_cache)
43
- vivid[:foo] = [ { :bar => true } ]
52
+ expect(root).to receive(:reset_cache).with("foo")
53
+ vivid["foo"] = [ { :bar => true } ]
44
54
  expect(vivid["foo"].class).to eql(Chef::Node::AttrArray)
45
55
  expect(vivid["foo"][0].class).to eql(Chef::Node::VividMash)
46
56
  expect(vivid["foo"][0]["bar"]).to be true
47
57
  end
48
58
 
49
59
  it "deep converts values through nested arrays" do
50
- allow(root).to receive(:reset_cache)
51
- vivid[:foo] = [ [ { :bar => true } ] ]
60
+ expect(root).to receive(:reset_cache).with("foo")
61
+ vivid["foo"] = [ [ { :bar => true } ] ]
52
62
  expect(vivid["foo"].class).to eql(Chef::Node::AttrArray)
53
63
  expect(vivid["foo"][0].class).to eql(Chef::Node::AttrArray)
54
64
  expect(vivid["foo"][0][0].class).to eql(Chef::Node::VividMash)
@@ -56,8 +66,8 @@ describe Chef::Node::VividMash do
56
66
  end
57
67
 
58
68
  it "deep converts values through hashes" do
59
- allow(root).to receive(:reset_cache)
60
- vivid[:foo] = { baz: { :bar => true } }
69
+ expect(root).to receive(:reset_cache).with("foo")
70
+ vivid["foo"] = { baz: { :bar => true } }
61
71
  expect(vivid["foo"]).to be_an_instance_of(Chef::Node::VividMash)
62
72
  expect(vivid["foo"]["baz"]).to be_an_instance_of(Chef::Node::VividMash)
63
73
  expect(vivid["foo"]["baz"]["bar"]).to be true
@@ -66,182 +76,144 @@ describe Chef::Node::VividMash do
66
76
 
67
77
  context "#read" do
68
78
  before do
69
- # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
70
- vivid
71
79
  expect(root).not_to receive(:reset_cache)
72
80
  end
73
81
 
74
82
  it "reads hashes deeply" do
75
- with_breadcrumb("one")
76
83
  expect(vivid.read("one", "two", "three")).to eql("four")
77
84
  end
78
85
 
79
86
  it "does not trainwreck when hitting hash keys that do not exist" do
80
- with_breadcrumb("one")
81
87
  expect(vivid.read("one", "five", "six")).to eql(nil)
82
88
  end
83
89
 
84
90
  it "does not trainwreck when hitting an array with an out of bounds index" do
85
- with_breadcrumb("array")
86
91
  expect(vivid.read("array", 5, "one")).to eql(nil)
87
92
  end
88
93
 
89
94
  it "does not trainwreck when hitting an array with a string key" do
90
- with_breadcrumb("array")
91
95
  expect(vivid.read("array", "one", "two")).to eql(nil)
92
96
  end
93
97
 
94
98
  it "does not trainwreck when traversing a nil" do
95
- with_breadcrumb("nil")
96
99
  expect(vivid.read("nil", "one", "two")).to eql(nil)
97
100
  end
98
101
  end
99
102
 
100
103
  context "#exist?" do
101
104
  before do
102
- # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
103
- vivid
104
105
  expect(root).not_to receive(:reset_cache)
105
106
  end
106
107
 
107
108
  it "true if there's a hash key there" do
108
- with_breadcrumb("one")
109
109
  expect(vivid.exist?("one", "two", "three")).to be true
110
110
  end
111
111
 
112
112
  it "true for intermediate hashes" do
113
- with_breadcrumb("one")
114
113
  expect(vivid.exist?("one")).to be true
115
114
  end
116
115
 
117
116
  it "true for arrays that exist" do
118
- with_breadcrumb("array")
119
117
  expect(vivid.exist?("array", 1)).to be true
120
118
  end
121
119
 
122
120
  it "true when the value of the key is nil" do
123
- with_breadcrumb("nil")
124
121
  expect(vivid.exist?("nil")).to be true
125
122
  end
126
123
 
127
124
  it "false when attributes don't exist" do
128
- with_breadcrumb("one")
129
125
  expect(vivid.exist?("one", "five", "six")).to be false
130
126
  end
131
127
 
132
128
  it "false when traversing a non-container" do
133
- with_breadcrumb("one")
134
129
  expect(vivid.exist?("one", "two", "three", "four")).to be false
135
130
  end
136
131
 
137
132
  it "false when an array index does not exist" do
138
- with_breadcrumb("array")
139
133
  expect(vivid.exist?("array", 3)).to be false
140
134
  end
141
135
 
142
136
  it "false when traversing a nil" do
143
- with_breadcrumb("nil")
144
137
  expect(vivid.exist?("nil", "foo", "bar")).to be false
145
138
  end
146
139
  end
147
140
 
148
141
  context "#read!" do
149
142
  before do
150
- # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
151
- vivid
152
143
  expect(root).not_to receive(:reset_cache)
153
144
  end
154
145
 
155
146
  it "reads hashes deeply" do
156
- with_breadcrumb("one")
157
147
  expect(vivid.read!("one", "two", "three")).to eql("four")
158
148
  end
159
149
 
160
150
  it "reads arrays deeply" do
161
- with_breadcrumb("array")
162
151
  expect(vivid.read!("array", 1)).to eql(1)
163
152
  end
164
153
 
165
154
  it "throws an exception when attributes do not exist" do
166
- with_breadcrumb("one")
167
155
  expect { vivid.read!("one", "five", "six") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
168
156
  end
169
157
 
170
158
  it "throws an exception when traversing a non-container" do
171
- with_breadcrumb("one")
172
159
  expect { vivid.read!("one", "two", "three", "four") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
173
160
  end
174
161
 
175
162
  it "throws an exception when an array element does not exist" do
176
- with_breadcrumb("array")
177
163
  expect { vivid.read!("array", 3) }.to raise_error(Chef::Exceptions::NoSuchAttribute)
178
164
  end
179
165
  end
180
166
 
181
167
  context "#write" do
182
- before do
183
- vivid
184
- expect(root).not_to receive(:reset_cache).with(nil)
185
- end
186
-
187
168
  it "should write into hashes" do
188
- with_breadcrumb("one")
189
169
  expect(root).to receive(:reset_cache).at_least(:once).with("one")
190
170
  vivid.write("one", "five", "six")
191
171
  expect(vivid["one"]["five"]).to eql("six")
192
172
  end
193
173
 
194
174
  it "should deeply autovivify" do
195
- with_breadcrumb("one")
196
175
  expect(root).to receive(:reset_cache).at_least(:once).with("one")
197
176
  vivid.write("one", "five", "six", "seven", "eight", "nine", "ten")
198
177
  expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten")
199
178
  end
200
179
 
201
180
  it "should raise an exception if you overwrite an array with a hash" do
202
- with_breadcrumb("array")
203
181
  expect(root).to receive(:reset_cache).at_least(:once).with("array")
204
182
  vivid.write("array", "five", "six")
205
183
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => "six" }, "nil" => nil })
206
184
  end
207
185
 
208
186
  it "should raise an exception if you traverse through an array with a hash" do
209
- with_breadcrumb("array")
210
187
  expect(root).to receive(:reset_cache).at_least(:once).with("array")
211
188
  vivid.write("array", "five", "six", "seven")
212
189
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => { "six" => "seven" } }, "nil" => nil })
213
190
  end
214
191
 
215
192
  it "should raise an exception if you overwrite a string with a hash" do
216
- with_breadcrumb("one")
217
193
  expect(root).to receive(:reset_cache).at_least(:once).with("one")
218
194
  vivid.write("one", "two", "three", "four", "five")
219
195
  expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => "five" } } }, "array" => [ 0, 1, 2 ], "nil" => nil })
220
196
  end
221
197
 
222
198
  it "should raise an exception if you traverse through a string with a hash" do
223
- with_breadcrumb("one")
224
199
  expect(root).to receive(:reset_cache).at_least(:once).with("one")
225
200
  vivid.write("one", "two", "three", "four", "five", "six")
226
201
  expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => { "five" => "six" } } } }, "array" => [ 0, 1, 2 ], "nil" => nil })
227
202
  end
228
203
 
229
204
  it "should raise an exception if you overwrite a nil with a hash" do
230
- with_breadcrumb("nil")
231
205
  expect(root).to receive(:reset_cache).at_least(:once).with("nil")
232
206
  vivid.write("nil", "one", "two")
233
207
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => "two" } })
234
208
  end
235
209
 
236
210
  it "should raise an exception if you traverse through a nil with a hash" do
237
- with_breadcrumb("nil")
238
211
  expect(root).to receive(:reset_cache).at_least(:once).with("nil")
239
212
  vivid.write("nil", "one", "two", "three")
240
213
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => { "two" => "three" } } })
241
214
  end
242
215
 
243
216
  it "writes with a block" do
244
- with_breadcrumb("one")
245
217
  expect(root).to receive(:reset_cache).at_least(:once).with("one")
246
218
  vivid.write("one", "five") { "six" }
247
219
  expect(vivid["one"]["five"]).to eql("six")
@@ -249,69 +221,55 @@ describe Chef::Node::VividMash do
249
221
  end
250
222
 
251
223
  context "#write!" do
252
- before do
253
- vivid
254
- expect(root).not_to receive(:reset_cache).with(nil)
255
- end
256
-
257
224
  it "should write into hashes" do
258
- with_breadcrumb("one")
259
225
  expect(root).to receive(:reset_cache).at_least(:once).with("one")
260
226
  vivid.write!("one", "five", "six")
261
227
  expect(vivid["one"]["five"]).to eql("six")
262
228
  end
263
229
 
264
230
  it "should deeply autovivify" do
265
- with_breadcrumb("one")
266
231
  expect(root).to receive(:reset_cache).at_least(:once).with("one")
267
232
  vivid.write!("one", "five", "six", "seven", "eight", "nine", "ten")
268
233
  expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten")
269
234
  end
270
235
 
271
236
  it "should raise an exception if you overwrite an array with a hash" do
272
- with_breadcrumb("array")
273
237
  expect(root).not_to receive(:reset_cache)
274
238
  expect { vivid.write!("array", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
275
239
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
276
240
  end
277
241
 
278
242
  it "should raise an exception if you traverse through an array with a hash" do
279
- with_breadcrumb("array")
280
243
  expect(root).not_to receive(:reset_cache)
281
244
  expect { vivid.write!("array", "five", "six", "seven") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
282
245
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
283
246
  end
284
247
 
285
248
  it "should raise an exception if you overwrite a string with a hash" do
286
- with_breadcrumb("one")
287
249
  expect(root).not_to receive(:reset_cache)
288
250
  expect { vivid.write!("one", "two", "three", "four", "five") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
289
251
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
290
252
  end
291
253
 
292
254
  it "should raise an exception if you traverse through a string with a hash" do
293
- with_breadcrumb("one")
294
255
  expect(root).not_to receive(:reset_cache)
295
256
  expect { vivid.write!("one", "two", "three", "four", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
296
257
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
297
258
  end
298
259
 
299
260
  it "should raise an exception if you overwrite a nil with a hash" do
300
- with_breadcrumb("nil")
301
261
  expect(root).not_to receive(:reset_cache)
302
262
  expect { vivid.write!("nil", "one", "two") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
303
263
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
304
264
  end
305
265
 
306
266
  it "should raise an exception if you traverse through a nil with a hash" do
307
- with_breadcrumb("nil")
308
267
  expect(root).not_to receive(:reset_cache)
309
268
  expect { vivid.write!("nil", "one", "two", "three") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
310
269
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
311
270
  end
312
271
 
313
272
  it "writes with a block" do
314
- with_breadcrumb("one")
315
273
  expect(root).to receive(:reset_cache).at_least(:once).with("one")
316
274
  vivid.write!("one", "five") { "six" }
317
275
  expect(vivid["one"]["five"]).to eql("six")
@@ -319,41 +277,31 @@ describe Chef::Node::VividMash do
319
277
  end
320
278
 
321
279
  context "#unlink" do
322
- before do
323
- vivid
324
- expect(root).not_to receive(:reset_cache).with(nil)
325
- end
326
-
327
280
  it "should return nil if the keys don't already exist" do
328
- expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original
329
281
  expect(root).not_to receive(:reset_cache)
330
282
  expect(vivid.unlink("five", "six", "seven", "eight")).to eql(nil)
331
283
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
332
284
  end
333
285
 
334
286
  it "should unlink hashes" do
335
- with_breadcrumb("one")
336
287
  expect(root).to receive(:reset_cache).at_least(:once).with("one")
337
288
  expect( vivid.unlink("one") ).to eql({ "two" => { "three" => "four" } })
338
289
  expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil })
339
290
  end
340
291
 
341
292
  it "should unlink array elements" do
342
- with_breadcrumb("array")
343
293
  expect(root).to receive(:reset_cache).at_least(:once).with("array")
344
294
  expect(vivid.unlink("array", 2)).to eql(2)
345
295
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil })
346
296
  end
347
297
 
348
298
  it "should unlink nil" do
349
- with_breadcrumb("nil")
350
299
  expect(root).to receive(:reset_cache).at_least(:once).with("nil")
351
300
  expect(vivid.unlink("nil")).to eql(nil)
352
301
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] })
353
302
  end
354
303
 
355
304
  it "should traverse a nil and safely do nothing" do
356
- with_breadcrumb("nil")
357
305
  expect(root).not_to receive(:reset_cache)
358
306
  expect(vivid.unlink("nil", "foo")).to eql(nil)
359
307
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
@@ -361,41 +309,31 @@ describe Chef::Node::VividMash do
361
309
  end
362
310
 
363
311
  context "#unlink!" do
364
- before do
365
- vivid
366
- expect(root).not_to receive(:reset_cache).with(nil)
367
- end
368
-
369
312
  it "should raise an exception if the keys don't already exist" do
370
- expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original
371
313
  expect(root).not_to receive(:reset_cache)
372
314
  expect { vivid.unlink!("five", "six", "seven", "eight") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
373
315
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
374
316
  end
375
317
 
376
318
  it "should unlink! hashes" do
377
- with_breadcrumb("one")
378
319
  expect(root).to receive(:reset_cache).at_least(:once).with("one")
379
320
  expect( vivid.unlink!("one") ).to eql({ "two" => { "three" => "four" } })
380
321
  expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil })
381
322
  end
382
323
 
383
324
  it "should unlink! array elements" do
384
- with_breadcrumb("array")
385
325
  expect(root).to receive(:reset_cache).at_least(:once).with("array")
386
326
  expect(vivid.unlink!("array", 2)).to eql(2)
387
327
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil })
388
328
  end
389
329
 
390
330
  it "should unlink! nil" do
391
- with_breadcrumb("nil")
392
331
  expect(root).to receive(:reset_cache).at_least(:once).with("nil")
393
332
  expect(vivid.unlink!("nil")).to eql(nil)
394
333
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] })
395
334
  end
396
335
 
397
336
  it "should raise an exception if it traverses a nil" do
398
- with_breadcrumb("nil")
399
337
  expect(root).not_to receive(:reset_cache)
400
338
  expect { vivid.unlink!("nil", "foo") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
401
339
  expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })