trabajo-afectivo/spec/support/vcr.rb

94 lines
3 KiB
Ruby
Raw Normal View History

VCR.configure do |config|
config.cassette_library_dir = 'test/data/vcr_cassettes'
config.hook_into :webmock
config.allow_http_connections_when_no_cassette = false
config.ignore_localhost = true
config.ignore_request do |request|
uri = URI(request.uri)
['zammad.com', 'google.com', 'elasticsearch', 'selenium'].any? do |site|
uri.host.include?(site)
end
end
2018-12-13 09:06:44 +00:00
config.register_request_matcher(:oauth_headers) do |r1, r2|
without_onetime_oauth_params = ->(params) { params.gsub(/oauth_(nonce|signature|timestamp)="[^"]+", /, '') }
r1.headers.except('Authorization') == r2.headers.except('Authorization') &&
r1.headers['Authorization']&.map(&without_onetime_oauth_params) ==
r2.headers['Authorization']&.map(&without_onetime_oauth_params)
end
end
Refactoring: Automatic RSpec VCR cassette name helper This commit was prepared to support upcoming additions to the test suite (specifically, better coverage for existing Twitter functionality). These upcoming changes will depend heavily on VCR.[0] (VCR is a Ruby gem that makes it easier to write and run tests that call out to external services over HTTP by "recording" HTTP transactions to a YAML file and "replaying" them later.) VCR is widely-used (4600 GitHub stars), but its API is a little clumsy-- You have to manually specify the name of a "cassette" file every time: it 'does something' do VCR.use_cassette('path/to/cassette') do ... end end This commit adds an RSpec metadata config option as a shorthand for the syntax above: it 'does something', :use_vcr do ... end This config option automatically generates a cassette filename based on the description of the example it's applied to. === Analysis of alternative approaches Ideally, these auto-generated cassette filenames should be unique: if filenames collide, multiple examples will share the same cassette. A first attempt generated names based on `example.full_description`, but that led to errors: Errno::ENAMETOOLONG: File name too long @ rb_sysopen - /opt/zammad/test/data/vcr_cassettes/models/ticket/article/ticket_article_callbacks_observers_async_transactions_-_auto-setting_of_outgoing_twitter_article_attributes_via_bg_jobs_when_the_original_channel_specified_in_ticket_preferences_was_deleted_but_a_new_one_with_the_same_screen_name_exists_sets_appropriate_status_attributes_on_the_new_channel.yml Another idea was to use MD5 digests of the above, but in fact both of these approaches share another problem: even minor changes to the description could break tests (unless the committer remembers to rename the cassette file to match): an altered description means VCR will record a new cassette file instead of replaying from the original. (Normally, this would only slow down the test instead of breaking it, but sometimes we modify tests and cassettes after recording them to hide sensitive data like API keys or login credentials.) The approach taken by this commit was to use partial descriptions, combining the parent `describe`/`context` label with the `it` label. This does not guarantee uniqueness-- even in the present refactoring, it produced a filename collision-- but it's a good middle ground. [0]: https://relishapp.com/vcr/vcr/docs
2019-11-12 08:17:21 +00:00
module VCRHelper
def self.auto_record(example)
Refactoring: Automatic RSpec VCR cassette name helper This commit was prepared to support upcoming additions to the test suite (specifically, better coverage for existing Twitter functionality). These upcoming changes will depend heavily on VCR.[0] (VCR is a Ruby gem that makes it easier to write and run tests that call out to external services over HTTP by "recording" HTTP transactions to a YAML file and "replaying" them later.) VCR is widely-used (4600 GitHub stars), but its API is a little clumsy-- You have to manually specify the name of a "cassette" file every time: it 'does something' do VCR.use_cassette('path/to/cassette') do ... end end This commit adds an RSpec metadata config option as a shorthand for the syntax above: it 'does something', :use_vcr do ... end This config option automatically generates a cassette filename based on the description of the example it's applied to. === Analysis of alternative approaches Ideally, these auto-generated cassette filenames should be unique: if filenames collide, multiple examples will share the same cassette. A first attempt generated names based on `example.full_description`, but that led to errors: Errno::ENAMETOOLONG: File name too long @ rb_sysopen - /opt/zammad/test/data/vcr_cassettes/models/ticket/article/ticket_article_callbacks_observers_async_transactions_-_auto-setting_of_outgoing_twitter_article_attributes_via_bg_jobs_when_the_original_channel_specified_in_ticket_preferences_was_deleted_but_a_new_one_with_the_same_screen_name_exists_sets_appropriate_status_attributes_on_the_new_channel.yml Another idea was to use MD5 digests of the above, but in fact both of these approaches share another problem: even minor changes to the description could break tests (unless the committer remembers to rename the cassette file to match): an altered description means VCR will record a new cassette file instead of replaying from the original. (Normally, this would only slow down the test instead of breaking it, but sometimes we modify tests and cassettes after recording them to hide sensitive data like API keys or login credentials.) The approach taken by this commit was to use partial descriptions, combining the parent `describe`/`context` label with the `it` label. This does not guarantee uniqueness-- even in the present refactoring, it produced a filename collision-- but it's a good middle ground. [0]: https://relishapp.com/vcr/vcr/docs
2019-11-12 08:17:21 +00:00
spec_path = Pathname.new(example.file_path).realpath
cassette_path = spec_path.relative_path_from(Rails.root.join('spec')).sub(/_spec\.rb$/, '')
cassette_name = "#{example.example_group.description} #{example.description}".gsub(/[^0-9A-Za-z.\-]+/, '_').downcase
request_profile = case example.metadata[:use_vcr]
when true
%i[method uri]
when :with_oauth_headers
%i[method uri oauth_headers]
end
VCR.use_cassette(cassette_path.join(cassette_name), match_requests_on: request_profile) do
example.run
end
end
end
module RSpec
VCR_ADVISORY = <<~MSG.freeze
If this test is failing unexpectedly, the VCR cassette may be to blame.
This can happen when changing `describe`/`context` labels on some specs;
see commit message 1ebddff95 for details.
Check `git status` to see if a new VCR cassette has been generated.
If so, rename the old cassette to replace the new one and try again.
MSG
module Support
module VCRHelper
def self.inject_advisory(example)
# block argument is an #<RSpec::Expectations::ExpectationNotMetError>
define_method(:notify_failure) do |e|
super(e.exception(VCR_ADVISORY + e.message))
end
example.run
ensure
remove_method(:notify_failure)
end
end
singleton_class.send(:prepend, VCRHelper)
end
module Expectations
module VCRHelper
def self.inject_advisory(example)
define_method(:handle_matcher) do |*args|
super(*args)
rescue => e
raise e.exception(VCR_ADVISORY + e.message)
end
example.run
ensure
remove_method(:handle_matcher)
end
end
PositiveExpectationHandler.singleton_class.send(:prepend, VCRHelper)
NegativeExpectationHandler.singleton_class.send(:prepend, VCRHelper)
end
end
RSpec.configure do |config|
config.around(:each, use_vcr: true, &VCRHelper.method(:auto_record))
config.around(:each, use_vcr: true, &RSpec::Support::VCRHelper.method(:inject_advisory))
config.around(:each, use_vcr: true, &RSpec::Expectations::VCRHelper.method(:inject_advisory))
end