backspin 0.4.0 → 0.4.2
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 +4 -4
- data/.gem_release.yml +13 -0
- data/CHANGELOG.md +5 -7
- data/CLAUDE.md +7 -3
- data/CONTRIBUTING.md +2 -2
- data/Gemfile +3 -1
- data/Gemfile.lock +3 -1
- data/MATCH_ON_USAGE.md +110 -0
- data/README.md +7 -2
- data/Rakefile +5 -1
- data/backspin.gemspec +6 -3
- data/examples/match_on_example.rb +116 -0
- data/fixtures/backspin/all_and_fields.yml +15 -0
- data/fixtures/backspin/all_bypass_equality.yml +14 -0
- data/fixtures/backspin/all_checks_equality.yml +17 -0
- data/fixtures/backspin/all_for_logging.yml +13 -0
- data/fixtures/backspin/all_matcher_basic.yml +14 -0
- data/fixtures/backspin/all_matcher_custom.yml +17 -0
- data/fixtures/backspin/all_matcher_demo.yml +14 -0
- data/fixtures/backspin/all_matcher_test.yml +14 -0
- data/fixtures/backspin/all_mode_filter.yml +14 -0
- data/fixtures/backspin/all_no_short_circuit.yml +14 -0
- data/fixtures/backspin/all_pass_field_fail.yml +14 -0
- data/fixtures/backspin/all_short_circuit.yml +14 -0
- data/fixtures/backspin/all_skips_equality.yml +17 -0
- data/fixtures/backspin/all_with_equality.yml +17 -0
- data/fixtures/backspin/all_with_fields.yml +17 -0
- data/fixtures/backspin/combined_fail_demo.yml +14 -0
- data/fixtures/backspin/combined_matcher_demo.yml +14 -0
- data/fixtures/backspin/credential_filter.yml +18 -0
- data/fixtures/backspin/echo_hello.yml +14 -0
- data/fixtures/backspin/echo_verify.yml +14 -0
- data/fixtures/backspin/episodes_filter.yml +26 -0
- data/fixtures/backspin/failure_test.yml +14 -0
- data/fixtures/backspin/field_matcher_demo.yml +17 -0
- data/fixtures/backspin/field_matcher_values.yml +14 -0
- data/fixtures/backspin/full_data_filter.yml +17 -0
- data/fixtures/backspin/key_confusion_test.yml +14 -0
- data/fixtures/backspin/match_on_any_fail.yml +21 -0
- data/fixtures/backspin/match_on_bad_format.yml +14 -0
- data/fixtures/backspin/match_on_fail.yml +15 -0
- data/fixtures/backspin/match_on_invalid.yml +14 -0
- data/fixtures/backspin/match_on_multiple.yml +28 -0
- data/fixtures/backspin/match_on_nil.yml +14 -0
- data/fixtures/backspin/match_on_other_fields.yml +23 -0
- data/fixtures/backspin/match_on_run_bang.yml +16 -0
- data/fixtures/backspin/match_on_run_bang_fail.yml +15 -0
- data/fixtures/backspin/match_on_single.yml +17 -0
- data/fixtures/backspin/mixed_calls.yml +24 -0
- data/fixtures/backspin/multi_command.yml +34 -0
- data/fixtures/backspin/multi_command_filter.yml +26 -0
- data/fixtures/backspin/multi_field_filter.yml +13 -0
- data/fixtures/backspin/multi_system.yml +20 -0
- data/fixtures/backspin/nil_filter.yml +14 -0
- data/fixtures/backspin/none_mode_test.yml +14 -0
- data/fixtures/backspin/path_test.yml +17 -0
- data/fixtures/backspin/playback_system.yml +12 -0
- data/fixtures/backspin/playback_test.yml +14 -0
- data/fixtures/backspin/stderr_test.yml +19 -0
- data/fixtures/backspin/string_symbol_test.yml +14 -0
- data/fixtures/backspin/system_echo.yml +12 -0
- data/fixtures/backspin/system_false.yml +18 -0
- data/fixtures/backspin/timestamp_test.yml +18 -0
- data/fixtures/backspin/use_record_filter.yml +15 -0
- data/fixtures/backspin/verify_system.yml +12 -0
- data/fixtures/backspin/verify_system_diff.yml +11 -0
- data/fixtures/backspin/version_test.yml +14 -0
- data/lib/backspin/command.rb +1 -5
- data/lib/backspin/command_diff.rb +98 -16
- data/lib/backspin/command_result.rb +2 -4
- data/lib/backspin/record.rb +31 -10
- data/lib/backspin/record_result.rb +20 -14
- data/lib/backspin/recorder.rb +100 -55
- data/lib/backspin/version.rb +3 -1
- data/lib/backspin.rb +36 -175
- data/release.rake +97 -0
- data/script/lint +6 -0
- metadata +79 -5
data/lib/backspin.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "yaml"
|
2
4
|
require "fileutils"
|
3
5
|
require "open3"
|
@@ -21,7 +23,7 @@ module Backspin
|
|
21
23
|
# Configuration for Backspin
|
22
24
|
class Configuration
|
23
25
|
attr_accessor :scrub_credentials
|
24
|
-
# The directory where backspin will store its files - defaults to
|
26
|
+
# The directory where backspin will store its files - defaults to fixtures/backspin
|
25
27
|
attr_accessor :backspin_dir
|
26
28
|
# Regex patterns to scrub from saved output
|
27
29
|
attr_reader :credential_patterns
|
@@ -29,7 +31,7 @@ module Backspin
|
|
29
31
|
def initialize
|
30
32
|
@scrub_credentials = true
|
31
33
|
@credential_patterns = default_credential_patterns
|
32
|
-
@backspin_dir = Pathname(Dir.pwd).join("
|
34
|
+
@backspin_dir = Pathname(Dir.pwd).join("fixtures", "backspin")
|
33
35
|
end
|
34
36
|
|
35
37
|
def add_credential_pattern(pattern)
|
@@ -50,22 +52,22 @@ module Backspin
|
|
50
52
|
def default_credential_patterns
|
51
53
|
[
|
52
54
|
# AWS credentials
|
53
|
-
/AKIA[0-9A-Z]{16}/,
|
54
|
-
|
55
|
-
|
55
|
+
/AKIA[0-9A-Z]{16}/, # AWS Access Key ID
|
56
|
+
%r{aws_secret_access_key\s*[:=]\s*["']?([A-Za-z0-9/+=]{40})["']?}i, # AWS Secret Key
|
57
|
+
%r{aws_session_token\s*[:=]\s*["']?([A-Za-z0-9/+=]+)["']?}i, # AWS Session Token
|
56
58
|
|
57
59
|
# Google Cloud credentials
|
58
|
-
/AIza[0-9A-Za-z\-_]{35}/,
|
60
|
+
/AIza[0-9A-Za-z\-_]{35}/, # Google API Key
|
59
61
|
/[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com/, # Google OAuth2 client ID
|
60
|
-
/-----BEGIN (RSA )?PRIVATE KEY-----/,
|
62
|
+
/-----BEGIN (RSA )?PRIVATE KEY-----/, # Private keys
|
61
63
|
|
62
64
|
# Generic patterns
|
63
|
-
/api[_-]?key\s*[:=]\s*["']?([A-Za-z0-9\-_]{20,})["']?/i,
|
65
|
+
/api[_-]?key\s*[:=]\s*["']?([A-Za-z0-9\-_]{20,})["']?/i, # Generic API keys
|
64
66
|
/auth[_-]?token\s*[:=]\s*["']?([A-Za-z0-9\-_]{20,})["']?/i, # Auth tokens
|
65
67
|
/Bearer\s+([A-Za-z0-9\-_]+)/, # Bearer tokens
|
66
|
-
/password\s*[:=]\s*["']?([^"'\s]{8,})["']?/i,
|
67
|
-
/-p([^"'\s]{8,})/,
|
68
|
-
/secret\s*[:=]\s*["']?([A-Za-z0-9\-_]{20,})["']?/i
|
68
|
+
/password\s*[:=]\s*["']?([^"'\s]{8,})["']?/i, # Passwords
|
69
|
+
/-p([^"'\s]{8,})/, # MySQL-style password args
|
70
|
+
/secret\s*[:=]\s*["']?([A-Za-z0-9\-_]{20,})["']?/i # Generic secrets
|
69
71
|
]
|
70
72
|
end
|
71
73
|
end
|
@@ -91,7 +93,6 @@ module Backspin
|
|
91
93
|
scrubbed = text.dup
|
92
94
|
configuration.credential_patterns.each do |pattern|
|
93
95
|
scrubbed.gsub!(pattern) do |match|
|
94
|
-
# Replace with asterisks of the same length
|
95
96
|
"*" * match.length
|
96
97
|
end
|
97
98
|
end
|
@@ -104,22 +105,39 @@ module Backspin
|
|
104
105
|
# @param options [Hash] Options for recording/verification
|
105
106
|
# @option options [Symbol] :mode (:auto) Recording mode - :auto, :record, :verify, :playback
|
106
107
|
# @option options [Proc] :filter Custom filter for recorded data
|
107
|
-
# @option options [Proc] :matcher Custom matcher for verification
|
108
|
+
# @option options [Proc, Hash] :matcher Custom matcher for verification
|
109
|
+
# - Proc: ->(recorded, actual) { ... } for full command matching
|
110
|
+
# - Hash: { stdout: ->(recorded, actual) { ... }, stderr: ->(recorded, actual) { ... } } for field-specific matching
|
111
|
+
# - Hash with :all key: { all: ->(recorded, actual) { ... }, stdout: ->(recorded, actual) { ... } } for combined matching
|
112
|
+
# When both :all and field matchers are present, both must pass for verification to succeed.
|
113
|
+
# Fields without specific matchers always use exact equality, regardless of :all presence.
|
108
114
|
# @return [RecordResult] Result object with output and status
|
109
115
|
def run(record_name, options = {}, &block)
|
110
116
|
raise ArgumentError, "record_name is required" if record_name.nil? || record_name.empty?
|
111
117
|
raise ArgumentError, "block is required" unless block_given?
|
112
118
|
|
113
|
-
record_path = build_record_path(record_name)
|
119
|
+
record_path = Record.build_record_path(record_name)
|
114
120
|
mode = determine_mode(options[:mode], record_path)
|
115
121
|
|
122
|
+
# Create or load the record based on mode
|
123
|
+
record = if mode == :record
|
124
|
+
Record.create(record_name)
|
125
|
+
else
|
126
|
+
Record.load_or_create(record_path)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Create recorder with all needed context
|
130
|
+
recorder = Recorder.new(record: record, options: options, mode: mode)
|
131
|
+
|
132
|
+
# Execute the appropriate mode
|
116
133
|
case mode
|
117
134
|
when :record
|
118
|
-
|
135
|
+
recorder.setup_recording_stubs(:capture3, :system)
|
136
|
+
recorder.perform_recording(&block)
|
119
137
|
when :verify
|
120
|
-
perform_verification(
|
138
|
+
recorder.perform_verification(&block)
|
121
139
|
when :playback
|
122
|
-
perform_playback(
|
140
|
+
recorder.perform_playback(&block)
|
123
141
|
else
|
124
142
|
raise ArgumentError, "Unknown mode: #{mode}"
|
125
143
|
end
|
@@ -136,7 +154,7 @@ module Backspin
|
|
136
154
|
|
137
155
|
if result.verified? == false
|
138
156
|
error_message = "Backspin verification failed!\n"
|
139
|
-
error_message += "Record: #{result.
|
157
|
+
error_message += "Record: #{result.record.path}\n"
|
140
158
|
|
141
159
|
# Use the error_message from the result which is now properly formatted
|
142
160
|
error_message += "\n#{result.error_message}" if result.error_message
|
@@ -155,162 +173,5 @@ module Backspin
|
|
155
173
|
# Auto mode: record if file doesn't exist, verify if it does
|
156
174
|
File.exist?(record_path) ? :verify : :record
|
157
175
|
end
|
158
|
-
|
159
|
-
def perform_recording(_record_name, record_path, options)
|
160
|
-
recorder = Recorder.new
|
161
|
-
recorder.record_calls(:capture3, :system)
|
162
|
-
|
163
|
-
output = yield
|
164
|
-
|
165
|
-
if output.is_a?(Array) && output.size == 3
|
166
|
-
stdout, stderr, status = output
|
167
|
-
status_int = status.respond_to?(:exitstatus) ? status.exitstatus : status
|
168
|
-
output = [stdout, stderr, status_int]
|
169
|
-
end
|
170
|
-
|
171
|
-
# Save the recording
|
172
|
-
FileUtils.mkdir_p(File.dirname(record_path))
|
173
|
-
record = Record.new(record_path)
|
174
|
-
record.clear
|
175
|
-
recorder.commands.each { |cmd| record.add_command(cmd) }
|
176
|
-
record.save(filter: options[:filter])
|
177
|
-
|
178
|
-
# Return result
|
179
|
-
RecordResult.new(
|
180
|
-
output: output,
|
181
|
-
mode: :record,
|
182
|
-
record_path: Pathname.new(record_path),
|
183
|
-
commands: recorder.commands
|
184
|
-
)
|
185
|
-
end
|
186
|
-
|
187
|
-
def perform_verification(_record_name, record_path, options)
|
188
|
-
record = Record.load_or_create(record_path)
|
189
|
-
|
190
|
-
raise RecordNotFoundError, "Record not found: #{record_path}" unless record.exists?
|
191
|
-
raise RecordNotFoundError, "No commands found in record" if record.empty?
|
192
|
-
|
193
|
-
# For verification, we need to track all commands executed
|
194
|
-
recorder = Recorder.new(mode: :verify, record: record)
|
195
|
-
recorder.setup_replay_stubs
|
196
|
-
|
197
|
-
# Track verification results for each command
|
198
|
-
command_diffs = []
|
199
|
-
command_index = 0
|
200
|
-
|
201
|
-
# Override stubs to verify each command as it's executed
|
202
|
-
allow(Open3).to receive(:capture3).and_wrap_original do |original_method, *args|
|
203
|
-
recorded_command = record.commands[command_index]
|
204
|
-
|
205
|
-
if recorded_command.nil?
|
206
|
-
raise RecordNotFoundError, "No more recorded commands, but tried to execute: #{args.inspect}"
|
207
|
-
end
|
208
|
-
|
209
|
-
if recorded_command.method_class != Open3::Capture3
|
210
|
-
raise RecordNotFoundError, "Expected #{recorded_command.method_class.name} but got Open3.capture3"
|
211
|
-
end
|
212
|
-
|
213
|
-
# Execute the actual command
|
214
|
-
stdout, stderr, status = original_method.call(*args)
|
215
|
-
|
216
|
-
# Create verification result
|
217
|
-
actual_result = CommandResult.new(
|
218
|
-
stdout: stdout,
|
219
|
-
stderr: stderr,
|
220
|
-
status: status.exitstatus
|
221
|
-
)
|
222
|
-
|
223
|
-
# Create CommandDiff to track the comparison
|
224
|
-
command_diffs << CommandDiff.new(
|
225
|
-
recorded_command: recorded_command,
|
226
|
-
actual_result: actual_result,
|
227
|
-
matcher: options[:matcher]
|
228
|
-
)
|
229
|
-
|
230
|
-
command_index += 1
|
231
|
-
[stdout, stderr, status]
|
232
|
-
end
|
233
|
-
|
234
|
-
allow_any_instance_of(Object).to receive(:system).and_wrap_original do |original_method, receiver, *args|
|
235
|
-
recorded_command = record.commands[command_index]
|
236
|
-
|
237
|
-
if recorded_command.nil?
|
238
|
-
raise RecordNotFoundError, "No more recorded commands, but tried to execute: system #{args.inspect}"
|
239
|
-
end
|
240
|
-
|
241
|
-
if recorded_command.method_class != ::Kernel::System
|
242
|
-
raise RecordNotFoundError, "Expected #{recorded_command.method_class.name} but got system"
|
243
|
-
end
|
244
|
-
|
245
|
-
# Execute the actual command
|
246
|
-
result = original_method.call(receiver, *args)
|
247
|
-
|
248
|
-
# Create verification result (system only gives us exit status)
|
249
|
-
actual_result = CommandResult.new(
|
250
|
-
stdout: "",
|
251
|
-
stderr: "",
|
252
|
-
status: result ? 0 : 1
|
253
|
-
)
|
254
|
-
|
255
|
-
# Create CommandDiff to track the comparison
|
256
|
-
command_diffs << CommandDiff.new(
|
257
|
-
recorded_command: recorded_command,
|
258
|
-
actual_result: actual_result,
|
259
|
-
matcher: options[:matcher]
|
260
|
-
)
|
261
|
-
|
262
|
-
command_index += 1
|
263
|
-
result
|
264
|
-
end
|
265
|
-
|
266
|
-
# Execute block
|
267
|
-
output = yield
|
268
|
-
|
269
|
-
# Check if all commands were executed
|
270
|
-
if command_index < record.commands.size
|
271
|
-
raise RecordNotFoundError, "Expected #{record.commands.size} commands but only #{command_index} were executed"
|
272
|
-
end
|
273
|
-
|
274
|
-
# Overall verification status
|
275
|
-
all_verified = command_diffs.all?(&:verified?)
|
276
|
-
|
277
|
-
RecordResult.new(
|
278
|
-
output: output,
|
279
|
-
mode: :verify,
|
280
|
-
verified: all_verified,
|
281
|
-
record_path: Pathname.new(record_path),
|
282
|
-
commands: record.commands,
|
283
|
-
command_diffs: command_diffs
|
284
|
-
)
|
285
|
-
end
|
286
|
-
|
287
|
-
def perform_playback(_record_name, record_path, _options)
|
288
|
-
record = Record.load_or_create(record_path)
|
289
|
-
|
290
|
-
raise RecordNotFoundError, "Record not found: #{record_path}" unless record.exists?
|
291
|
-
raise RecordNotFoundError, "No commands found in record" if record.empty?
|
292
|
-
|
293
|
-
# Setup replay mode - this will handle returning values for all commands
|
294
|
-
recorder = Recorder.new(mode: :replay, record: record)
|
295
|
-
recorder.setup_replay_stubs
|
296
|
-
|
297
|
-
# Execute block (all commands will be stubbed with recorded values)
|
298
|
-
output = yield
|
299
|
-
|
300
|
-
RecordResult.new(
|
301
|
-
output: output,
|
302
|
-
mode: :playback,
|
303
|
-
verified: true, # Always true for playback
|
304
|
-
record_path: Pathname.new(record_path),
|
305
|
-
commands: record.commands
|
306
|
-
)
|
307
|
-
end
|
308
|
-
|
309
|
-
def build_record_path(name)
|
310
|
-
backspin_dir = configuration.backspin_dir
|
311
|
-
backspin_dir.mkpath
|
312
|
-
|
313
|
-
File.join(backspin_dir, "#{name}.yml")
|
314
|
-
end
|
315
176
|
end
|
316
177
|
end
|
data/release.rake
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
|
5
|
+
# Simplified release tasks using gem-release
|
6
|
+
# Install with: gem install gem-release
|
7
|
+
|
8
|
+
namespace :release do
|
9
|
+
desc "Release a new version (bump, tag, release)"
|
10
|
+
task :version, [:level] do |t, args|
|
11
|
+
level = args[:level] || "patch"
|
12
|
+
|
13
|
+
# Pre-release checks
|
14
|
+
Rake::Task["release:check"].invoke
|
15
|
+
|
16
|
+
puts "\nReleasing #{level} version..."
|
17
|
+
|
18
|
+
# Use gem-release to bump, tag, and release to rubygems and github
|
19
|
+
sh "gem bump --version #{level} --github --tag --release"
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Create GitHub release for current version"
|
23
|
+
task :github do
|
24
|
+
version = Backspin::VERSION
|
25
|
+
|
26
|
+
if system("which gh > /dev/null 2>&1")
|
27
|
+
puts "\nCreating GitHub release for v#{version}..."
|
28
|
+
sh "gh release create v#{version} --title 'Release v#{version}' --generate-notes"
|
29
|
+
else
|
30
|
+
puts "\nGitHub CLI not found. Create release manually at:"
|
31
|
+
puts "https://github.com/rsanheim/backspin/releases/new?tag=v#{version}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Check if ready for release"
|
36
|
+
task :check do
|
37
|
+
require "open-uri"
|
38
|
+
require "json"
|
39
|
+
|
40
|
+
current_version = Backspin::VERSION
|
41
|
+
errors = []
|
42
|
+
|
43
|
+
# Check RubyGems for latest version
|
44
|
+
begin
|
45
|
+
gem_data = JSON.parse(URI.open("https://rubygems.org/api/v1/gems/backspin.json").read)
|
46
|
+
latest_version = gem_data["version"]
|
47
|
+
|
48
|
+
if Gem::Version.new(current_version) <= Gem::Version.new(latest_version)
|
49
|
+
errors << "Current version (#{current_version}) is not greater than latest released version (#{latest_version})"
|
50
|
+
else
|
51
|
+
puts "✓ Version #{current_version} is ready for release (latest: #{latest_version})"
|
52
|
+
end
|
53
|
+
rescue => e
|
54
|
+
puts "⚠ Could not check RubyGems version: #{e.message}"
|
55
|
+
end
|
56
|
+
|
57
|
+
# Check git status
|
58
|
+
if system("git diff --quiet && git diff --cached --quiet")
|
59
|
+
puts "✓ No uncommitted changes"
|
60
|
+
else
|
61
|
+
errors << "You have uncommitted changes"
|
62
|
+
end
|
63
|
+
|
64
|
+
# Check branch
|
65
|
+
current_branch = `git rev-parse --abbrev-ref HEAD`.strip
|
66
|
+
if current_branch != "main"
|
67
|
+
puts "⚠ Not on main branch (currently on #{current_branch})"
|
68
|
+
print "Continue anyway? (y/N): "
|
69
|
+
response = $stdin.gets.chomp
|
70
|
+
errors << "Not on main branch" unless response.downcase == "y"
|
71
|
+
else
|
72
|
+
puts "✓ On main branch"
|
73
|
+
end
|
74
|
+
|
75
|
+
unless errors.empty?
|
76
|
+
puts "\n❌ Cannot release:"
|
77
|
+
errors.each { |e| puts " - #{e}" }
|
78
|
+
abort
|
79
|
+
end
|
80
|
+
|
81
|
+
puts "\n✅ All checks passed!"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Convenience tasks
|
86
|
+
desc "Release patch version"
|
87
|
+
task "release:patch" => ["release:version"]
|
88
|
+
|
89
|
+
desc "Release minor version"
|
90
|
+
task "release:minor" do
|
91
|
+
Rake::Task["release:version"].invoke("minor")
|
92
|
+
end
|
93
|
+
|
94
|
+
desc "Release major version"
|
95
|
+
task "release:major" do
|
96
|
+
Rake::Task["release:version"].invoke("major")
|
97
|
+
end
|
data/script/lint
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: backspin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob Sanheim
|
@@ -9,6 +9,20 @@ bindir: exe
|
|
9
9
|
cert_chain: []
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: ostruct
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: 0.5.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.5.0
|
12
26
|
- !ruby/object:Gem::Dependency
|
13
27
|
name: rspec-mocks
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -24,19 +38,19 @@ dependencies:
|
|
24
38
|
- !ruby/object:Gem::Version
|
25
39
|
version: '3.0'
|
26
40
|
- !ruby/object:Gem::Dependency
|
27
|
-
name:
|
41
|
+
name: gem-release
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
29
43
|
requirements:
|
30
44
|
- - "~>"
|
31
45
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
33
|
-
type: :
|
46
|
+
version: '2.2'
|
47
|
+
type: :development
|
34
48
|
prerelease: false
|
35
49
|
version_requirements: !ruby/object:Gem::Requirement
|
36
50
|
requirements:
|
37
51
|
- - "~>"
|
38
52
|
- !ruby/object:Gem::Version
|
39
|
-
version:
|
53
|
+
version: '2.2'
|
40
54
|
description: Backspin is a Ruby library for characterization testing of command-line
|
41
55
|
interfaces. Inspired by VCR's cassette-based approach, it records and replays CLI
|
42
56
|
interactions to make testing faster and more deterministic.
|
@@ -47,6 +61,7 @@ extensions: []
|
|
47
61
|
extra_rdoc_files: []
|
48
62
|
files:
|
49
63
|
- ".circleci/config.yml"
|
64
|
+
- ".gem_release.yml"
|
50
65
|
- ".gitignore"
|
51
66
|
- ".rspec"
|
52
67
|
- ".standard.yml"
|
@@ -56,12 +71,69 @@ files:
|
|
56
71
|
- Gemfile
|
57
72
|
- Gemfile.lock
|
58
73
|
- LICENSE.txt
|
74
|
+
- MATCH_ON_USAGE.md
|
59
75
|
- README.md
|
60
76
|
- Rakefile
|
61
77
|
- backspin.gemspec
|
62
78
|
- bin/rake
|
63
79
|
- bin/rspec
|
64
80
|
- bin/setup
|
81
|
+
- examples/match_on_example.rb
|
82
|
+
- fixtures/backspin/all_and_fields.yml
|
83
|
+
- fixtures/backspin/all_bypass_equality.yml
|
84
|
+
- fixtures/backspin/all_checks_equality.yml
|
85
|
+
- fixtures/backspin/all_for_logging.yml
|
86
|
+
- fixtures/backspin/all_matcher_basic.yml
|
87
|
+
- fixtures/backspin/all_matcher_custom.yml
|
88
|
+
- fixtures/backspin/all_matcher_demo.yml
|
89
|
+
- fixtures/backspin/all_matcher_test.yml
|
90
|
+
- fixtures/backspin/all_mode_filter.yml
|
91
|
+
- fixtures/backspin/all_no_short_circuit.yml
|
92
|
+
- fixtures/backspin/all_pass_field_fail.yml
|
93
|
+
- fixtures/backspin/all_short_circuit.yml
|
94
|
+
- fixtures/backspin/all_skips_equality.yml
|
95
|
+
- fixtures/backspin/all_with_equality.yml
|
96
|
+
- fixtures/backspin/all_with_fields.yml
|
97
|
+
- fixtures/backspin/combined_fail_demo.yml
|
98
|
+
- fixtures/backspin/combined_matcher_demo.yml
|
99
|
+
- fixtures/backspin/credential_filter.yml
|
100
|
+
- fixtures/backspin/echo_hello.yml
|
101
|
+
- fixtures/backspin/echo_verify.yml
|
102
|
+
- fixtures/backspin/episodes_filter.yml
|
103
|
+
- fixtures/backspin/failure_test.yml
|
104
|
+
- fixtures/backspin/field_matcher_demo.yml
|
105
|
+
- fixtures/backspin/field_matcher_values.yml
|
106
|
+
- fixtures/backspin/full_data_filter.yml
|
107
|
+
- fixtures/backspin/key_confusion_test.yml
|
108
|
+
- fixtures/backspin/match_on_any_fail.yml
|
109
|
+
- fixtures/backspin/match_on_bad_format.yml
|
110
|
+
- fixtures/backspin/match_on_fail.yml
|
111
|
+
- fixtures/backspin/match_on_invalid.yml
|
112
|
+
- fixtures/backspin/match_on_multiple.yml
|
113
|
+
- fixtures/backspin/match_on_nil.yml
|
114
|
+
- fixtures/backspin/match_on_other_fields.yml
|
115
|
+
- fixtures/backspin/match_on_run_bang.yml
|
116
|
+
- fixtures/backspin/match_on_run_bang_fail.yml
|
117
|
+
- fixtures/backspin/match_on_single.yml
|
118
|
+
- fixtures/backspin/mixed_calls.yml
|
119
|
+
- fixtures/backspin/multi_command.yml
|
120
|
+
- fixtures/backspin/multi_command_filter.yml
|
121
|
+
- fixtures/backspin/multi_field_filter.yml
|
122
|
+
- fixtures/backspin/multi_system.yml
|
123
|
+
- fixtures/backspin/nil_filter.yml
|
124
|
+
- fixtures/backspin/none_mode_test.yml
|
125
|
+
- fixtures/backspin/path_test.yml
|
126
|
+
- fixtures/backspin/playback_system.yml
|
127
|
+
- fixtures/backspin/playback_test.yml
|
128
|
+
- fixtures/backspin/stderr_test.yml
|
129
|
+
- fixtures/backspin/string_symbol_test.yml
|
130
|
+
- fixtures/backspin/system_echo.yml
|
131
|
+
- fixtures/backspin/system_false.yml
|
132
|
+
- fixtures/backspin/timestamp_test.yml
|
133
|
+
- fixtures/backspin/use_record_filter.yml
|
134
|
+
- fixtures/backspin/verify_system.yml
|
135
|
+
- fixtures/backspin/verify_system_diff.yml
|
136
|
+
- fixtures/backspin/version_test.yml
|
65
137
|
- lib/backspin.rb
|
66
138
|
- lib/backspin/command.rb
|
67
139
|
- lib/backspin/command_diff.rb
|
@@ -70,6 +142,8 @@ files:
|
|
70
142
|
- lib/backspin/record_result.rb
|
71
143
|
- lib/backspin/recorder.rb
|
72
144
|
- lib/backspin/version.rb
|
145
|
+
- release.rake
|
146
|
+
- script/lint
|
73
147
|
homepage: https://github.com/rsanheim/backspin
|
74
148
|
licenses:
|
75
149
|
- MIT
|