diff --git a/lib/puppet/type/concat_file.rb b/lib/puppet/type/concat_file.rb index 1c005c6..3bbf3ee 100644 --- a/lib/puppet/type/concat_file.rb +++ b/lib/puppet/type/concat_file.rb @@ -1,363 +1,365 @@ +# frozen_string_literal: true + require 'puppet/type/file/owner' require 'puppet/type/file/group' require 'puppet/type/file/mode' require 'puppet/util/checksums' Puppet::Type.newtype(:concat_file) do @doc = <<-DOC @summary Generates a file with content from fragments sharing a common unique tag. @example Concat_fragment <<| tag == 'unique_tag' |>> concat_file { '/tmp/file': tag => 'unique_tag', # Optional. Default to undef path => '/tmp/file', # Optional. If given it overrides the resource name owner => 'root', # Optional. Default to undef group => 'root', # Optional. Default to undef mode => '0644' # Optional. Default to undef order => 'numeric' # Optional, Default to 'numeric' ensure_newline => false # Optional, Defaults to false } DOC ensurable do desc <<-DOC Specifies whether the destination file should exist. Setting to 'absent' tells Puppet to delete the destination file if it exists, and negates the effect of any other parameters. DOC defaultvalues defaultto { :present } end def exists? self[:ensure] == :present end newparam(:tag) do desc 'Required. Specifies a unique tag reference to collect all concat_fragments with the same tag.' end newparam(:path, namevar: true) do desc <<-DOC Specifies a destination file for the combined fragments. Valid options: a string containing an absolute path. Default value: the title of your declared resource. DOC validate do |value| unless Puppet::Util.absolute_path?(value, :posix) || Puppet::Util.absolute_path?(value, :windows) raise ArgumentError, _("File paths must be fully qualified, not '%{_value}'") % { _value: value } end end end newparam(:owner, parent: Puppet::Type::File::Owner) do desc <<-DOC Specifies the owner of the destination file. Valid options: a string containing a username or integer containing a uid. DOC end newparam(:group, parent: Puppet::Type::File::Group) do desc <<-DOC Specifies a permissions group for the destination file. Valid options: a string containing a group name or integer containing a gid. DOC end newparam(:mode, parent: Puppet::Type::File::Mode) do desc <<-DOC Specifies the permissions mode of the destination file. Valid options: a string containing a permission mode value in octal notation. DOC end newparam(:order) do desc <<-DOC Specifies a method for sorting your fragments by name within the destination file. You can override this setting for individual fragments by adjusting the order parameter in their concat::fragment declarations. DOC newvalues(:alpha, :numeric) defaultto :numeric end newparam(:backup) do desc <<-DOC Specifies whether (and how) to back up the destination file before overwriting it. Your value gets passed on to Puppet's native file resource for execution. Valid options: true, false, or a string representing either a target filebucket or a filename extension beginning with ".".' DOC validate do |value| unless [TrueClass, FalseClass, String].include?(value.class) raise ArgumentError, _('Backup must be a Boolean or String') end end end newparam(:replace, boolean: true, parent: Puppet::Parameter::Boolean) do desc 'Specifies whether to overwrite the destination file if it already exists.' defaultto true end newparam(:validate_cmd) do desc <<-DOC Specifies a validation command to apply to the destination file. Requires Puppet version 3.5 or newer. Valid options: a string to be passed to a file resource. DOC validate do |value| unless value.is_a?(String) raise ArgumentError, _('Validate_cmd must be a String') end end end newparam(:ensure_newline, boolean: true, parent: Puppet::Parameter::Boolean) do desc "Specifies whether to add a line break at the end of each fragment that doesn't already end in one." defaultto false end newparam(:format) do desc <<-DOC Specify what data type to merge the fragments as. Valid options: 'plain', 'yaml', 'json', 'json-array', 'json-pretty', 'json-array-pretty'. DOC newvalues(:plain, :yaml, :json, :'json-array', :'json-pretty', :'json-array-pretty') defaultto :plain end newparam(:force, boolean: true, parent: Puppet::Parameter::Boolean) do desc 'Specifies whether to merge data structures, keeping the values with higher order.' defaultto false end newparam(:selinux_ignore_defaults, boolean: true, parent: Puppet::Parameter::Boolean) do desc <<-DOC See the file type's selinux_ignore_defaults documentention: https://docs.puppetlabs.com/references/latest/type.html#file-attribute-selinux_ignore_defaults. DOC end newparam(:selrange) do desc "See the file type's selrange documentation: https://docs.puppetlabs.com/references/latest/type.html#file-attribute-selrange" validate do |value| raise ArgumentError, _('Selrange must be a String') unless value.is_a?(String) end end newparam(:selrole) do desc "See the file type's selrole documentation: https://docs.puppetlabs.com/references/latest/type.html#file-attribute-selrole" validate do |value| raise ArgumentError, _('Selrole must be a String') unless value.is_a?(String) end end newparam(:seltype) do desc "See the file type's seltype documentation: https://docs.puppetlabs.com/references/latest/type.html#file-attribute-seltype" validate do |value| raise ArgumentError, _('Seltype must be a String') unless value.is_a?(String) end end newparam(:seluser) do desc "See the file type's seluser documentation: https://docs.puppetlabs.com/references/latest/type.html#file-attribute-seluser" validate do |value| raise ArgumentError, _('Seluser must be a String') unless value.is_a?(String) end end newparam(:show_diff, boolean: true, parent: Puppet::Parameter::Boolean) do desc <<-DOC Specifies whether to set the show_diff parameter for the file resource. Useful for hiding secrets stored in hiera from insecure reporting methods. DOC end # Autorequire the file we are generating below # Why is this necessary ? autorequire(:file) do [self[:path]] end def fragments # Collect fragments that target this resource by path, title or tag. @fragments ||= catalog.resources.map { |resource| next unless resource.is_a?(Puppet::Type.type(:concat_fragment)) if resource[:target] == self[:path] || resource[:target] == title || (resource[:tag] && resource[:tag] == self[:tag]) resource end }.compact end def decompound(d) d.split('___', 2).map { |v| (v =~ %r{^\d+$}) ? v.to_i : v } end def should_content return @generated_content if @generated_content @generated_content = '' content_fragments = [] fragments.each do |r| content_fragments << ["#{r[:order]}___#{r[:name]}", fragment_content(r)] end sorted = if self[:order] == :numeric content_fragments.sort do |a, b| decompound(a[0]) <=> decompound(b[0]) end else content_fragments.sort_by do |a| a_order, a_name = a[0].split('__', 2) [a_order, a_name] end end case self[:format] when :plain @generated_content = sorted.map { |cf| cf[1] }.join when :yaml content_array = sorted.map do |cf| YAML.safe_load(cf[1]) end content_hash = content_array.reduce({}) do |memo, current| nested_merge(memo, current) end @generated_content = content_hash.to_yaml when :json, :'json-array', :'json-pretty', :'json-array-pretty' content_array = sorted.map do |cf| JSON.parse(cf[1]) end if [:json, :'json-pretty'].include?(self[:format]) content_hash = content_array.reduce({}) do |memo, current| nested_merge(memo, current) end @generated_content = if self[:format] == :json content_hash.to_json else JSON.pretty_generate(content_hash) end else @generated_content = if self[:format] == :'json-array' content_array.to_json else JSON.pretty_generate(content_array) end end end @generated_content end def nested_merge(hash1, hash2) # If a hash is empty, simply return the other return hash1 if hash2.empty? return hash2 if hash1.empty? # Unique merge for arrays if hash1.is_a?(Array) && hash2.is_a?(Array) return (hash1 + hash2).uniq end # Deep-merge Hashes; higher order value is kept hash1.merge(hash2) do |k, v1, v2| if v1.is_a?(Hash) && v2.is_a?(Hash) nested_merge(v1, v2) elsif v1.is_a?(Array) && v2.is_a?(Array) nested_merge(v1, v2) else # Fail if there are duplicate keys without force unless v1 == v2 unless self[:force] err_message = [ "Duplicate key '#{k}' found with values '#{v1}' and #{v2}'.", "Use 'force' attribute to merge keys.", ] raise(_(err_message.join(' '))) end Puppet.debug("Key '#{k}': replacing '#{v2}' with '#{v1}'.") end v1 end end end def fragment_content(r) if r[:content].nil? == false fragment_content = r[:content] elsif r[:source].nil? == false @source = nil Array(r[:source]).each do |source| if Puppet::FileServing::Metadata.indirection.find(source) @source = source break end end raise _('Could not retrieve source(s) %{_array}') % { _array: Array(r[:source]).join(', ') } unless @source tmp = Puppet::FileServing::Content.indirection.find(@source) fragment_content = tmp.content unless tmp.nil? end if self[:ensure_newline] newline = Puppet::Util::Platform.windows? ? "\r\n" : "\n" fragment_content << newline unless fragment_content =~ %r{#{newline}\Z} end fragment_content end def generate file_opts = { ensure: (self[:ensure] == :absent) ? :absent : :file, } [:path, :owner, :group, :mode, :replace, :backup, :selinux_ignore_defaults, :selrange, :selrole, :seltype, :seluser, :validate_cmd, :show_diff].each do |param| file_opts[param] = self[param] unless self[param].nil? end metaparams = Puppet::Type.metaparams excluded_metaparams = [:before, :notify, :require, :subscribe, :tag] metaparams.reject! { |param| excluded_metaparams.include? param } metaparams.each do |metaparam| file_opts[metaparam] = self[metaparam] unless self[metaparam].nil? end [Puppet::Type.type(:file).new(file_opts)] end def eval_generate content = should_content unless content.nil? catalog.resource("File[#{self[:path]}]")[:content] = content end [catalog.resource("File[#{self[:path]}]")] end end diff --git a/lib/puppet/type/concat_fragment.rb b/lib/puppet/type/concat_fragment.rb index c66a557..a576ccd 100644 --- a/lib/puppet/type/concat_fragment.rb +++ b/lib/puppet/type/concat_fragment.rb @@ -1,95 +1,97 @@ +# frozen_string_literal: true + Puppet::Type.newtype(:concat_fragment) do @doc = <<-DOC @summary Manages the fragment. @example # The example is based on exported resources. concat_fragment { \"uniqe_name_${::fqdn}\": tag => 'unique_name', order => 10, # Optional. Default to 10 content => 'some content' # OR # content => template('template.erb') source => 'puppet:///path/to/file' } DOC newparam(:name, namevar: true) do desc 'Name of resource.' end newparam(:target) do desc <<-DOC Required. Specifies the destination file of the fragment. Valid options: a string containing the path or title of the parent concat_file resource. DOC validate do |value| raise ArgumentError, _('Target must be a String') unless value.is_a?(String) end end newparam(:content) do desc <<-DOC Supplies the content of the fragment. Note: You must supply either a content parameter or a source parameter. Valid options: a string DOC validate do |value| raise ArgumentError, _('Content must be a String') unless value.is_a?(String) end end newparam(:source) do desc <<-DOC Specifies a file to read into the content of the fragment. Note: You must supply either a content parameter or a source parameter. Valid options: a string or an array, containing one or more Puppet URLs. DOC validate do |value| raise ArgumentError, _('Content must be a String or Array') unless [String, Array].include?(value.class) end end newparam(:order) do desc <<-DOC Reorders your fragments within the destination file. Fragments that share the same order number are ordered by name. The string option is recommended. DOC defaultto '10' validate do |val| raise Puppet::ParseError, _('$order is not a string or integer.') unless val.is_a?(String) || val.is_a?(Integer) raise Puppet::ParseError, _('Order cannot contain \'/\', \':\', or \'\\n\'.') if val.to_s =~ %r{[:\n\/]} end end newparam(:tag) do desc 'Specifies a unique tag to be used by concat_file to reference and collect content.' end autorequire(:file) do found = catalog.resources.select do |resource| next unless resource.is_a?(Puppet::Type.type(:concat_file)) resource[:path] == self[:target] || resource.title == self[:target] || (resource[:tag] && resource[:tag] == self[:tag]) end if found.empty? tag_message = (self[:tag]) ? "or tag '#{self[:tag]} " : '' warning "Target Concat_file with path or title '#{self[:target]}' #{tag_message}not found in the catalog" end end validate do # Check if target is set raise Puppet::ParseError, _("No 'target' or 'tag' set") unless self[:target] || self[:tag] # Check if either source or content is set. raise error if none is set raise Puppet::ParseError, _("Set either 'source' or 'content'") if self[:source].nil? && self[:content].nil? # Check if both are set, if so rais error raise Puppet::ParseError, _("Can't use 'source' and 'content' at the same time") if !self[:source].nil? && !self[:content].nil? end end diff --git a/spec/acceptance/backup_spec.rb b/spec/acceptance/backup_spec.rb index 80a9879..78448ec 100644 --- a/spec/acceptance/backup_spec.rb +++ b/spec/acceptance/backup_spec.rb @@ -1,77 +1,79 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'concat backup parameter' do before(:all) do @basedir = setup_test_directory end describe 'when puppet' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': backup => 'puppet', } concat::fragment { 'new file': target => '#{@basedir}/file', content => 'new contents', } MANIFEST end it 'applies the manifest twice with "Filebucketed" stdout and no stderr' do expect(apply_manifest(pp, catch_failures: true, debug: true).stdout).to match(%r{Filebucketed.*to puppet with sum.*}) apply_manifest(pp, catch_changes: true) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match %r{new contents} end end describe 'when .backup' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': backup => '.backup', } concat::fragment { 'new file': target => '#{@basedir}/file', content => 'backup extension', } MANIFEST end # XXX Puppet doesn't mention anything about filebucketing with a given # extension like .backup it 'applies the manifest twice no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match %r{backup extension} expect(file("#{@basedir}/file.backup")).to be_file expect(file("#{@basedir}/file.backup").content).to match %r{new contents} end end # XXX The backup parameter uses validate_string() and thus can't be the # boolean false value, but the string 'false' has the same effect in Puppet 3 describe "when 'false'" do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': backup => '.backup', } concat::fragment { 'new file': target => '#{@basedir}/file', content => 'new contents', } MANIFEST end it 'applies the manifest twice with no "Filebucketed" stdout and no stderr' do apply_manifest(pp, catch_failures: true) do |r| expect(r.stdout).not_to match(%r{Filebucketed}) end apply_manifest(pp, catch_changes: true) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match %r{new contents} end end end diff --git a/spec/acceptance/concat_spec.rb b/spec/acceptance/concat_spec.rb index 1e6e752..2effba4 100644 --- a/spec/acceptance/concat_spec.rb +++ b/spec/acceptance/concat_spec.rb @@ -1,169 +1,171 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' case os[:family] when 'aix' username = 'root' groupname = 'system' when 'darwin' username = 'root' groupname = 'wheel' when 'windows' username = 'Administrator' groupname = 'Administrators' else username = 'root' groupname = 'root' end describe 'basic concat test' do before(:all) do @basedir = setup_test_directory end describe 'with owner/group root' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': owner => '#{username}', group => '#{groupname}', mode => '0644', } concat::fragment { '1': target => '#{@basedir}/file', content => '1', order => '01', } concat::fragment { '2': target => '#{@basedir}/file', content => '2', order => '02', } MANIFEST end it 'idempotent, file matches' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file")).to be_owned_by username unless os[:family] == 'windows' expect(file("#{@basedir}/file")).to be_grouped_into groupname unless os[:family] == 'windows' || os[:family] == 'darwin' expect(file("#{@basedir}/file")).to be_mode 644 unless os[:family] == 'aix' || os[:family] == 'windows' expect(file("#{@basedir}/file").content).to match '1' expect(file("#{@basedir}/file").content).to match '2' end end describe 'when present with path set' do let(:pp) do <<-MANIFEST concat { 'file': ensure => present, path => '#{@basedir}/file', mode => '0644', } concat::fragment { '1': target => 'file', content => '1', order => '01', } MANIFEST end it 'idempotent, file matches' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file")).to be_mode 644 unless os[:family] == 'aix' || os[:family] == 'windows' expect(file("#{@basedir}/file").content).to match '1' end end describe 'with no fragments declared' do let(:pp) do <<-MANIFEST concat { 'file': ensure => present, path => '#{@basedir}/file', mode => '0644', } MANIFEST end it 'applies manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to eq '' end end describe 'when absent with path set' do let(:pp) do <<-MANIFEST concat { 'file': ensure => absent, path => '#{@basedir}/file', mode => '0644', } concat::fragment { '1': target => 'file', content => '1', order => '01', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/file")).not_to be_file end end describe 'when present with path that has special characters' do filename = (os[:family] == 'windows') ? 'file(1)' : 'file(1:2)' let(:pp) do <<-MANIFEST concat { '#{filename}': ensure => present, path => '#{@basedir}/#{filename}', mode => '0644', } concat::fragment { '1': target => '#{filename}', content => '1', order => '01', } MANIFEST end it 'idempotent, file matches' do idempotent_apply(pp) expect(file("#{@basedir}/#{filename}")).to be_file expect(file("#{@basedir}/#{filename}")).to be_mode 644 unless os[:family] == 'aix' || os[:family] == 'windows' expect(file("#{@basedir}/#{filename}").content).to match '1' end end describe 'with noop properly' do let(:pp) do <<-MANIFEST concat { 'file': ensure => present, path => '#{@basedir}/file', mode => '0644', noop => true, } concat::fragment { '1': target => 'file', content => '1', order => '01', } MANIFEST end it 'applies manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/file")).not_to be_file end end end diff --git a/spec/acceptance/concurrency_spec.rb b/spec/acceptance/concurrency_spec.rb index 49d39e4..f3e184a 100644 --- a/spec/acceptance/concurrency_spec.rb +++ b/spec/acceptance/concurrency_spec.rb @@ -1,35 +1,37 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'concurrency, with file recursive purge' do before(:all) do @basedir = setup_test_directory end describe 'when run should still create concat file' do let(:pp) do <<-MANIFEST file { '#{@basedir}/bar': ensure => directory, purge => true, recurse => true, } concat { "foobar": ensure => 'present', path => '#{@basedir}/bar/foobar', } concat::fragment { 'foo': target => 'foobar', content => 'foo', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/bar/foobar")).to be_file expect(file("#{@basedir}/bar/foobar").content).to match 'foo' end end end diff --git a/spec/acceptance/force_spec.rb b/spec/acceptance/force_spec.rb index 6785606..47f179b 100644 --- a/spec/acceptance/force_spec.rb +++ b/spec/acceptance/force_spec.rb @@ -1,147 +1,149 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'force merge of file' do before(:all) do @basedir = setup_test_directory end describe 'when run should not force' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': format => 'yaml', force => false, } concat::fragment { '1': target => '#{@basedir}/file', content => '{"one": "foo"}', } concat::fragment { '2': target => '#{@basedir}/file', content => '{"one": "bar"}', } MANIFEST end it 'applies manifest twice with stderr check' do expect(apply_manifest(pp, catch_failures: true).stderr).to match("Duplicate key 'one' found with values 'foo' and bar'. Use 'force' attribute to merge keys.") expect(apply_manifest(pp, catch_failures: true).stderr).to match("Duplicate key 'one' found with values 'foo' and bar'. Use 'force' attribute to merge keys.") expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match 'file exists' expect(file("#{@basedir}/file").content).not_to match 'one: foo' expect(file("#{@basedir}/file").content).not_to match 'one: bar' end end describe 'when run should not force by default' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': format => 'yaml', } concat::fragment { '1': target => '#{@basedir}/file', content => '{"one": "foo"}', } concat::fragment { '2': target => '#{@basedir}/file', content => '{"one": "bar"}', } MANIFEST end it 'applies manifest twice with stderr check' do expect(apply_manifest(pp, catch_failures: true).stderr).to match("Duplicate key 'one' found with values 'foo' and bar'. Use 'force' attribute to merge keys.") expect(apply_manifest(pp, catch_failures: true).stderr).to match("Duplicate key 'one' found with values 'foo' and bar'. Use 'force' attribute to merge keys.") expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match 'file exists' expect(file("#{@basedir}/file").content).not_to match 'one: foo' expect(file("#{@basedir}/file").content).not_to match 'one: bar' end end describe 'when run should force' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': format => 'yaml', force => true, } concat::fragment { '1': target => '#{@basedir}/file', content => '{"one": "foo"}', } concat::fragment { '2': target => '#{@basedir}/file', content => '{"one": "bar"}', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match 'one: foo' end end describe 'when run should force merge nested arrays' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': format => 'json', force => true, } concat::fragment { '1': target => '#{@basedir}/file', content => '{"one": [1]}', } concat::fragment { '2': target => '#{@basedir}/file', content => '{"one": [2]}', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to contain '{"one":\[1,2\]}' end end describe 'when run should not force on plain' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': force => true, format => plain, } concat::fragment { '1': target => '#{@basedir}/file', content => '{"one": "foo"}', } concat::fragment { '2': target => '#{@basedir}/file', content => '{"one": "bar"}', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match '{"one": "foo"}{"one": "bar"}' end end end diff --git a/spec/acceptance/format_spec.rb b/spec/acceptance/format_spec.rb index 7bd1994..3b9adfb 100644 --- a/spec/acceptance/format_spec.rb +++ b/spec/acceptance/format_spec.rb @@ -1,214 +1,216 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'format of file' do before(:all) do @basedir = setup_test_directory end describe 'when run should default to plain' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': } concat::fragment { '1': target => '#{@basedir}/file', content => '{"one": "foo"}', } concat::fragment { '2': target => '#{@basedir}/file', content => '{"one": "bar"}', } MANIFEST end it 'idempotent, file matches' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match '{"one": "foo"}{"one": "bar"}' end end describe 'when run should output to plain format' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': format => plain, } concat::fragment { '1': target => '#{@basedir}/file', content => '{"one": "foo"}', } concat::fragment { '2': target => '#{@basedir}/file', content => '{"one": "bar"}', } MANIFEST end it 'idempotent, file matches' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match '{"one": "foo"}{"one": "bar"}' end end describe 'when run should output to yaml format' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': format => 'yaml', } concat::fragment { '1': target => '#{@basedir}/file', content => '{"one": "foo"}', } concat::fragment { '2': target => '#{@basedir}/file', content => '{"two": "bar"}', } MANIFEST end it 'idempotent, file matches' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match 'one: foo\Rtwo: bar' end end describe 'when run should output yaml arrays to yaml format' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': format => 'yaml', } concat::fragment { '1': target => '#{@basedir}/file', content => to_yaml([{ 'one.a' => 'foo', 'one.b' => 'bar' }]), } concat::fragment { '2': target => '#{@basedir}/file', content => to_yaml([{ 'two.a' => 'dip', 'two.b' => 'doot' }]), } MANIFEST end it 'idempotent, file matches' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match '- one.a: foo\R one.b: bar\R- two.a: dip\R two.b: doot' end end describe 'when run should output to json format' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': format => 'json', } concat::fragment { '1': target => '#{@basedir}/file', content => '{"one": "foo"}', } concat::fragment { '2': target => '#{@basedir}/file', content => '{"two": "bar"}', } MANIFEST end it 'idempotent, file matches' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match '{"one":"foo","two":"bar"}' end end describe 'when run should output to json-array format' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': format => 'json-array', } concat::fragment { '1': target => '#{@basedir}/file', content => '{"one": "foo"}', } concat::fragment { '2': target => '#{@basedir}/file', content => '{"two": "bar"}', } MANIFEST end it 'idempotent, file matches' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match '[{"one":"foo"},{"two":"bar"}]' end end describe 'when run should output to json-pretty format' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': format => 'json-pretty', } concat::fragment { '1': target => '#{@basedir}/file', content => '{"one": "foo"}', } concat::fragment { '2': target => '#{@basedir}/file', content => '{"two": "bar"}', } MANIFEST end it 'idempotent, file matches' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match '{\R "one": "foo",\R "two": "bar"\R}' end end describe 'when run should output to json-array-pretty format' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': format => 'json-array-pretty', } concat::fragment { '1': target => '#{@basedir}/file', content => '{"one": "foo"}', } concat::fragment { '2': target => '#{@basedir}/file', content => '{"two": "bar"}', } MANIFEST end it 'idempotent, file matches' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match '[\n {\n "one": "foo"\n },\n {\n "two": "bar"\n }\n]' end end end diff --git a/spec/acceptance/fragment_order_spec.rb b/spec/acceptance/fragment_order_spec.rb index 2e2e39a..397bb85 100644 --- a/spec/acceptance/fragment_order_spec.rb +++ b/spec/acceptance/fragment_order_spec.rb @@ -1,98 +1,100 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'concat::fragment order' do before(:all) do @basedir = setup_test_directory end describe 'with reverse order, alphabetical' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/foo': order => 'alpha' } concat::fragment { '1': target => '#{@basedir}/foo', content => 'string1', order => '15', } concat::fragment { '2': target => '#{@basedir}/foo', content => 'string2', # default order 10 } concat::fragment { '3': target => '#{@basedir}/foo', content => 'string3', order => '1', } MANIFEST end it 'idempotent, file matches' do idempotent_apply(pp) expect(file("#{@basedir}/foo")).to be_file expect(file("#{@basedir}/foo").content).to match %r{string3string2string1} end end describe 'with reverse order, numeric' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/foo': order => 'numeric' } concat::fragment { '1': target => '#{@basedir}/foo', content => 'string1', order => '15', } concat::fragment { '2': target => '#{@basedir}/foo', content => 'string2', # default order 10 } concat::fragment { '3': target => '#{@basedir}/foo', content => 'string3', order => '1', } MANIFEST end it 'idempotent, file matches' do idempotent_apply(pp) expect(file("#{@basedir}/foo")).to be_file expect(file("#{@basedir}/foo").content).to match %r{string3string2string1} end end describe 'with normal order' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/foo': } concat::fragment { '1': target => '#{@basedir}/foo', content => 'string1', order => '01', } concat::fragment { '2': target => '#{@basedir}/foo', content => 'string2', order => '02' } concat::fragment { '3': target => '#{@basedir}/foo', content => 'string3', order => '03', } MANIFEST end it 'idempotent, file matches' do idempotent_apply(pp) expect(file("#{@basedir}/foo")).to be_file expect(file("#{@basedir}/foo").content).to match %r{string1string2string3} end end end diff --git a/spec/acceptance/fragment_replace_spec.rb b/spec/acceptance/fragment_replace_spec.rb index f552f7b..9d32610 100644 --- a/spec/acceptance/fragment_replace_spec.rb +++ b/spec/acceptance/fragment_replace_spec.rb @@ -1,182 +1,184 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'replacement of' do before(:all) do @basedir = setup_test_directory end describe 'file' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': replace => false, } concat::fragment { '1': target => '#{@basedir}/file', content => '1', } concat::fragment { '2': target => '#{@basedir}/file', content => '2', } concat { '#{@basedir}/file2': replace => true, } concat::fragment { 'file2_1': target => '#{@basedir}/file2', content => '1', } concat::fragment { 'file2_2': target => '#{@basedir}/file2', content => '2', } MANIFEST end it 'when file should not succeed' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match 'file exists' expect(file("#{@basedir}/file").content).not_to match '1' expect(file("#{@basedir}/file").content).not_to match '2' end it 'when file should succeed' do expect(file("#{@basedir}/file2")).to be_file expect(file("#{@basedir}/file2").content).not_to match 'file exists' expect(file("#{@basedir}/file2").content).to match '1' expect(file("#{@basedir}/file2").content).to match '2' end end describe 'symlink', unless: (os[:family] == 'windows') do # XXX the core puppet file type will replace a symlink with a plain file # when using ensure => present and source => ... but it will not when using # ensure => present and content => ...; this is somewhat confusing behavior before(:all) do pp = <<-MANIFEST file { '#{@basedir}': ensure => directory, } file { '#{@basedir}/file': ensure => link, target => '#{@basedir}/dangling', } MANIFEST apply_manifest(pp) end let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': replace => false, } concat::fragment { '1': target => '#{@basedir}/file', content => '1', } concat::fragment { '2': target => '#{@basedir}/file', content => '2', } concat { '#{@basedir}/file2': replace => true, } concat::fragment { 'file2_1': target => '#{@basedir}/file2', content => '1', } concat::fragment { 'file2_2': target => '#{@basedir}/file2', content => '2', } MANIFEST end it 'when symlink should not succeed' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_linked_to "#{@basedir}/dangling" unless os[:family] == 'aix' || os[:family] == 'windows' expect(file("#{@basedir}/dangling")).not_to be_file expect(file("#{@basedir}/dangling")).not_to be_directory end it 'when symlink should succeed' do expect(file("#{@basedir}/file2")).to be_file expect(file("#{@basedir}/file2").content).to match '1' expect(file("#{@basedir}/file2").content).to match '2' end end describe 'when directory should not succeed' do before(:all) do pp = <<-MANIFEST file { '#{@basedir}': ensure => directory, } file { '#{@basedir}/file': ensure => directory, } MANIFEST apply_manifest(pp) end let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': } concat::fragment { '1': target => '#{@basedir}/file', content => '1', } concat::fragment { '2': target => '#{@basedir}/file', content => '2', } MANIFEST end it 'applies the manifest twice with stderr' do expect(apply_manifest(pp, expect_failures: true).stderr).to match(%r{change from '?directory'? to '?file'? failed}) expect(apply_manifest(pp, expect_failures: true).stderr).to match(%r{change from '?directory'? to '?file'? failed}) expect(file("#{@basedir}/file")).to be_directory end end # XXX # when there are no fragments, and the replace param will only replace # files and symlinks, not directories. The semantics either need to be # changed, extended, or a new param introduced to control directory # replacement. describe 'when directory should succeed', pending: 'not yet implemented' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': } concat::fragment { '1': target => '#{@basedir}/file', content => '1', } concat::fragment { '2': target => '#{@basedir}/file', content => '2', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match '1' end end end diff --git a/spec/acceptance/fragment_source_spec.rb b/spec/acceptance/fragment_source_spec.rb index 38f5167..387f3dc 100644 --- a/spec/acceptance/fragment_source_spec.rb +++ b/spec/acceptance/fragment_source_spec.rb @@ -1,143 +1,145 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' case os[:family] when 'aix' username = 'root' groupname = 'system' when 'darwin' username = 'root' groupname = 'wheel' when 'windows' username = 'Administrator' groupname = 'Administrators' else username = 'root' groupname = 'root' end describe 'concat::fragment source' do before(:all) do @basedir = setup_test_directory end describe 'when run should read file fragments from local system' do let(:pp) do <<-MANIFEST file { '#{@basedir}/file1': content => "file1 contents\n" } file { '#{@basedir}/file2': content => "file2 contents\n" } concat { '#{@basedir}/foo': } concat::fragment { '1': target => '#{@basedir}/foo', source => '#{@basedir}/file1', require => File['#{@basedir}/file1'], } concat::fragment { '2': target => '#{@basedir}/foo', content => 'string1 contents', } concat::fragment { '3': target => '#{@basedir}/foo', source => '#{@basedir}/file2', require => File['#{@basedir}/file2'], } MANIFEST end it 'idempotent, file matches' do idempotent_apply(pp) expect(file("#{@basedir}/foo")).to be_file expect(file("#{@basedir}/foo").content).to match 'file1 contents' expect(file("#{@basedir}/foo").content).to match 'file2 contents' end end describe 'when run should create files containing first match only.' do let(:pp) do <<-MANIFEST file { '#{@basedir}/file1': content => "file1 contents\n" } file { '#{@basedir}/file2': content => "file2 contents\n" } concat { '#{@basedir}/result_file1': owner => '#{username}', group => '#{groupname}', mode => '0644', } concat { '#{@basedir}/result_file2': owner => '#{username}', group => '#{groupname}', mode => '0644', } concat { '#{@basedir}/result_file3': owner => '#{username}', group => '#{groupname}', mode => '0644', } concat::fragment { '1': target => '#{@basedir}/result_file1', source => [ '#{@basedir}/file1', '#{@basedir}/file2' ], require => [ File['#{@basedir}/file1'], File['#{@basedir}/file2'] ], order => '01', } concat::fragment { '2': target => '#{@basedir}/result_file2', source => [ '#{@basedir}/file2', '#{@basedir}/file1' ], require => [ File['#{@basedir}/file1'], File['#{@basedir}/file2'] ], order => '01', } concat::fragment { '3': target => '#{@basedir}/result_file3', source => [ '#{@basedir}/file1', '#{@basedir}/file2' ], require => [ File['#{@basedir}/file1'], File['#{@basedir}/file2'] ], order => '01', } MANIFEST end it 'idempotent, files match' do idempotent_apply(pp) expect(file("#{@basedir}/result_file1")).to be_file expect(file("#{@basedir}/result_file1").content).to match 'file1 contents' expect(file("#{@basedir}/result_file1").content).not_to match 'file2 contents' expect(file("#{@basedir}/result_file2")).to be_file expect(file("#{@basedir}/result_file2").content).to match 'file2 contents' expect(file("#{@basedir}/result_file2").content).not_to match 'file1 contents' expect(file("#{@basedir}/result_file3")).to be_file expect(file("#{@basedir}/result_file3").content).to match 'file1 contents' expect(file("#{@basedir}/result_file3").content).not_to match 'file2 contents' end end describe 'when run should fail if no match on source.' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/fail_no_source': owner => '#{username}', group => '#{groupname}', mode => '0644', } concat::fragment { '1': target => '#{@basedir}/fail_no_source', source => [ '#{@basedir}/nofilehere', '#{@basedir}/nothereeither' ], order => '01', } MANIFEST end it 'applies the manifest with resource failures' do expect(apply_manifest(pp, catch_failures: true).stderr).to match(%r{Failed to generate additional resources using 'eval_generate'}) expect(file("#{@basedir}/fail_no_source")).not_to be_directory end end end diff --git a/spec/acceptance/fragments_are_always_replaced_spec.rb b/spec/acceptance/fragments_are_always_replaced_spec.rb index fc3f96a..aaae525 100644 --- a/spec/acceptance/fragments_are_always_replaced_spec.rb +++ b/spec/acceptance/fragments_are_always_replaced_spec.rb @@ -1,101 +1,103 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'concat::fragment replace' do before(:all) do @basedir = setup_test_directory end describe 'when run should create fragment files' do let(:pp1) do <<-MANIFEST concat { '#{@basedir}/foo': } concat::fragment { '1': target => '#{@basedir}/foo', content => 'caller has replace unset run 1', } MANIFEST end let(:pp2) do <<-MANIFEST concat { '#{@basedir}/foo': } concat::fragment { '1': target => '#{@basedir}/foo', content => 'caller has replace unset run 2', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp1) idempotent_apply(pp2) expect(file("#{@basedir}/foo")).to be_file expect(file("#{@basedir}/foo").content).not_to match 'caller has replace unset run 1' expect(file("#{@basedir}/foo").content).to match 'caller has replace unset run 2' end end # should create fragment files describe 'when run should replace its own fragment files when caller has File { replace=>true } set' do let(:pp1) do <<-MANIFEST File { replace=>true } concat { '#{@basedir}/foo': } concat::fragment { '1': target => '#{@basedir}/foo', content => 'caller has replace true set run 1', } MANIFEST end let(:pp2) do <<-MANIFEST File { replace=>true } concat { '#{@basedir}/foo': } concat::fragment { '1': target => '#{@basedir}/foo', content => 'caller has replace true set run 2', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp1) idempotent_apply(pp2) expect(file("#{@basedir}/foo")).to be_file expect(file("#{@basedir}/foo").content).not_to match 'caller has replace true set run 1' expect(file("#{@basedir}/foo").content).to match 'caller has replace true set run 2' end end # should replace its own fragment files when caller has File(replace=>true) set describe 'when run should replace its own fragment files even when caller has File { replace=>false } set' do let(:pp1) do <<-MANIFEST File { replace=>false } concat { '#{@basedir}/foo': } concat::fragment { '1': target => '#{@basedir}/foo', content => 'caller has replace false set run 1', } MANIFEST end let(:pp2) do <<-MANIFEST File { replace=>false } concat { '#{@basedir}/foo': } concat::fragment { '1': target => '#{@basedir}/foo', content => 'caller has replace false set run 2', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp1) idempotent_apply(pp2) expect(file("#{@basedir}/foo")).to be_file expect(file("#{@basedir}/foo").content).not_to match 'caller has replace false set run 1' expect(file("#{@basedir}/foo").content).to match 'caller has replace false set run 2' end end # should replace its own fragment files even when caller has File(replace=>false) set end diff --git a/spec/acceptance/newline_spec.rb b/spec/acceptance/newline_spec.rb index 7a8bfdf..28b0e9f 100644 --- a/spec/acceptance/newline_spec.rb +++ b/spec/acceptance/newline_spec.rb @@ -1,54 +1,56 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'concat ensure_newline parameter' do before(:all) do @basedir = setup_test_directory end describe 'when false' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': ensure_newline => false, } concat::fragment { '1': target => '#{@basedir}/file', content => '1', } concat::fragment { '2': target => '#{@basedir}/file', content => '2', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match '12' end end describe 'when true' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': ensure_newline => true, } concat::fragment { '1': target => '#{@basedir}/file', content => '1', } concat::fragment { '2': target => '#{@basedir}/file', content => '2', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match %r{1\r?\n2\r?\n} end end end diff --git a/spec/acceptance/noop_spec.rb b/spec/acceptance/noop_spec.rb index 3432ae7..bdd913f 100644 --- a/spec/acceptance/noop_spec.rb +++ b/spec/acceptance/noop_spec.rb @@ -1,26 +1,28 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'concat noop parameter', if: ['debian', 'redhat', 'ubuntu'].include?(os[:family]) do before(:all) do @basedir = setup_test_directory end describe 'with "/usr/bin/test -e %"' do let(:pp) do <<-MANIFEST concat_file { '#{@basedir}/file': noop => false, } concat_fragment { 'content': target => '#{@basedir}/file', content => 'content', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to contain 'content' end end end diff --git a/spec/acceptance/order_spec.rb b/spec/acceptance/order_spec.rb index 307502b..fa0e1c7 100644 --- a/spec/acceptance/order_spec.rb +++ b/spec/acceptance/order_spec.rb @@ -1,66 +1,68 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'concat order' do before(:all) do @basedir = setup_test_directory end describe 'sortby alpha' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/foo': order => 'alpha' } concat::fragment { '1': target => '#{@basedir}/foo', content => 'string1', order => '1', } concat::fragment { '2': target => '#{@basedir}/foo', content => 'string2', order => '2', } concat::fragment { '10': target => '#{@basedir}/foo', content => 'string10', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/foo")).to be_file expect(file("#{@basedir}/foo").content).to match %r{string1string10string2} end end describe 'sortby numeric' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/foo': order => 'numeric' } concat::fragment { '1': target => '#{@basedir}/foo', content => 'string1', order => '1', } concat::fragment { '2': target => '#{@basedir}/foo', content => 'string2', order => '2', } concat::fragment { '10': target => '#{@basedir}/foo', content => 'string10', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/foo")).to be_file expect(file("#{@basedir}/foo").content).to match %r{string1string2string10} end end end diff --git a/spec/acceptance/pup_1963_spec.rb b/spec/acceptance/pup_1963_spec.rb index c42e3fc..b34d131 100644 --- a/spec/acceptance/pup_1963_spec.rb +++ b/spec/acceptance/pup_1963_spec.rb @@ -1,70 +1,72 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' command = case os[:family] when 'windows' 'cmd.exe /c echo triggered' else 'echo triggered' end describe 'with metaparameters' do before(:each) do @basedir = setup_test_directory end describe 'with subscribed resources' do let(:pp) do <<-MANIFEST concat { "foobar": ensure => 'present', path => '#{@basedir}/foobar', } concat::fragment { 'foo': target => 'foobar', content => 'foo', } exec { 'trigger': path => $::path, command => "#{command}", subscribe => Concat['foobar'], refreshonly => true, } MANIFEST end it 'applies the manifest twice with no changes second apply' do expect(apply_manifest(pp, catch_failures: true).stdout).to match(%r{Triggered 'refresh'}) expect(apply_manifest(pp, catch_changes: true).stdout).not_to match(%r{Triggered 'refresh'}) end end describe 'with resources to notify' do let(:pp) do <<-MANIFEST exec { 'trigger': path => $::path, command => "#{command}", refreshonly => true, } concat { "foobar": ensure => 'present', path => '#{@basedir}/foobar', notify => Exec['trigger'], } concat::fragment { 'foo': target => 'foobar', content => 'foo', } MANIFEST end it 'applies the manifest twice with no changes second apply' do expect(apply_manifest(pp, catch_failures: true).stdout).to match(%r{Triggered 'refresh'}) expect(apply_manifest(pp, catch_changes: true).stdout).not_to match(%r{Triggered 'refresh'}) end end end diff --git a/spec/acceptance/quoted_paths_spec.rb b/spec/acceptance/quoted_paths_spec.rb index e331c08..f4440bc 100644 --- a/spec/acceptance/quoted_paths_spec.rb +++ b/spec/acceptance/quoted_paths_spec.rb @@ -1,33 +1,35 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'quoted paths' do before(:all) do @basedir = setup_test_directory end describe 'with path with blanks' do let(:pp) do <<-MANIFEST file { '#{@basedir}/concat test': ensure => directory, } concat { '#{@basedir}/concat test/foo': } concat::fragment { '1': target => '#{@basedir}/concat test/foo', content => 'string1', } concat::fragment { '2': target => '#{@basedir}/concat test/foo', content => 'string2', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/concat test/foo")).to be_file expect(file("#{@basedir}/concat test/foo").content).to match %r{string1string2} end end end diff --git a/spec/acceptance/specinfra_stubs.rb b/spec/acceptance/specinfra_stubs.rb index 94622ca..a470dd8 100644 --- a/spec/acceptance/specinfra_stubs.rb +++ b/spec/acceptance/specinfra_stubs.rb @@ -1,19 +1,21 @@ +# frozen_string_literal: true + # class Specinfra::Command::Windows::Base::File class Specinfra::Command::Windows::Base::File < Specinfra::Command::Windows::Base class << self def check_is_owned_by(file, owner) Backend::PowerShell::Command.new do exec "if((Get-Item '#{file}').GetAccessControl().Owner -match '#{owner}' -or ((Get-Item '#{file}').GetAccessControl().Owner -match '#{owner}').Length -gt 0){ exit 0 } else { exit 1 }" end end end end # class Specinfra::Command::Base::File class Specinfra::Command::Base::File < Specinfra::Command::Base class << self def get_content(file) "cat '#{file}' 2> /dev/null || echo -n" end end end diff --git a/spec/acceptance/symbolic_name_spec.rb b/spec/acceptance/symbolic_name_spec.rb index 1a55a84..6684ad3 100644 --- a/spec/acceptance/symbolic_name_spec.rb +++ b/spec/acceptance/symbolic_name_spec.rb @@ -1,34 +1,36 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'symbolic name' do before(:all) do @basedir = setup_test_directory end let(:pp) do <<-MANIFEST concat { 'not_abs_path': path => '#{@basedir}/file', } concat::fragment { '1': target => 'not_abs_path', content => '1', order => '01', } concat::fragment { '2': target => 'not_abs_path', content => '2', order => '02', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match '1' expect(file("#{@basedir}/file").content).to match '2' end end diff --git a/spec/acceptance/validation_spec.rb b/spec/acceptance/validation_spec.rb index 7a3641a..3d08c16 100644 --- a/spec/acceptance/validation_spec.rb +++ b/spec/acceptance/validation_spec.rb @@ -1,27 +1,29 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'validation, concat validate_cmd parameter', if: ['debian', 'redhat', 'ubuntu'].include?(os[:family]) do before(:all) do @basedir = setup_test_directory end context 'with "/usr/bin/test -e %"' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': validate_cmd => '/usr/bin/test -e %', } concat::fragment { 'content': target => '#{@basedir}/file', content => 'content', } MANIFEST end it 'applies the manifest twice with no stderr' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to contain 'content' end end end diff --git a/spec/acceptance/warn_header_spec.rb b/spec/acceptance/warn_header_spec.rb index 84894b4..54deb58 100644 --- a/spec/acceptance/warn_header_spec.rb +++ b/spec/acceptance/warn_header_spec.rb @@ -1,84 +1,86 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'concat warn_header =>' do before(:all) do @basedir = setup_test_directory end describe 'applies the manifest twice with no stderr' do let(:pp) do <<-MANIFEST concat { '#{@basedir}/file': warn => true, } concat::fragment { '1': target => '#{@basedir}/file', content => '1', order => '01', } concat::fragment { '2': target => '#{@basedir}/file', content => '2', order => '02', } concat { '#{@basedir}/file2': warn => false, } concat::fragment { 'file2_1': target => '#{@basedir}/file2', content => '1', order => '01', } concat::fragment { 'file2_2': target => '#{@basedir}/file2', content => '2', order => '02', } concat { '#{@basedir}/file3': warn => "# foo\n", } concat::fragment { 'file3_1': target => '#{@basedir}/file3', content => '1', order => '01', } concat::fragment { 'file3_2': target => '#{@basedir}/file3', content => '2', order => '02', } MANIFEST end it 'when true should enable default warning message' do idempotent_apply(pp) expect(file("#{@basedir}/file")).to be_file expect(file("#{@basedir}/file").content).to match %r{# This file is managed by Puppet\. DO NOT EDIT\.} expect(file("#{@basedir}/file").content).to match %r{1} expect(file("#{@basedir}/file").content).to match %r{2} end it 'when false should not enable default warning message' do expect(file("#{@basedir}/file2")).to be_file expect(file("#{@basedir}/file2").content).not_to match %r{# This file is managed by Puppet\. DO NOT EDIT\.} expect(file("#{@basedir}/file2").content).to match %r{1} expect(file("#{@basedir}/file2").content).to match %r{2} end it 'when foo should overide default warning message' do expect(file("#{@basedir}/file3")).to be_file expect(file("#{@basedir}/file3").content).to match %r{# foo} expect(file("#{@basedir}/file3").content).to match %r{1} expect(file("#{@basedir}/file3").content).to match %r{2} end end end diff --git a/spec/acceptance/warnings_spec.rb b/spec/acceptance/warnings_spec.rb index 46b984d..d25180e 100644 --- a/spec/acceptance/warnings_spec.rb +++ b/spec/acceptance/warnings_spec.rb @@ -1,26 +1,28 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'warnings' do before(:all) do @basedir = setup_test_directory end context 'when concat::fragment target not found' do let(:pp) do <<-MANIFEST concat { 'file': path => '#{@basedir}/file', } concat::fragment { 'foo': target => '#{@basedir}/bar', content => 'bar', } MANIFEST end it 'applies manifests, check stderr' do expect(apply_manifest(pp, catch_failures: true).stderr).to match 'not found in the catalog' expect(apply_manifest(pp, catch_failures: true).stderr).to match 'not found in the catalog' end end end diff --git a/spec/defines/concat_fragment_spec.rb b/spec/defines/concat_fragment_spec.rb index 71c7d06..85a90f3 100644 --- a/spec/defines/concat_fragment_spec.rb +++ b/spec/defines/concat_fragment_spec.rb @@ -1,156 +1,158 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'concat::fragment' do shared_examples 'fragment' do |title, params| params = {} if params.nil? p = { content: nil, source: nil, order: 10, }.merge(params) let(:title) { title } let(:params) { params } let(:pre_condition) do "concat{ '#{p[:target]}': }" end it do is_expected.to contain_concat(p[:target]) end it do is_expected.to contain_concat_file(p[:target]) end it do is_expected.to contain_concat_fragment(title) end end context 'when title' do ['0', '1', 'a', 'z'].each do |title| it_behaves_like 'fragment', title, target: '/etc/motd', content: "content for #{title}" end end # title context 'when target =>' do ['./etc/motd', 'etc/motd', 'motd_header'].each do |target| context target do it_behaves_like 'fragment', target, target: '/etc/motd', content: "content for #{target}" end end context 'when false' do let(:title) { 'motd_header' } let(:params) { { target: false } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{parameter 'target' expects a .*String.*}) end end end # target => context 'when content =>' do ['', 'ashp is our hero'].each do |content| context content do it_behaves_like 'fragment', 'motd_header', content: content, target: '/etc/motd' end end context 'when false' do let(:title) { 'motd_header' } let(:params) { { content: false, target: '/etc/motd' } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{expects a value of type Undef( or String|, String, or Deferred), got Boolean}) end end end # content => context 'when source =>' do ['', '/foo/bar', ['/foo/bar', '/foo/baz']].each do |source| context source do it_behaves_like 'fragment', 'motd_header', source: source, target: '/etc/motd' end end context 'when false' do let(:title) { 'motd_header' } let(:params) { { source: false, target: '/etc/motd' } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{parameter 'source' expects a .*String.*Array.*}) end end end # source => context 'when order =>' do ['', '42', 'a', 'z'].each do |order| context "'#{order}'" do it_behaves_like 'fragment', 'motd_header', order: order, target: '/etc/motd' end end context 'when false' do let(:title) { 'motd_header' } let(:params) { { order: false, target: '/etc/motd' } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{Evaluation Error.*expects.*Boolean.*}) end end context 'when 123:456' do let(:title) { 'motd_header' } let(:params) { { order: '123:456', target: '/etc/motd' } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{cannot contain}) end end context 'when 23/456' do let(:title) { 'motd_header' } let(:params) { { order: '123/456', target: '/etc/motd' } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{cannot contain}) end end context 'when 123\n456' do let(:title) { 'motd_header' } let(:params) { { order: "123\n456", target: '/etc/motd' } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{cannot contain}) end end end # order => context 'with more than one content source' do context 'with source and content' do let(:title) { 'motd_header' } let(:params) do { target: '/etc/motd', source: '/foo', content: 'bar', } end it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{Can\'t use \'source\' and \'content\' at the same time}m) end end end # more than one content source end diff --git a/spec/defines/concat_spec.rb b/spec/defines/concat_spec.rb index 4adcd01..840cd44 100644 --- a/spec/defines/concat_spec.rb +++ b/spec/defines/concat_spec.rb @@ -1,402 +1,404 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'concat' do shared_examples 'concat' do |title, params, id| params = {} if params.nil? id = 'root' if id.nil? # default param values p = { ensure: 'present', path: title, owner: nil, group: nil, mode: '0644', warn: false, backup: 'puppet', replace: true, force: false, }.merge(params) file_defaults = { backup: p[:backup], } present_expect = { ensure: 'present', owner: p[:owner], group: p[:group], mode: p[:mode], path: p[:path], backup: p[:backup], replace: p[:replace], selinux_ignore_defaults: p[:selinux_ignore_defaults], selrange: p[:selrange], selrole: p[:selrole], seltype: p[:seltype], seluser: p[:seluser], force: p[:force], } let(:title) { title } let(:params) { params } let(:facts) do { id: id, osfamily: 'Debian', path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', kernel: 'Linux', is_pe: false, } end if p[:ensure] == 'present' it do is_expected.to contain_concat(title).with(file_defaults.merge(present_expect)) end else it do is_expected.to contain_concat(title).with(file_defaults.merge(ensure: 'absent', backup: p[:backup])) end end end context 'when title without path param' do # title/name is the default value for the path param. therefore, the # title must be an absolute path unless path is specified ['/foo', '/foo/bar', '/foo/bar/baz'].each do |title| context title do it_behaves_like 'concat', '/etc/foo.bar' end end ['./foo', 'foo', 'foo/bar'].each do |title| context title do let(:title) { title } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{Stdlib::Unixpath}) end end end end context 'when title with path param' do ['/foo', 'foo', 'foo/bar'].each do |title| context title do it_behaves_like 'concat', title, path: '/etc/foo.bar' end end end context 'when title with special characters in title' do ['foo:bar', 'foo*bar', 'foo(bar)', 'foo@bar'].each do |title| context title do it_behaves_like 'concat', title, path: '/etc/foo.bar' end end end context 'when as non-root user' do it_behaves_like 'concat', '/etc/foo.bar', {}, 'bob' end context 'when ensure =>' do ['present', 'absent'].each do |ens| context ens do it_behaves_like 'concat', '/etc/foo.bar', ensure: ens end end context 'when invalid' do let(:title) { '/etc/foo.bar' } let(:params) { { ensure: 'invalid' } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{expects a match for Enum\['absent', 'present'\]}) end end end # ensure => context 'when path =>' do context 'when /foo' do it_behaves_like 'concat', '/etc/foo.bar', path: '/foo' end context 'when false' do let(:title) { '/etc/foo.bar' } let(:params) { { path: false } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{Stdlib::Unixpath}) end end ['./foo', 'foo', 'foo/bar'].each do |path| context path do let(:title) { '/etc/foo.bar' } let(:params) { { path: path } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{Stdlib::Unixpath}) end end end end # path => context 'when owner =>' do ['apenney', 1000, '1001'].each do |owner| context owner do it_behaves_like 'concat', '/etc/foo.bar', owner: owner end end context 'when false' do let(:title) { '/etc/foo.bar' } let(:params) { { owner: false } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{Evaluation Error.*expects.*String.*Boolean.*}) end end end # owner => context 'when group =>' do ['apenney', 1000, '1001'].each do |group| context group do it_behaves_like 'concat', '/etc/foo.bar', group: group end end context 'when false' do let(:title) { '/etc/foo.bar' } let(:params) { { group: false } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{Evaluation Error.*expects.*String.*Boolean.*}) end end end # group => context 'when mode =>' do context 'when 1755' do it_behaves_like 'concat', '/etc/foo.bar', mode: '1755' end context 'when false' do let(:title) { '/etc/foo.bar' } let(:params) { { mode: false } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{parameter 'mode' expects .*String.*}) end end end # mode => context 'when warn =>' do [true, false, '# foo'].each do |warn| context warn do it_behaves_like 'concat', '/etc/foo.bar', warn: warn end end context 'when (stringified boolean)' do ['true', 'yes', 'on', 'false', 'no', 'off'].each do |warn| define warn do it_behaves_like 'concat', '/etc/foo.bar', warn: warn it 'creates a warning' do skip('rspec-puppet support for testing warning()') end end end end context 'when 123' do let(:title) { '/etc/foo.bar' } let(:params) { { warn: 123 } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{parameter 'warn' expects .*Boolean.*String.*}) end end end # warn => context 'when show_diff =>' do [true, false].each do |show_diff| context show_diff do it_behaves_like 'concat', '/etc/foo.bar', show_diff: show_diff end end context 'when 123' do let(:title) { '/etc/foo.bar' } let(:params) { { show_diff: 123 } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{parameter 'show_diff' expects .*Boolean.*}) end end end # show_diff => context 'when backup =>' do ['reverse', false, true].each do |backup| context backup.to_s do it_behaves_like 'concat', '/etc/foo.bar', backup: backup end end context 'when true' do let(:title) { '/etc/foo.bar' } let(:params) { { backup: [] } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{parameter 'backup' expects .*Boolean.*String.*}) end end end # backup => context 'when replace =>' do [true, false].each do |replace| context replace do it_behaves_like 'concat', '/etc/foo.bar', replace: replace end end context 'when 123' do let(:title) { '/etc/foo.bar' } let(:params) { { replace: 123 } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{parameter 'replace' expects .*Boolean.*}) end end end # replace => context 'when force =>' do [true, false].each do |force| context force do it_behaves_like 'concat', '/etc/foo.bar', force: force end end context 'when 123' do let(:title) { '/etc/foo.bar' } let(:params) { { force: 123 } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{parameter 'force' expects .*Boolean.*}) end end end # force => context 'when order =>' do ['alpha', 'numeric'].each do |order| context order do it_behaves_like 'concat', '/etc/foo.bar', order: order end end context 'when invalid' do let(:title) { '/etc/foo.bar' } let(:params) { { order: 'invalid' } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{expects a match for Enum\['alpha', 'numeric'\]}) end end end # order => context 'when ensure_newline =>' do [true, false].each do |ensure_newline| context 'when true' do it_behaves_like 'concat', '/etc/foo.bar', ensure_newline: ensure_newline end end context 'when 123' do let(:title) { '/etc/foo.bar' } let(:params) { { ensure_newline: 123 } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{parameter 'ensure_newline' expects a Boolean value}) end end end # ensure_newline => context 'when validate_cmd =>' do context 'when /usr/bin/test -e %' do it_behaves_like 'concat', '/etc/foo.bar', validate_cmd: '/usr/bin/test -e %' end [1234, true].each do |cmd| context cmd do let(:title) { '/etc/foo.bar' } let(:params) { { validate_cmd: cmd } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{parameter 'validate_cmd' expects.*String.*}) end end end end # validate_cmd => context 'when selinux_ignore_defaults =>' do let(:title) { '/etc/foo.bar' } [true, false].each do |v| context v do it_behaves_like 'concat', '/etc/foo.bar', selinux_ignore_defaults: v end end context 'when 123' do let(:title) { '/etc/foo.bar' } let(:params) { { selinux_ignore_defaults: 123 } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{Evaluation Error.*expects.*Boolean.*}) end end end # selinux_ignore_defaults => [ :selrange, :selrole, :seltype, :seluser, ].each do |p| context " #{p} =>" do let(:title) { '/etc/foo.bar' } context 'when foo' do it_behaves_like 'concat', '/etc/foo.bar', p => 'foo' end context 'when false' do let(:title) { '/etc/foo.bar' } let(:params) { { p => false } } it 'fails' do expect { catalogue }.to raise_error(Puppet::Error, %r{parameter '#{p}' expects.*String.*}) end end end # #{p} => end end diff --git a/spec/spec_helper_local.rb b/spec/spec_helper_local.rb index fc29827..abac766 100644 --- a/spec/spec_helper_local.rb +++ b/spec/spec_helper_local.rb @@ -1,75 +1,77 @@ +# frozen_string_literal: true + if ENV['COVERAGE'] == 'yes' require 'simplecov' require 'simplecov-console' require 'codecov' SimpleCov.formatters = [ SimpleCov::Formatter::HTMLFormatter, SimpleCov::Formatter::Console, SimpleCov::Formatter::Codecov, ] SimpleCov.start do track_files 'lib/**/*.rb' add_filter '/spec' # do not track vendored files add_filter '/vendor' add_filter '/.vendor' # do not track gitignored files # this adds about 4 seconds to the coverage check # this could definitely be optimized add_filter do |f| # system returns true if exit status is 0, which with git-check-ignore means file is ignored system("git check-ignore --quiet #{f.filename}") end end end shared_examples 'Puppet::Parameter::Boolean' do |parameter| [true, :true, 'true', :yes, 'yes'].each do |value| it "accepts #{value} (#{value.class}) as a value" do resource[parameter] = value expect(resource[parameter]).to eq(true) end end [false, :false, 'false', :no, 'no'].each do |value| it "accepts #{value} (#{value.class}) as a value" do resource[parameter] = value expect(resource[parameter]).to eq(false) end end it 'does not accept "foo" as a value' do expect { resource[parameter] = 'foo' }.to raise_error(%r{Invalid value "foo"}) end end shared_examples 'a parameter that accepts only string values' do |parameter| it 'accepts a string value' do resource[parameter] = 'foo' expect(resource[parameter]).to eq('foo') end it 'does not accept an array value' do expect { resource[parameter] = ['foo', 'bar'] }.to raise_error(%r{must be a String}) end it 'does not accept a hash value' do expect { resource[parameter] = { foo: 'bar' } }.to raise_error(%r{must be a String}) end it 'does not accept an integer value' do expect { resource[parameter] = 9001 }.to raise_error(%r{must be a String}) end it 'does not accept a boolean true value' do expect { resource[parameter] = true }.to raise_error(%r{must be a String}) end it 'does not accept a boolean false value' do expect { resource[parameter] = false }.to raise_error(%r{must be a String}) end end diff --git a/spec/unit/type/concat_file_spec.rb b/spec/unit/type/concat_file_spec.rb index 892ff4f..6df9177 100644 --- a/spec/unit/type/concat_file_spec.rb +++ b/spec/unit/type/concat_file_spec.rb @@ -1,150 +1,152 @@ +# frozen_string_literal: true + require 'spec_helper' describe Puppet::Type.type(:concat_file) do let(:resource) { described_class.new(name: '/foo/bar') } describe 'key attributes' do let(:subject) { described_class.key_attributes } it 'contain only :path' do is_expected.to eq([:path]) end end describe 'parameter :path' do it 'does not accept unqualified paths' do expect { resource[:path] = 'foo' }.to raise_error( %r{File paths must be fully qualified}, ) end end describe 'parameter :owner' do subject { described_class.attrclass(:owner) } it 'inherits Puppet::Type::File::Owner' do is_expected.to be < Puppet::Type::File::Owner end end describe 'parameter :group' do subject { described_class.attrclass(:group) } it 'inherits Puppet::Type::File::Group' do is_expected.to be < Puppet::Type::File::Group end end describe 'parameter :mode' do subject { described_class.attrclass(:mode) } it 'inherits Puppet::Type::File::Mode' do is_expected.to be < Puppet::Type::File::Mode end end describe 'parameter :order' do it 'accepts "alpha" as a value' do resource[:order] = 'alpha' expect(resource[:order]).to eq(:alpha) end it 'accepts "numeric" as a value' do resource[:order] = 'numeric' expect(resource[:order]).to eq(:numeric) end it 'does not accept "bar" as a value' do expect { resource[:order] = 'bar' }.to raise_error(%r{Invalid value "bar"}) end end describe 'parameter :backup' do it 'accepts true (TrueClass) as a value' do resource[:backup] = true expect(resource[:backup]).to eq(true) end it 'accepts false (FalseClass) as a value' do resource[:backup] = false expect(resource[:backup]).to eq(false) end it 'accepts "foo" as a value' do resource[:backup] = 'foo' expect(resource[:backup]).to eq('foo') end end describe 'parameter :selrange' do it_behaves_like 'a parameter that accepts only string values', :selrange end describe 'parameter :selrole' do it_behaves_like 'a parameter that accepts only string values', :selrole end describe 'parameter :seltype' do it_behaves_like 'a parameter that accepts only string values', :seltype end describe 'parameter :seluser' do it_behaves_like 'a parameter that accepts only string values', :seluser end describe 'parameter :replace' do it_behaves_like 'Puppet::Parameter::Boolean', :replace end describe 'parameter :ensure_newline' do it_behaves_like 'Puppet::Parameter::Boolean', :ensure_newline end describe 'parameter :show_diff' do it_behaves_like 'Puppet::Parameter::Boolean', :show_diff end describe 'parameter :selinux_ignore_defaults' do it_behaves_like 'Puppet::Parameter::Boolean', :selinux_ignore_defaults end describe 'parameter :force' do it_behaves_like 'Puppet::Parameter::Boolean', :force end describe 'parameter :format' do it 'accepts "plain" as a value' do resource[:format] = 'plain' expect(resource[:format]).to eq(:plain) end it 'accepts "yaml" as a value' do resource[:format] = 'yaml' expect(resource[:format]).to eq(:yaml) end it 'accepts "json" as a value' do resource[:format] = 'json' expect(resource[:format]).to eq(:json) end it 'accepts "json-array" as a value' do resource[:format] = 'json-array' expect(resource[:format]).to eq(:'json-array') end it 'accepts "json-pretty" as a value' do resource[:format] = 'json-pretty' expect(resource[:format]).to eq(:'json-pretty') end it 'accepts "json-array-pretty" as a value' do resource[:format] = 'json-array-pretty' expect(resource[:format]).to eq(:'json-array-pretty') end it 'does not accept "bar" as a value' do expect { resource[:format] = 'bar' }.to raise_error(%r{Invalid value "bar"}) end end end diff --git a/spec/unit/type/concat_fragment_spec.rb b/spec/unit/type/concat_fragment_spec.rb index 5a2700c..991472d 100644 --- a/spec/unit/type/concat_fragment_spec.rb +++ b/spec/unit/type/concat_fragment_spec.rb @@ -1,111 +1,113 @@ +# frozen_string_literal: true + require 'spec_helper' describe Puppet::Type.type(:concat_fragment) do let(:resource) do described_class.new(name: 'foo', target: 'bar', content: 'baz') end describe 'key attributes' do let(:subject) { described_class.key_attributes } it 'contain only :name' do is_expected.to eq([:name]) end end describe 'parameter :target' do it_behaves_like 'a parameter that accepts only string values', :target end describe 'parameter :content' do it_behaves_like 'a parameter that accepts only string values', :content end describe 'parameter :source' do it 'accepts a string value' do resource[:source] = 'foo' expect(resource[:source]).to eq('foo') end it 'accepts an array value' do resource[:source] = ['foo', 'bar'] expect(resource[:source]).to eq(['foo', 'bar']) end it 'does not accept a hash value' do expect { resource[:source] = { foo: 'bar' } }.to raise_error(%r{must be a String or Array}) end it 'does not accept an integer value' do expect { resource[:source] = 9001 }.to raise_error(%r{must be a String or Array}) end it 'does not accept a boolean true value' do expect { resource[:source] = true }.to raise_error(%r{must be a String or Array}) end it 'does not accept a boolean false value' do expect { resource[:source] = false }.to raise_error(%r{must be a String or Array}) end end describe 'parameter :order' do it 'accepts a string value' do resource[:order] = 'foo' expect(resource[:order]).to eq('foo') end it 'accepts an integer value' do resource[:order] = 9001 expect(resource[:order]).to eq(9001) end it 'does not accept an array value' do expect { resource[:order] = ['foo', 'bar'] }.to raise_error(%r{is not a string or integer}) end it 'does not accept a hash value' do expect { resource[:order] = { foo: 'bar' } }.to raise_error(%r{is not a string or integer}) end it 'does not accept a boolean true value' do expect { resource[:order] = true }.to raise_error(%r{is not a string or integer}) end it 'does not accept a boolean false value' do expect { resource[:order] = false }.to raise_error(%r{is not a string or integer}) end it 'does not accept a string with ":" in it/' do expect { resource[:order] = ':foo' }.to raise_error(%r{Order cannot contain}) end it 'does not accept a string with "\n" in it/' do expect { resource[:order] = "\nfoo" }.to raise_error(%r{Order cannot contain}) end it 'does not accept a string with "/" in it/' do expect { resource[:order] = '/foo' }.to raise_error(%r{Order cannot contain}) end end context 'without a value set for :target or :tag' do it 'throws an error' do expect { described_class.new(name: 'foo', content: 'baz') }.to raise_error(%r{No 'target' or 'tag' set}) end end context 'without a value set for both :content and :source' do it 'throws an error' do expect { described_class.new(name: 'foo', target: 'bar') }.to raise_error(%r{Set either 'source' or 'content'}) end end context 'with a value set for both :content and :source' do it 'throws an error' do expect { described_class.new(name: 'foo', target: 'bar', content: 'baz', source: 'qux') }.to raise_error(%r{Can't use 'source' and 'content' at the same time}) end end end