Validating Cassette Migration Workflow in VCR
This test suite validates the VCR Cassette Migrator functionality, which handles the migration of VCR cassettes from version 1.x to 2.x format. The tests ensure proper handling of YAML formatting, data structures, and edge cases during the migration process.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
vcr/vcr
spec/lib/vcr/cassette/migrator_spec.rb
require 'tmpdir'
require 'vcr/cassette/migrator'
require 'yaml'
RSpec.describe VCR::Cassette::Migrator do
let(:original_contents) { <<-EOF
---
- !ruby/struct:VCR::HTTPInteraction
request: !ruby/struct:VCR::Request
method: :get
uri: http://example.com:80/foo
body:
headers:
response: !ruby/struct:VCR::Response
status: !ruby/struct:VCR::ResponseStatus
code: 200
message: OK
headers:
content-type:
- text/html;charset=utf-8
content-length:
- "9"
body: Hello foo
http_version: "1.1"
- !ruby/struct:VCR::HTTPInteraction
request: !ruby/struct:VCR::Request
method: :get
uri: http://localhost:7777/bar
body:
headers:
response: !ruby/struct:VCR::Response
status: !ruby/struct:VCR::ResponseStatus
code: 200
message: OK
headers:
content-type:
- text/html;charset=utf-8
content-length:
- "9"
body: Hello bar
http_version: "1.1"
EOF
}
let(:updated_contents) { <<-EOF
---
http_interactions:
- request:
method: get
uri: http://example.com/foo
body:
encoding: US-ASCII
string: ""
headers: {}
response:
status:
code: 200
message: OK
headers:
Content-Type:
- text/html;charset=utf-8
Content-Length:
- "9"
body:
encoding: UTF-8
string: Hello foo
http_version: "1.1"
recorded_at: Wed, 04 May 2011 12:30:00 GMT
- request:
method: get
uri: http://localhost:7777/bar
body:
encoding: US-ASCII
string: ""
headers: {}
response:
status:
code: 200
message: OK
headers:
Content-Type:
- text/html;charset=utf-8
Content-Length:
- "9"
body:
encoding: UTF-8
string: Hello bar
http_version: "1.1"
recorded_at: Wed, 04 May 2011 12:30:00 GMT
recorded_with: VCR #{VCR.version}
EOF
}
let(:dir) { './tmp/migrator' }
before(:each) do
# ensure the directory is empty
FileUtils.rm_rf dir
FileUtils.mkdir_p dir
end
before(:each) do
# the encoding won't be set on rubies that don't support it
updated_contents.gsub!(/^\s+encoding:.*$/, '')
end unless ''.respond_to?(:encoding)
# JRuby serializes YAML with some slightly different whitespace.
before(:each) do
[original_contents, updated_contents].each do |contents|
contents.gsub!(/^(\s+)-/, '\1 -')
end
updated_contents.gsub!(/^(- | )/, ' \1')
end if RUBY_PLATFORM == 'java'
let(:filemtime) { Time.utc(2011, 5, 4, 12, 30) }
let(:out_io) { StringIO.new }
let(:file_name) { File.join(dir, "example.yml") }
let(:output) { out_io.rewind; out_io.read }
subject { described_class.new(dir, out_io) }
before(:each) do
allow(File).to receive(:mtime).with(file_name).and_return(filemtime)
end
def load_file(file_name)
if ::YAML.respond_to?(:unsafe_load_file)
::YAML.unsafe_load_file(file_name)
else
::YAML.load_file(file_name)
end
end
it 'migrates a cassette from the 1.x to 2.x format' do
File.open(file_name, 'w') { |f| f.write(original_contents) }
subject.migrate!
expect(load_file(file_name)).to eq(YAML.load(updated_contents))
expect(output).to match(/Migrated example.yml/)
end
it 'ignores files that do not contain arrays' do
File.open(file_name, 'w') { |f| f.write(true.to_yaml) }
subject.migrate!
expect(File.read(file_name)).to eq(true.to_yaml)
expect(output).to match(/Ignored example.yml since it does not appear to be a valid VCR 1.x cassette/)
end
it 'ignores files that contain YAML arrays of other things' do
File.open(file_name, 'w') { |f| f.write([{}, {}].to_yaml) }
subject.migrate!
expect(File.read(file_name)).to eq([{}, {}].to_yaml)
expect(output).to match(/Ignored example.yml since it does not appear to be a valid VCR 1.x cassette/)
end
it 'ignores URIs that have sensitive data substitutions' do
modified_contents = original_contents.gsub('example.com', '<HOST>')
File.open(file_name, 'w') { |f| f.write(modified_contents) }
subject.migrate!
expect(load_file(file_name)).to eq(YAML.load(updated_contents.gsub('example.com', '<HOST>:80')))
end
it 'ignores files that are empty' do
File.open(file_name, 'w') { |f| f.write('') }
subject.migrate!
expect(File.read(file_name)).to eq('')
expect(output).to match(/Ignored example.yml since it could not be parsed as YAML/)
end
shared_examples_for "ignoring invalid YAML" do
it 'ignores files that cannot be parsed as valid YAML (such as ERB cassettes)' do
modified_contents = original_contents.gsub(/\A---/, "---\n<% 3.times do %>")
modified_contents = modified_contents.gsub(/\z/, "<% end %>")
File.open(file_name, 'w') { |f| f.write(modified_contents) }
subject.migrate!
expect(File.read(file_name)).to eq(modified_contents)
expect(output).to match(/Ignored example.yml since it could not be parsed as YAML/)
end
end
context 'with syck' do
it_behaves_like "ignoring invalid YAML"
end
context 'with psych' do
before(:each) do
YAML::ENGINE.yamler = 'psych'
end
it_behaves_like "ignoring invalid YAML"
end if defined?(YAML::ENGINE)
end