From 5c70b7c5d3bad994d00e676b69ce0b3a72f1b3ad Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Sat, 27 Jun 2026 22:02:07 -0700 Subject: [PATCH] Fix NameError from removed rspec-expectations diff class (#13) rspec-expectations renamed its private diff helper from ExpectedsForMultipleDiffs to MultiMatcherDiff in 3.12.4 and changed its signature (actual moved from message_with_diff into the from factory). A prior change removed the version pin and the broken require but left the call site referencing the old class, turning the original LoadError into a latent NameError that fires whenever a resource-parameter assertion renders a diff. Add a diff_for helper that detects which class is available and uses the matching API, so chefspec works across the full rspec ~> 3.0 range. Also drop the now-stale gemspec comment describing a pin that no longer exists, and replace the pending resource_matcher spec with a test covering the diff-rendering failure path. Signed-off-by: Tim Smith --- chefspec.gemspec | 3 --- lib/chefspec/matchers/resource_matcher.rb | 25 ++++++++++++++++--- spec/unit/matchers/resource_matcher_spec.rb | 27 ++++++++++++++++++++- 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/chefspec.gemspec b/chefspec.gemspec index b3719b92..9106902a 100644 --- a/chefspec.gemspec +++ b/chefspec.gemspec @@ -31,7 +31,4 @@ Gem::Specification.new do |s| # this needs to be remedied before Ruby 3.3 s.add_dependency "logger", "< 1.6" s.add_development_dependency "cookstyle", "~> 8.4" - - # temporary restriction to a version of rspec-expectations that includes the - # `RSpec::Matchers::ExpectedsForMultipleDiffs` class (renamed in 3.12.4) end diff --git a/lib/chefspec/matchers/resource_matcher.rb b/lib/chefspec/matchers/resource_matcher.rb index 25b188b9..c78d5cff 100644 --- a/lib/chefspec/matchers/resource_matcher.rb +++ b/lib/chefspec/matchers/resource_matcher.rb @@ -1,4 +1,3 @@ -# require "rspec/matchers/expecteds_for_multiple_diffs" This is now a private class and will throw errors when the require statement is executed require "rspec/expectations/fail_with" module ChefSpec::Matchers @@ -66,8 +65,7 @@ def failure_message "\n\n" \ " " + unmatched_parameters.collect { |parameter, h| msg = "#{parameter} #{h[:expected].inspect}, was #{h[:actual].inspect}" - diff = ::RSpec::Matchers::ExpectedsForMultipleDiffs.from(h[:expected]) \ - .message_with_diff(message, ::RSpec::Expectations.differ, h[:actual]) + diff = diff_for(h[:expected], h[:actual], message) msg += diff if diff msg }.join("\n ") @@ -95,6 +93,27 @@ def failure_message_when_negated private + # + # Build the diff text for a mismatched parameter using rspec-expectations' + # private diff helper. That helper was renamed and its signature changed in + # rspec-expectations 3.12.4 (`ExpectedsForMultipleDiffs.from(expected)` with + # `message_with_diff(message, differ, actual)` became + # `MultiMatcherDiff.from(expected, actual)` with + # `message_with_diff(message, differ)`), so support both shapes. + # + # @return [String, nil] + # + def diff_for(expected, actual, message) + differ = ::RSpec::Expectations.differ + if defined?(::RSpec::Matchers::MultiMatcherDiff) + ::RSpec::Matchers::MultiMatcherDiff.from(expected, actual) + .message_with_diff(message, differ) + else + ::RSpec::Matchers::ExpectedsForMultipleDiffs.from(expected) + .message_with_diff(message, differ, actual) + end + end + def unmatched_parameters return @_unmatched_parameters if @_unmatched_parameters diff --git a/spec/unit/matchers/resource_matcher_spec.rb b/spec/unit/matchers/resource_matcher_spec.rb index aec15441..3269e61c 100644 --- a/spec/unit/matchers/resource_matcher_spec.rb +++ b/spec/unit/matchers/resource_matcher_spec.rb @@ -1,5 +1,30 @@ require "spec_helper" describe ChefSpec::Matchers::ResourceMatcher do - pending + subject { described_class.new(:template, :create, "/etc/foo") } + + describe "#failure_message" do + context "when a resource is found but has unmatched parameters" do + before do + # Pretend we found a matching resource with a mismatched, multi-line + # parameter so that #failure_message renders a diff. This is the code + # path that depends on rspec-expectations' (private) diff helper class. + allow(subject).to receive(:resource).and_return("template[/etc/foo]") + allow(subject).to receive(:unmatched_parameters).and_return( + content: { expected: "line one\nline two\n", actual: "line one\nline three\n" } + ) + end + + it "renders a diff without raising" do + expect { subject.failure_message }.not_to raise_error + end + + it "includes the parameter name and a diff in the message" do + message = subject.failure_message + expect(message).to include("expected \"template[/etc/foo]\" to have parameters") + expect(message).to include("content") + expect(message).to include("line three") + end + end + end end