Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cassette specific match_requests_on modifications actually change global VCR.configuration.default_cassette_options #1006

Open
garrick-mindtrip opened this issue Dec 3, 2023 · 2 comments

Comments

@garrick-mindtrip
Copy link

This issue has been intermittently causing requests that are recorded in the cassette to not match. It turns out that one test attempting to add :body to the matching criteria for the current cassette actually ended up overwriting VCR.configuration.default_cassette_options[:match_requests_on]. This is because the way cassettes copy and merge default/global options down into the cassette specific options doesn't copy the array value, they end up pointing at the same array, so once a test modifies it, all other tests running in that process henceforth will be impacted.

Here's a test that reproduces (You don't actually need any cassettes sitting at /tmp):

require 'vcr'

VCR.use_cassette "/tmp/cassette-1" do
  # By default we match on uri, method, this is what is printed
  puts VCR.current_cassette.match_requests_on.join(", ")
end

VCR.use_cassette "/tmp/cassette-2" do
  # Here we add :body to the matching criteria, but only on the current cassette
  # Note the current cassette's array and the global array are the same based on object_id
  puts "Default/global match_requests_on array object_id: #{VCR.configuration.default_cassette_options[:match_requests_on].object_id}"
  puts "Current cassette match_requests_on array object_id: #{VCR.current_cassette.match_requests_on.object_id}"
  puts VCR.configuration.default_cassette_options[:match_requests_on].join(", ")
  VCR.current_cassette.match_requests_on.push(:body)
  puts VCR.configuration.default_cassette_options[:match_requests_on].join(", ")
end

VCR.use_cassette "/tmp/cassette-3" do
  # The attempt above to add body to cassette-2 matching pollutes cassette-3
  # and now cassette 3 is also matching on body
  puts VCR.current_cassette.match_requests_on.join(", ")
end

The workaround monkeypatch we came up with is this:

module VCRCassetteExtension
  def initialize(name, options={})
    super(name, VCR.configuration.default_cassette_options.transform_values(&:dup).merge(options))
  end
end
VCR::Cassette.prepend VCRCassetteExtension

Which would translate roughly to this diff

diff --git a/lib/vcr/cassette.rb b/lib/vcr/cassette.rb
index 6c171dc..d9d29a3 100644
--- a/lib/vcr/cassette.rb
+++ b/lib/vcr/cassette.rb
@@ -57,7 +57,7 @@ module VCR
     # @see VCR#insert_cassette
     def initialize(name, options = {})
       @name    = name
-      @options = VCR.configuration.default_cassette_options.merge(options)
+      @options = VCR.configuration.default_cassette_options.transform_values(&:dup).merge(options)
       @mutex   = Mutex.new
 
       assert_valid_options!

If there are cleaner/better ways to do it please LMK!

Ruby 3.2.2
Gem v6.2.0
HTTP n/a
Mock n/a
Rails n/a
Rspec n/a

@galori
Copy link
Collaborator

galori commented Dec 22, 2024

Does this patch make it so that if the configuration is done globally (say in a before(:suite) rspec hook), it takes effect globally, and if it is done for an individual cassette it doesn't?

(Otherwise it might surprise folks)

@garrick-mindtrip
Copy link
Author

I agree and that is the case. If you configure via VCR.configure {|config| config.default_cassette_options={...}} or moral equivalent that will be maintained henceforth. Let me know if you had a different thought in terms of a configuration path that would imply global/default but may not have the effect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants