diff --git a/lib/chefspec/matchers/resource_matcher.rb b/lib/chefspec/matchers/resource_matcher.rb index 25b188b9..d1402435 100644 --- a/lib/chefspec/matchers/resource_matcher.rb +++ b/lib/chefspec/matchers/resource_matcher.rb @@ -115,8 +115,12 @@ def unmatched_parameters def matches_parameter?(parameter, expected) value = safe_send(parameter) if parameter == :source - # Chef 11+ stores the source parameter internally as an Array - Array(expected) == Array(value) + # Chef may store the source parameter internally as an Array. Match the + # expectation against the value directly first, so RSpec matchers and + # Regexps work against a scalar source, then fall back to comparing the + # array-normalized forms so a plain string still matches a single-element + # array source. + expected === value || Array(expected) == Array(value) elsif expected.is_a?(Class) # Ruby can't compare classes with === expected == value diff --git a/spec/unit/matchers/resource_matcher_spec.rb b/spec/unit/matchers/resource_matcher_spec.rb index aec15441..9db99cb5 100644 --- a/spec/unit/matchers/resource_matcher_spec.rb +++ b/spec/unit/matchers/resource_matcher_spec.rb @@ -1,5 +1,38 @@ require "spec_helper" describe ChefSpec::Matchers::ResourceMatcher do - pending + subject { described_class.new(:windows_certificate, :create, "CN=example.com") } + + describe "matching the :source parameter" do + # `matches_parameter?` is the private comparison that `.with(source: ...)` + # relies on. Drive it directly with a stubbed resource value. + def source_matches?(expected, actual_source) + allow(subject).to receive(:resource).and_return(double(source: actual_source)) + subject.send(:matches_parameter?, :source, expected) + end + + it "matches an exact string source" do + expect(source_matches?("C:/MyFile.pem", "C:/MyFile.pem")).to be(true) + end + + it "matches a plain string against an array-valued source" do + expect(source_matches?("foo.erb", ["foo.erb"])).to be(true) + end + + it "matches an exact array source" do + expect(source_matches?(%w{a b}, %w{a b})).to be(true) + end + + it "matches an RSpec matcher against a scalar source (issue #980)" do + expect(source_matches?(end_with("MyFile.pem"), "C:/MyFile.pem")).to be(true) + end + + it "matches a Regexp against a scalar source" do + expect(source_matches?(/MyFile\.pem/, "C:/MyFile.pem")).to be(true) + end + + it "does not match when the matcher does not apply" do + expect(source_matches?(end_with("other.pem"), "C:/MyFile.pem")).to be(false) + end + end end