1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
|
require 'vcr'
require 'multi_json'
module VCRHelpers
def normalize_cassette_hash(cassette_hash)
cassette_hash['recorded_with'] = "VCR #{VCR.version}"
cassette_hash['http_interactions'].map! { |h| normalize_http_interaction(h) }
cassette_hash
end
def normalize_headers(object)
object.headers = {} and return if object.headers.nil?
object.headers = {}.tap do |hash|
object.headers.each do |key, value|
hash[key.downcase] = value
end
end
end
def static_timestamp
@static_timestamp ||= Time.now
end
def normalize_http_interaction(hash)
VCR::HTTPInteraction.from_hash(hash).tap do |i|
normalize_headers(i.request)
normalize_headers(i.response)
i.recorded_at &&= static_timestamp
i.request.body ||= ''
i.response.body ||= ''
i.response.status.message ||= ''
i.response.adapter_metadata.clear
# Remove non-deterministic headers and headers
# that get added by a particular HTTP library (but not by others)
i.response.headers.reject! { |k, v| %w[ server date connection ].include?(k) }
i.request.headers.reject! { |k, v| %w[ accept user-agent connection expect date ].include?(k) }
# Some HTTP libraries include an extra space ("OK " instead of "OK")
i.response.status.message = i.response.status.message.strip
if @scenario_parameters.to_s =~ /excon|faraday/
# Excon/Faraday do not expose the status message or http version,
# so we have no way to record these attributes.
i.response.status.message = nil
i.response.http_version = nil
elsif @scenario_parameters.to_s.include?('webmock')
# WebMock does not expose the HTTP version so we have no way to record it
i.response.http_version = nil
end
end
end
def normalize_cassette_content(content)
return content unless @scenario_parameters.to_s.include?('patron')
cassette_hash = YAML.load(content)
cassette_hash['http_interactions'].map! do |hash|
VCR::HTTPInteraction.from_hash(hash).tap do |i|
i.request.headers = (i.request.headers || {}).merge!('Expect' => [''])
end.to_hash
end
YAML.dump(cassette_hash)
end
def modify_file(file_name, orig_text, new_text)
in_current_dir do
file = File.read(file_name)
regex = /#{Regexp.escape(orig_text)}/
expect(file).to match(regex)
file = file.gsub(regex, new_text)
File.open(file_name, 'w') { |f| f.write(file) }
end
end
end
World(VCRHelpers)
Given(/the following files do not exist:/) do |files|
check_file_presence(files.raw.map{|file_row| file_row[0]}, false)
end
Given(/^the directory "([^"]*)" does not exist$/) do |dir|
check_directory_presence([dir], false)
end
Given(/^a previously recorded cassette file "([^"]*)" with:$/) do |file_name, content|
write_file(file_name, normalize_cassette_content(content))
end
Given(/^it is (.*)$/) do |date_string|
set_env('DATE_STRING', date_string)
end
Given(/^that port numbers in "([^"]*)" are normalized to "([^"]*)"$/) do |file_name, port|
in_current_dir do
contents = File.read(file_name)
contents = contents.gsub(/:\d{2,}\//, ":#{port}/")
File.open(file_name, 'w') { |f| f.write(contents) }
end
end
When(/^I modify the file "([^"]*)" to replace "([^"]*)" with "([^"]*)"$/) do |file_name, orig_text, new_text|
modify_file(file_name, orig_text, new_text)
end
When(/^I append to file "([^"]*)":$/) do |file_name, content|
append_to_file(file_name, "\n" + content)
end
When(/^I set the "([^"]*)" environment variable to "([^"]*)"$/) do |var, value|
set_env(var, value)
end
Then(/^the file "([^"]*)" should exist$/) do |file_name|
check_file_presence([file_name], true)
end
Then(/^it should (pass|fail) with "([^"]*)"$/) do |pass_fail, partial_output|
assert_exit_status_and_partial_output(pass_fail == 'pass', partial_output)
end
Then(/^it should (pass|fail) with an error like:$/) do |pass_fail, partial_output|
assert_success(pass_fail == 'pass')
# different implementations place the exception class at different
# places relative to the message (i.e. with a multiline error message)
process_output = all_output.gsub(/\s*\(VCR::Errors::\w+\)/, '')
# Some implementations include extra leading spaces, for some reason...
process_output.gsub!(/^\s*/, '')
partial_output.gsub!(/^\s*/, '')
assert_partial_output(partial_output, process_output)
end
Then(/^the output should contain each of the following:$/) do |table|
table.raw.flatten.each do |string|
assert_partial_output(string, all_output)
end
end
Then(/^the file "([^"]*)" should contain YAML like:$/) do |file_name, expected_content|
actual_content = in_current_dir { File.read(file_name) }
expect(normalize_cassette_hash(YAML.load(actual_content))).to eq(normalize_cassette_hash(YAML.load(expected_content.to_s)))
end
Then(/^the file "([^"]*)" should contain JSON like:$/) do |file_name, expected_content|
actual_content = in_current_dir { File.read(file_name) }
actual = MultiJson.decode(actual_content)
expected = MultiJson.decode(expected_content.to_s)
expect(normalize_cassette_hash(actual)).to eq(normalize_cassette_hash(expected))
end
Then(/^the file "([^"]*)" should contain compressed YAML like:$/) do |file_name, expected_content|
actual_content = in_current_dir { File.read(file_name) }
unzipped_content = Zlib::Inflate.inflate(actual_content)
expect(normalize_cassette_hash(YAML.load(unzipped_content))).to eq(normalize_cassette_hash(YAML.load(expected_content.to_s)))
end
Then(/^the file "([^"]*)" should contain ruby like:$/) do |file_name, expected_content|
actual_content = in_current_dir { File.read(file_name) }
actual = eval(actual_content)
expected = eval(expected_content)
expect(normalize_cassette_hash(actual)).to eq(normalize_cassette_hash(expected))
end
Then(/^the file "([^"]*)" should contain each of these:$/) do |file_name, table|
table.raw.flatten.each do |string|
check_file_content(file_name, string, true)
end
end
Then(/^the file "([^"]*)" should contain a YAML fragment like:$/) do |file_name, fragment|
in_current_dir do
file_content = File.read(file_name)
# Normalize by removing leading and trailing whitespace...
file_content = file_content.split("\n").map do |line|
# Different versions of psych use single vs. double quotes
# And then 2.1 sometimes adds quotes...
line.strip.gsub('"', "'").gsub("'", '')
end.join("\n")
expect(file_content).to include(fragment.gsub("'", ''))
end
end
Then(/^the cassette "([^"]*)" should have the following response bodies:$/) do |file, table|
interactions = in_current_dir { YAML.load_file(file) }['http_interactions'].map { |h| VCR::HTTPInteraction.from_hash(h) }
actual_response_bodies = interactions.map { |i| i.response.body }
expected_response_bodies = table.raw.flatten
expect(actual_response_bodies).to match(expected_response_bodies)
end
Then(/^it should (pass|fail)$/) do |pass_fail|
assert_success(pass_fail == 'pass')
end
|