vcr-log_unmatched_cassettes 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5715a8e1d849e98faea2e124290b2fa5d4b74a3e75f7feced17ad4b64457e115
4
+ data.tar.gz: 7a51740f8bcae35c1ddb986e143a21b5182a9e7b9c38e17a4257428958b18269
5
+ SHA512:
6
+ metadata.gz: e2cd3c15a2d8a73d93e6a9d78d64cf09436c43191a8dad13064f2f4b13287920e276ac397d41b2b438f9c0a5c413d146deb34df4f1731ab7ad062f8caad213eb
7
+ data.tar.gz: 67c3705c67566b056274f65fa397b82c95692c8fc5ee2db8de5f6233010be4251d11b719d005dd08f9699fb0f4aceb7fa313b923ae7ed07f49f93ca7d462f51b
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 sshaw
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # VCR::LogUnmatchedCassettes
2
+
3
+ Make finding and re-recording unmatched [VCR cassettes](https://github.com/vcr/vcr) (test that fail due to `VCR::Errors::UnhandledHTTPRequestError`)
4
+ easier by logging the test and unmatched cassette paths to a CSV.
5
+
6
+ ## Usage
7
+
8
+ In your `Gemfile`
9
+
10
+ ```rb
11
+ gem "vcr-log_unmatched_cassettes", :group => "test"
12
+ ```
13
+
14
+ In your spec configuration, after loading VCR, call:
15
+ ```rb
16
+ VCR::LogUnmatchedCassettes.install
17
+ ```
18
+
19
+ Now tests that fails due to VCR being able to unmatch the request (`VCR::Errors::UnhandledHTTPRequestError`) will be output to the system's temp directory in a CSV.
20
+ If you're using Rails it will be output to `./tmp`.
21
+
22
+ ## Why?
23
+
24
+ When tests fail due to unmatched HTTP requests the path is output within a large blob of text. This may be fine for a couple of tests but
25
+ with large amounts of tests the path can be a pain track down. This becomes worse when one needs the paths for multiple tests.
26
+
27
+ Often one needs the path solely to delete the cassette and re-record it. This is often preferred to using VCR's `record: :all` globally as `:all` is a bit heavy-handed.
28
+
29
+ `record: :all` can be applied per test, but this requires modifying the test so that it can be scoped to `:all`.
30
+
31
+ Remove all cassettes is an option but this can clutter your git history with needless and _very_ bloated diffs.
32
+
33
+ ## License
34
+
35
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
36
+
37
+ ## Author
38
+
39
+ Skye Shaw [sshaw AT gmail.com]
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VCR
4
+ module LogUnmatchedCassettes
5
+ VERSION = "0.0.1"
6
+ end
7
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "vcr"
4
+ require "fileutils"
5
+ require "tmpdir"
6
+ require "csv"
7
+
8
+ module VCR
9
+ module LogUnmatchedCassettes
10
+ module MinitestHook # :nodoc:
11
+ # Minitest stores the test's exception by running it through
12
+ # `sanitize_exception` in `capture_exceptions`. A marshable exception is
13
+ # kept as-is, but VCR::Errors::UnhandledHTTPRequestError is not marshable
14
+ # (it memoizes the active cassette), so Minitest "neuters" it into a
15
+ # generic RuntimeError, losing the class and any `cause` chain. Capture
16
+ # the original exception here so it can still be identified in
17
+ # `after_teardown` (where `$!` is nil).
18
+ def sanitize_exception(error)
19
+ @vcr_unmatched_error = error
20
+ super
21
+ end
22
+
23
+ def after_teardown
24
+ super
25
+
26
+ vcr_error = VCR::LogUnmatchedCassettes.find_vcr_error(@vcr_unmatched_error)
27
+ return unless vcr_error
28
+
29
+ cassette = VCR.current_cassette
30
+ return unless cassette
31
+
32
+ location = "#{self.class.name}##{name}"
33
+ path = cassette.respond_to?(:file) ? cassette.file : cassette.name.to_s
34
+
35
+ VCR::LogUnmatchedCassettes.record(location, path)
36
+ end
37
+ end
38
+
39
+ class << self
40
+ attr_accessor :enabled, :output_dir
41
+
42
+ def install
43
+ self.enabled = true if enabled.nil?
44
+ self.output_dir = defined?(Rails) && Rails.respond_to?(:root) ? Rails.root.join("tmp").to_s : Dir.tmpdir
45
+
46
+ @failures = []
47
+
48
+ if defined?(::RSpec)
49
+ install_rspec
50
+ elsif defined?(Minitest)
51
+ install_minitest
52
+ else
53
+ raise "VCR::LogUnmatchedCassettes requires RSpec or Minitest. Neither was found."
54
+ end
55
+ end
56
+
57
+ def record(location, cassette_path)
58
+ return if !enabled || cassette_path.nil? || cassette_path.empty?
59
+
60
+ @failures << [location, cassette_path]
61
+ end
62
+
63
+ def write_report
64
+ return if @failures.empty?
65
+
66
+ FileUtils.mkdir_p(output_dir)
67
+
68
+ timestamp = Time.now.strftime("%Y%m%d%H%M%S")
69
+ filename = File.join(output_dir, "vcr-unmatched-cassettes.#{timestamp}.csv")
70
+
71
+ CSV.open(filename, "w") do |csv|
72
+ csv << %w[test cassette]
73
+
74
+ @failures.each do |test, cassette|
75
+ csv << [test, cassette]
76
+ end
77
+ end
78
+
79
+ warn "\n[VCR::LogUnmatchedCassettes] Wrote #{@failures.size} failed cassette(s) to: #{filename}"
80
+ end
81
+
82
+ # Walks the exception + cause chain looking for UnhandledHTTPRequestError
83
+ def find_vcr_error(exception)
84
+ current = exception
85
+
86
+ while current
87
+ return current if current.is_a?(VCR::Errors::UnhandledHTTPRequestError)
88
+ current = current.cause
89
+ end
90
+
91
+ nil
92
+ end
93
+
94
+ private
95
+
96
+ def install_rspec
97
+ ::RSpec.configure do |config|
98
+ config.after(:each) do |example|
99
+ vcr_error = VCR::LogUnmatchedCassettes.find_vcr_error(example.exception)
100
+ next unless vcr_error
101
+
102
+ cassette = VCR.current_cassette
103
+ next unless cassette
104
+
105
+ # :location is "./spec/foo_spec.rb:42"
106
+ VCR::LogUnmatchedCassettes.record(example.metadata[:location], cassette.file)
107
+ end
108
+
109
+ config.after(:suite) do
110
+ VCR::LogUnmatchedCassettes.write_report
111
+ end
112
+ end
113
+ end
114
+
115
+ def install_minitest
116
+ Minitest::Test.prepend(MinitestHook)
117
+
118
+ Minitest.after_run { VCR::LogUnmatchedCassettes.write_report }
119
+ end
120
+ end
121
+ end
122
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vcr-log_unmatched_cassettes
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Skye Shaw
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2026-10-22 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: vcr
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: bundler
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: csv
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: minitest
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rake
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rspec
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: vcr
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ email:
111
+ - skye.shaw@gmail.com
112
+ executables: []
113
+ extensions: []
114
+ extra_rdoc_files: []
115
+ files:
116
+ - LICENSE.txt
117
+ - README.md
118
+ - Rakefile
119
+ - lib/vcr/log_unmatched_cassettes.rb
120
+ - lib/vcr/log_unmatched_cassettes/version.rb
121
+ homepage: https://github.com/sshaw/vcr-log_unmatched_cassettes
122
+ licenses:
123
+ - MIT
124
+ metadata:
125
+ homepage_uri: https://github.com/sshaw/vcr-log_unmatched_cassettes
126
+ source_code_uri: https://github.com/sshaw/vcr-log_unmatched_cassettes
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '2.7'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubygems_version: 3.6.2
142
+ specification_version: 4
143
+ summary: Make finding and re-recording unmatched VCR cassettes easier by logging the
144
+ test and unmatched cassette paths to a CSV
145
+ test_files: []