diff options
Diffstat (limited to 'spec/unit/node/vivid_mash_spec.rb')
-rw-r--r-- | spec/unit/node/vivid_mash_spec.rb | 255 |
1 files changed, 168 insertions, 87 deletions
diff --git a/spec/unit/node/vivid_mash_spec.rb b/spec/unit/node/vivid_mash_spec.rb index 5319ba4a35..834b9864f8 100644 --- a/spec/unit/node/vivid_mash_spec.rb +++ b/spec/unit/node/vivid_mash_spec.rb @@ -1,5 +1,5 @@ # -# Copyright:: Copyright 2016, Chef Software Inc. +# Copyright:: Copyright (c) Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,202 +19,212 @@ require "spec_helper" require "chef/node/attribute_collections" describe Chef::Node::VividMash do - class Root - attr_accessor :top_level_breadcrumb - end - - let(:root) { Root.new } + let(:root) { instance_double(Chef::Node::Attribute) } let(:vivid) do - expect(root).to receive(:reset_cache).at_least(:once).with(nil) - Chef::Node::VividMash.new(root, - { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil } + Chef::Node::VividMash.new( + { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }, + root ) end - def with_breadcrumb(key) - expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original - expect(root).to receive(:top_level_breadcrumb=).with(key).at_least(:once).and_call_original + context "without a root node" do + let(:vivid) do + Chef::Node::VividMash.new( + { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil } + ) + end + + it "sets the root to the root object" do + expect(vivid["one"]["two"].__root__).to eql(vivid) + end + + it "does not send reset cache" do + # if we setup the expectation here then the object winds up responding to :reset_cache and then it fails... + # expect(vivid).not_to receive(:reset_cache) + # but even so we expect to blow up here with NoMethodError if we screw up and send :reset_cache to a root VividMash + vivid["one"]["foo"] = "bar" + end + end + + context "#[]" do + it "works with array slices" do + expect(vivid["array"][1, 2]).to eql([1, 2]) + end + end + + context "#[]=" do + it "works with array slices" do + vivid["array"][3, 2] = [ 3, 4 ] + expect(vivid["array"]).to eql([0, 1, 2, 3, 4]) + end + + it "deep converts values through arrays" do + expect(root).to receive(:reset_cache).with("foo") + vivid["foo"] = [ { bar: true } ] + expect(vivid["foo"].class).to eql(Chef::Node::AttrArray) + expect(vivid["foo"][0].class).to eql(Chef::Node::VividMash) + expect(vivid["foo"][0]["bar"]).to be true + end + + it "deep converts values through nested arrays" do + expect(root).to receive(:reset_cache).with("foo") + vivid["foo"] = [ [ { bar: true } ] ] + expect(vivid["foo"].class).to eql(Chef::Node::AttrArray) + expect(vivid["foo"][0].class).to eql(Chef::Node::AttrArray) + expect(vivid["foo"][0][0].class).to eql(Chef::Node::VividMash) + expect(vivid["foo"][0][0]["bar"]).to be true + end + + it "deep converts values through hashes" do + expect(root).to receive(:reset_cache).with("foo") + vivid["foo"] = { baz: { bar: true } } + expect(vivid["foo"]).to be_an_instance_of(Chef::Node::VividMash) + expect(vivid["foo"]["baz"]).to be_an_instance_of(Chef::Node::VividMash) + expect(vivid["foo"]["baz"]["bar"]).to be true + end end context "#read" do before do - # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards - vivid expect(root).not_to receive(:reset_cache) end it "reads hashes deeply" do - with_breadcrumb("one") expect(vivid.read("one", "two", "three")).to eql("four") end it "does not trainwreck when hitting hash keys that do not exist" do - with_breadcrumb("one") expect(vivid.read("one", "five", "six")).to eql(nil) end it "does not trainwreck when hitting an array with an out of bounds index" do - with_breadcrumb("array") expect(vivid.read("array", 5, "one")).to eql(nil) end it "does not trainwreck when hitting an array with a string key" do - with_breadcrumb("array") expect(vivid.read("array", "one", "two")).to eql(nil) end it "does not trainwreck when traversing a nil" do - with_breadcrumb("nil") expect(vivid.read("nil", "one", "two")).to eql(nil) end end context "#exist?" do before do - # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards - vivid expect(root).not_to receive(:reset_cache) end it "true if there's a hash key there" do - with_breadcrumb("one") expect(vivid.exist?("one", "two", "three")).to be true end it "true for intermediate hashes" do - with_breadcrumb("one") expect(vivid.exist?("one")).to be true end it "true for arrays that exist" do - with_breadcrumb("array") expect(vivid.exist?("array", 1)).to be true end it "true when the value of the key is nil" do - with_breadcrumb("nil") expect(vivid.exist?("nil")).to be true end it "false when attributes don't exist" do - with_breadcrumb("one") expect(vivid.exist?("one", "five", "six")).to be false end it "false when traversing a non-container" do - with_breadcrumb("one") expect(vivid.exist?("one", "two", "three", "four")).to be false end it "false when an array index does not exist" do - with_breadcrumb("array") expect(vivid.exist?("array", 3)).to be false end it "false when traversing a nil" do - with_breadcrumb("nil") expect(vivid.exist?("nil", "foo", "bar")).to be false end end context "#read!" do before do - # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards - vivid expect(root).not_to receive(:reset_cache) end it "reads hashes deeply" do - with_breadcrumb("one") expect(vivid.read!("one", "two", "three")).to eql("four") end it "reads arrays deeply" do - with_breadcrumb("array") expect(vivid.read!("array", 1)).to eql(1) end it "throws an exception when attributes do not exist" do - with_breadcrumb("one") - expect { vivid.read!("one", "five", "six") }.to raise_error(Chef::Exceptions::NoSuchAttribute) + expect { vivid.read!("one", "five", "six") }.to raise_error(Chef::Exceptions::NoSuchAttribute, "one.five.six") end it "throws an exception when traversing a non-container" do - with_breadcrumb("one") - expect { vivid.read!("one", "two", "three", "four") }.to raise_error(Chef::Exceptions::NoSuchAttribute) + expect { vivid.read!("one", "two", "three", "four") }.to raise_error(Chef::Exceptions::NoSuchAttribute, "one.two.three.four") end it "throws an exception when an array element does not exist" do - with_breadcrumb("array") - expect { vivid.read!("array", 3) }.to raise_error(Chef::Exceptions::NoSuchAttribute) + expect { vivid.read!("array", 3) }.to raise_error(Chef::Exceptions::NoSuchAttribute, "array.3") end end context "#write" do - before do - vivid - expect(root).not_to receive(:reset_cache).with(nil) - end - it "should write into hashes" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write("one", "five", "six") expect(vivid["one"]["five"]).to eql("six") end it "should deeply autovivify" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write("one", "five", "six", "seven", "eight", "nine", "ten") expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten") end it "should raise an exception if you overwrite an array with a hash" do - with_breadcrumb("array") expect(root).to receive(:reset_cache).at_least(:once).with("array") vivid.write("array", "five", "six") expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => "six" }, "nil" => nil }) end it "should raise an exception if you traverse through an array with a hash" do - with_breadcrumb("array") expect(root).to receive(:reset_cache).at_least(:once).with("array") vivid.write("array", "five", "six", "seven") expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => { "six" => "seven" } }, "nil" => nil }) end it "should raise an exception if you overwrite a string with a hash" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write("one", "two", "three", "four", "five") expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => "five" } } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should raise an exception if you traverse through a string with a hash" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write("one", "two", "three", "four", "five", "six") expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => { "five" => "six" } } } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should raise an exception if you overwrite a nil with a hash" do - with_breadcrumb("nil") expect(root).to receive(:reset_cache).at_least(:once).with("nil") vivid.write("nil", "one", "two") expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => "two" } }) end it "should raise an exception if you traverse through a nil with a hash" do - with_breadcrumb("nil") expect(root).to receive(:reset_cache).at_least(:once).with("nil") vivid.write("nil", "one", "two", "three") expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => { "two" => "three" } } }) end it "writes with a block" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write("one", "five") { "six" } expect(vivid["one"]["five"]).to eql("six") @@ -222,69 +232,55 @@ describe Chef::Node::VividMash do end context "#write!" do - before do - vivid - expect(root).not_to receive(:reset_cache).with(nil) - end - it "should write into hashes" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write!("one", "five", "six") expect(vivid["one"]["five"]).to eql("six") end it "should deeply autovivify" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write!("one", "five", "six", "seven", "eight", "nine", "ten") expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten") end it "should raise an exception if you overwrite an array with a hash" do - with_breadcrumb("array") expect(root).not_to receive(:reset_cache) expect { vivid.write!("array", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should raise an exception if you traverse through an array with a hash" do - with_breadcrumb("array") expect(root).not_to receive(:reset_cache) expect { vivid.write!("array", "five", "six", "seven") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should raise an exception if you overwrite a string with a hash" do - with_breadcrumb("one") expect(root).not_to receive(:reset_cache) expect { vivid.write!("one", "two", "three", "four", "five") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should raise an exception if you traverse through a string with a hash" do - with_breadcrumb("one") expect(root).not_to receive(:reset_cache) expect { vivid.write!("one", "two", "three", "four", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should raise an exception if you overwrite a nil with a hash" do - with_breadcrumb("nil") expect(root).not_to receive(:reset_cache) expect { vivid.write!("nil", "one", "two") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should raise an exception if you traverse through a nil with a hash" do - with_breadcrumb("nil") expect(root).not_to receive(:reset_cache) expect { vivid.write!("nil", "one", "two", "three") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "writes with a block" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write!("one", "five") { "six" } expect(vivid["one"]["five"]).to eql("six") @@ -292,41 +288,31 @@ describe Chef::Node::VividMash do end context "#unlink" do - before do - vivid - expect(root).not_to receive(:reset_cache).with(nil) - end - it "should return nil if the keys don't already exist" do - expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original expect(root).not_to receive(:reset_cache) expect(vivid.unlink("five", "six", "seven", "eight")).to eql(nil) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should unlink hashes" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") expect( vivid.unlink("one") ).to eql({ "two" => { "three" => "four" } }) expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should unlink array elements" do - with_breadcrumb("array") expect(root).to receive(:reset_cache).at_least(:once).with("array") expect(vivid.unlink("array", 2)).to eql(2) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil }) end it "should unlink nil" do - with_breadcrumb("nil") expect(root).to receive(:reset_cache).at_least(:once).with("nil") expect(vivid.unlink("nil")).to eql(nil) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] }) end it "should traverse a nil and safely do nothing" do - with_breadcrumb("nil") expect(root).not_to receive(:reset_cache) expect(vivid.unlink("nil", "foo")).to eql(nil) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) @@ -334,44 +320,139 @@ describe Chef::Node::VividMash do end context "#unlink!" do - before do - vivid - expect(root).not_to receive(:reset_cache).with(nil) - end - it "should raise an exception if the keys don't already exist" do - expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original expect(root).not_to receive(:reset_cache) expect { vivid.unlink!("five", "six", "seven", "eight") }.to raise_error(Chef::Exceptions::NoSuchAttribute) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should unlink! hashes" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") expect( vivid.unlink!("one") ).to eql({ "two" => { "three" => "four" } }) expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should unlink! array elements" do - with_breadcrumb("array") expect(root).to receive(:reset_cache).at_least(:once).with("array") expect(vivid.unlink!("array", 2)).to eql(2) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil }) end it "should unlink! nil" do - with_breadcrumb("nil") expect(root).to receive(:reset_cache).at_least(:once).with("nil") expect(vivid.unlink!("nil")).to eql(nil) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] }) end it "should raise an exception if it traverses a nil" do - with_breadcrumb("nil") expect(root).not_to receive(:reset_cache) expect { vivid.unlink!("nil", "foo") }.to raise_error(Chef::Exceptions::NoSuchAttribute) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end end end + +describe Chef::Node::AttrArray do + let(:root) { instance_double(Chef::Node::Attribute) } + + let(:array) do + Chef::Node::AttrArray.new( + %w{zero one two}, + root + ) + end + + context "#<<" do + it "converts a Hash appended with #<< to a VividMash" do + array << { "three" => "four" } + expect(array[3].class).to eql(Chef::Node::VividMash) + end + + it "deeply converts objects appended with #<<" do + array << [ { "three" => [ 0, 1] } ] + expect(array[3].class).to eql(Chef::Node::AttrArray) + expect(array[3][0].class).to eql(Chef::Node::VividMash) + expect(array[3][0]["three"].class).to eql(Chef::Node::AttrArray) + end + end + + context "#[]=" do + it "assigning a Hash into an array converts it to VividMash" do + array[0] = { "zero" => "zero2" } + expect(array[0].class).to eql(Chef::Node::VividMash) + end + end + + context "#push" do + it "pushing a Hash into an array converts it to VividMash" do + array.push({ "three" => "four" }) + expect(array[3].class).to eql(Chef::Node::VividMash) + end + end + + context "#unshift" do + it "unshifting a Hash into an array converts it to VividMash" do + array.unshift({ "zero" => "zero2" }) + expect(array[0].class).to eql(Chef::Node::VividMash) + end + end + + context "#insert" do + it "inserting a Hash into an array converts it to VividMash" do + array.insert(1, { "zero" => "zero2" }) + expect(array[1].class).to eql(Chef::Node::VividMash) + end + end + + context "#collect!" do + it "converts Hashes" do + array.collect! { |x| { "zero" => "zero2" } } + expect(array[1].class).to eql(Chef::Node::VividMash) + end + end + + context "#map!" do + it "converts Hashes" do + array.map! { |x| { "zero" => "zero2" } } + expect(array[1].class).to eql(Chef::Node::VividMash) + end + end + + context "#compact!" do + it "VividMashes remain VividMashes" do + array = Chef::Node::AttrArray.new( + [ nil, { "one" => "two" }, nil ], + root + ) + expect(array[1].class).to eql(Chef::Node::VividMash) + array.compact! + expect(array[0].class).to eql(Chef::Node::VividMash) + end + end + + context "#fill" do + it "inserts VividMashes for Hashes" do + array.fill({ "one" => "two" }) + expect(array[0].class).to eql(Chef::Node::VividMash) + end + end + + context "#flatten!" do + it "flattens sub-arrays maintaining VividMashes in them" do + array = Chef::Node::AttrArray.new( + [ [ { "one" => "two" } ], [ { "one" => "two" } ] ], + root + ) + expect(array[0][0].class).to eql(Chef::Node::VividMash) + array.flatten! + expect(array[0].class).to eql(Chef::Node::VividMash) + end + end + + context "#replace" do + it "replaces the array converting hashes to mashes" do + array.replace([ { "foo" => "bar" } ]) + expect(array[0].class).to eql(Chef::Node::VividMash) + end + end +end |