# coding: utf-8 require 'spec_helper' require 'puppet/util/yaml' describe Puppet::Util::Yaml do include PuppetSpec::Files let(:filename) { tmpfile("yaml") } shared_examples_for 'yaml file loader' do |load_method| it 'returns false when the file is empty' do file_path = file_containing('input', '') expect(load_method.call(file_path)).to eq(false) end it 'reads a YAML file from disk' do file_path = file_containing('input', YAML.dump({ "my" => "data" })) expect(load_method.call(file_path)).to eq({ "my" => "data" }) end it 'reads YAML as UTF-8' do file_path = file_containing('input', YAML.dump({ "my" => "𠜎" })) expect(load_method.call(file_path)).to eq({ "my" => "𠜎" }) end end context "#safe_load" do it 'raises an error if YAML is invalid' do expect { Puppet::Util::Yaml.safe_load('{ invalid') }.to raise_error(Puppet::Util::Yaml::YamlLoadError, %r[\(\): .* at line \d+ column \d+]) end it 'raises if YAML contains classes not in the list' do expect { Puppet::Util::Yaml.safe_load(<): Tried to load unspecified class: Puppet::Node::Facts") end it 'includes the filename if YAML contains classes not in the list' do expect { Puppet::Util::Yaml.safe_load(< nil) --- a: null YAML end it 'loads a numeric' do expect(Puppet::Util::Yaml.safe_load('42')).to eq(42) end it 'loads a string' do expect(Puppet::Util::Yaml.safe_load('puppet')).to eq('puppet') end it 'loads an array' do expect(Puppet::Util::Yaml.safe_load(<<~YAML)).to eq([1, 2]) --- - 1 - 2 YAML end it 'loads a hash' do expect(Puppet::Util::Yaml.safe_load(<<~YAML)).to eq('a' => 1, 'b' => 2) --- a: 1 b: 2 YAML end it 'loads an alias' do expect(Puppet::Util::Yaml.safe_load(<<~YAML)).to eq('a' => [], 'b' => []) --- a: &1 [] b: *1 YAML end end context "#safe_load_file" do it_should_behave_like 'yaml file loader', Puppet::Util::Yaml.method(:safe_load_file) it 'raises an error when the file is invalid YAML' do file_path = file_containing('input', '{ invalid') expect { Puppet::Util::Yaml.safe_load_file(file_path) }.to raise_error(Puppet::Util::Yaml::YamlLoadError, %r[\(#{file_path}\): .* at line \d+ column \d+]) end it 'raises an error when the filename is illegal' do expect { Puppet::Util::Yaml.safe_load_file("not\0allowed") }.to raise_error(ArgumentError, /pathname contains null byte/) end it 'raises an error when the file does not exist' do expect { Puppet::Util::Yaml.safe_load_file('does/not/exist.yaml') }.to raise_error(Errno::ENOENT, /No such file or directory/) end end context "#safe_load_file_if_valid" do before do Puppet[:log_level] = 'debug' end it_should_behave_like 'yaml file loader', Puppet::Util::Yaml.method(:safe_load_file_if_valid) it 'returns nil when the file is invalid YAML and debug logs about it' do file_path = file_containing('input', '{ invalid') expect(Puppet).to receive(:debug) .with(/Could not retrieve YAML content .+ expected ',' or '}'/).and_call_original expect(Puppet::Util::Yaml.safe_load_file_if_valid(file_path)).to eql(nil) end it 'returns nil when the filename is illegal and debug logs about it' do expect(Puppet).to receive(:debug) .with(/Could not retrieve YAML content .+: pathname contains null byte/).and_call_original expect(Puppet::Util::Yaml.safe_load_file_if_valid("not\0allowed")).to eql(nil) end it 'returns nil when the file does not exist and debug logs about it' do expect(Puppet).to receive(:debug) .with(/Could not retrieve YAML content .+: No such file or directory/).and_call_original expect(Puppet::Util::Yaml.safe_load_file_if_valid('does/not/exist.yaml')).to eql(nil) end end context '#load_file' do it_should_behave_like 'yaml file loader', Puppet::Util::Yaml.method(:load_file) it 'raises an error when the file is invalid YAML' do file_path = file_containing('input', '{ invalid') expect { Puppet::Util::Yaml.load_file(file_path) }.to raise_error(Puppet::Util::Yaml::YamlLoadError, %r{\(#{file_path}\): .* at line \d+ column \d+}) end it 'raises an error when the filename is illegal' do expect { Puppet::Util::Yaml.load_file("not\0allowed") }.to raise_error(Puppet::Util::Yaml::YamlLoadError, /null byte/) end it 'raises an error when the file does not exist' do expect { Puppet::Util::Yaml.load_file('does/not/exist.yaml') }.to raise_error(Puppet::Util::Yaml::YamlLoadError, /No such file or directory/) end it 'allows return value to be overridden' do file_path = file_containing('input', '') expect(Puppet::Util::Yaml.load_file(file_path, {})).to eq({}) end it 'loads arbitrary objects' do file_path = file_containing('input', "--- !ruby/object {}\n") expect(Puppet::Util::Yaml.load_file(file_path, {})).to be_instance_of(Object) end it 'should allow one to strip ruby tags that would otherwise not parse' do file_path = file_containing('input', "---\nweirddata: !ruby/hash:Not::A::Valid::Class {}") expect(Puppet::Util::Yaml.load_file(file_path, {}, true)).to eq({"weirddata" => {}}) end it 'should not strip non-ruby tags' do file_path = file_containing('input', "---\nweirddata: !binary |-\n e21kNX04MTE4ZGY2NmM5MTc3OTg4ZWE4Y2JiOWEzMjMyNzFkYg==") expect(Puppet::Util::Yaml.load_file(file_path, {}, true)).to eq({"weirddata" => "{md5}8118df66c9177988ea8cbb9a323271db"}) end it 'writes data formatted as YAML to disk' do file_path = file_containing('input', '') Puppet::Util::Yaml.dump({ "my" => "data" }, file_path) expect(Puppet::Util::Yaml.load_file(file_path)).to eq({ "my" => "data" }) end end end