backspin 0.10.0 → 0.12.0
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/.circleci/config.yml +3 -0
- data/CHANGELOG.md +11 -1
- data/Gemfile.lock +3 -1
- data/README.md +82 -0
- data/Rakefile +27 -0
- data/backspin.gemspec +2 -0
- data/docs/backspin-result-api-sketch.md +8 -6
- data/fixtures/backspin/.gitkeep +1 -0
- data/fixtures/projects/dummy_cli_gem/.rspec +3 -0
- data/fixtures/projects/dummy_cli_gem/Gemfile +12 -0
- data/fixtures/projects/dummy_cli_gem/Gemfile.lock +55 -0
- data/fixtures/projects/dummy_cli_gem/LICENSE.txt +1 -0
- data/fixtures/projects/dummy_cli_gem/README.md +7 -0
- data/fixtures/projects/dummy_cli_gem/Rakefile +8 -0
- data/fixtures/projects/dummy_cli_gem/dummy_cli_gem.gemspec +22 -0
- data/fixtures/projects/dummy_cli_gem/exe/dummy_cli_gem +8 -0
- data/fixtures/projects/dummy_cli_gem/fixtures/backspin/dummy_echo.yml +18 -0
- data/fixtures/projects/dummy_cli_gem/fixtures/backspin/dummy_ls.yml +18 -0
- data/fixtures/projects/dummy_cli_gem/lib/dummy_cli_gem/cli.rb +35 -0
- data/fixtures/projects/dummy_cli_gem/lib/dummy_cli_gem/version.rb +5 -0
- data/fixtures/projects/dummy_cli_gem/lib/dummy_cli_gem.rb +7 -0
- data/fixtures/projects/dummy_cli_gem/mise.toml +2 -0
- data/fixtures/projects/dummy_cli_gem/spec/dummy_cli_gem_backspin_spec.rb +46 -0
- data/fixtures/projects/dummy_cli_gem/spec/fixtures/backspin/dummy_echo.yml +18 -0
- data/fixtures/projects/dummy_cli_gem/spec/fixtures/backspin/dummy_ls.yml +18 -0
- data/fixtures/projects/dummy_cli_gem/spec/fixtures/listing_target/alpha.txt +1 -0
- data/fixtures/projects/dummy_cli_gem/spec/spec_helper.rb +22 -0
- data/lib/backspin/configuration.rb +13 -0
- data/lib/backspin/record.rb +42 -5
- data/lib/backspin/version.rb +1 -1
- data/lib/backspin.rb +24 -12
- data/mise.toml +2 -0
- metadata +37 -57
- data/fixtures/backspin/all_and_fields.yml +0 -15
- data/fixtures/backspin/all_bypass_equality.yml +0 -14
- data/fixtures/backspin/all_checks_equality.yml +0 -17
- data/fixtures/backspin/all_for_logging.yml +0 -14
- data/fixtures/backspin/all_matcher_basic.yml +0 -14
- data/fixtures/backspin/all_matcher_custom.yml +0 -17
- data/fixtures/backspin/all_matcher_demo.yml +0 -14
- data/fixtures/backspin/all_matcher_test.yml +0 -14
- data/fixtures/backspin/all_mode_filter.yml +0 -14
- data/fixtures/backspin/all_no_short_circuit.yml +0 -14
- data/fixtures/backspin/all_pass_field_fail.yml +0 -14
- data/fixtures/backspin/all_short_circuit.yml +0 -14
- data/fixtures/backspin/all_skips_equality.yml +0 -17
- data/fixtures/backspin/all_with_equality.yml +0 -17
- data/fixtures/backspin/all_with_fields.yml +0 -17
- data/fixtures/backspin/combined_fail_demo.yml +0 -14
- data/fixtures/backspin/combined_matcher_demo.yml +0 -14
- data/fixtures/backspin/credential_filter.yml +0 -18
- data/fixtures/backspin/echo_hello.yml +0 -14
- data/fixtures/backspin/echo_verify.yml +0 -14
- data/fixtures/backspin/episodes_filter.yml +0 -26
- data/fixtures/backspin/failure_test.yml +0 -14
- data/fixtures/backspin/field_matcher_demo.yml +0 -17
- data/fixtures/backspin/field_matcher_values.yml +0 -14
- data/fixtures/backspin/full_data_filter.yml +0 -17
- data/fixtures/backspin/key_confusion_test.yml +0 -14
- data/fixtures/backspin/match_on_any_fail.yml +0 -21
- data/fixtures/backspin/match_on_bad_format.yml +0 -14
- data/fixtures/backspin/match_on_fail.yml +0 -15
- data/fixtures/backspin/match_on_invalid.yml +0 -14
- data/fixtures/backspin/match_on_multiple.yml +0 -28
- data/fixtures/backspin/match_on_nil.yml +0 -14
- data/fixtures/backspin/match_on_other_fields.yml +0 -23
- data/fixtures/backspin/match_on_run_bang.yml +0 -16
- data/fixtures/backspin/match_on_run_bang_fail.yml +0 -15
- data/fixtures/backspin/match_on_single.yml +0 -17
- data/fixtures/backspin/mixed_calls.yml +0 -24
- data/fixtures/backspin/multi_command.yml +0 -34
- data/fixtures/backspin/multi_command_filter.yml +0 -26
- data/fixtures/backspin/multi_field_filter.yml +0 -13
- data/fixtures/backspin/multi_system.yml +0 -20
- data/fixtures/backspin/nil_filter.yml +0 -14
- data/fixtures/backspin/none_mode_test.yml +0 -14
- data/fixtures/backspin/path_test.yml +0 -17
- data/fixtures/backspin/playback_system.yml +0 -12
- data/fixtures/backspin/playback_test.yml +0 -14
- data/fixtures/backspin/stderr_test.yml +0 -19
- data/fixtures/backspin/strict_test.yml +0 -14
- data/fixtures/backspin/string_symbol_test.yml +0 -14
- data/fixtures/backspin/system_echo.yml +0 -12
- data/fixtures/backspin/system_false.yml +0 -18
- data/fixtures/backspin/timestamp_test.yml +0 -18
- data/fixtures/backspin/verify_system.yml +0 -12
- data/fixtures/backspin/verify_system_diff.yml +0 -11
- data/fixtures/backspin/version_test.yml +0 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6373427307003e8469435ce1c461e6f9bf6ff307c952bce87f07c393470f5fdc
|
|
4
|
+
data.tar.gz: 3e8487ba8345a4fef0b8419487de0e84b756c302ada8b313e2bf0e5cd6c1ec1a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d7f1915e4632d8fb4b08ae6a8ff0e6e9846ed3f1cd783c7dcc283ca2d3816afb518173a50cd519b5859eb6d991be7636a81a48e34fb63f0176d14c9a2bdd500a
|
|
7
|
+
data.tar.gz: 7307a43c18730ec430f39dbadecb301eaad177ecc3795906f9682ec3e4781e9d5d9ec2b8c913220f8c5985393ae90e73974e5f2855a40ea17d33f87914b5a527
|
data/.circleci/config.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.12.0
|
|
4
|
+
* Added `BACKSPIN_MODE` environment variable to globally override recording mode (`auto`, `record`, `verify`).
|
|
5
|
+
* Explicit `mode:` kwarg still takes highest precedence, followed by the env var, then auto-detection.
|
|
6
|
+
* Added configurable logger to `Backspin::Configuration` (defaults to WARN level, logfmt-lite format, and can be disabled with `config.logger = nil`).
|
|
7
|
+
|
|
8
|
+
## 0.11.0 - 2026-02-11
|
|
9
|
+
* Added immutable top-level `first_recorded_at` metadata for record files.
|
|
10
|
+
* Added mutable top-level `recorded_at` metadata that updates on each successful re-record.
|
|
11
|
+
* Added top-level `record_count`, incremented on each successful record write.
|
|
12
|
+
* Bumped record format to 4.1; loading remains backward-compatible with 4.0 record files.
|
|
13
|
+
|
|
3
14
|
## 0.10.0 - 2026-02-11
|
|
4
15
|
* Added `filter_on` to `Backspin.run` and `Backspin.capture` (`:both` default, `:record` opt-out).
|
|
5
16
|
* Changed default filter behavior: `filter` now applies during verify comparisons/diffs when `filter_on: :both`.
|
|
@@ -11,7 +22,6 @@
|
|
|
11
22
|
* Breaking: result convenience accessors (`result.stdout`, `result.stderr`, `result.status`) were removed in favor of snapshot access.
|
|
12
23
|
* Breaking: record format bumped to 4.0 and now persists a single `snapshot` object (v3 records are rejected).
|
|
13
24
|
* Simplification: removed legacy `Command`, `CommandResult`, and `RecordResult` layers; matcher/diff now operate directly on snapshots.
|
|
14
|
-
* Added focused coverage for the new result contract and capture stream restoration behavior.
|
|
15
25
|
* Updated project docs to reflect the BackspinResult + Snapshot API surface.
|
|
16
26
|
|
|
17
27
|
## 0.8.0 - 2026-02-05
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
backspin (0.
|
|
4
|
+
backspin (0.12.0)
|
|
5
|
+
logger
|
|
5
6
|
|
|
6
7
|
GEM
|
|
7
8
|
remote: https://rubygems.org/
|
|
@@ -11,6 +12,7 @@ GEM
|
|
|
11
12
|
json (2.16.0)
|
|
12
13
|
language_server-protocol (3.17.0.5)
|
|
13
14
|
lint_roller (1.1.0)
|
|
15
|
+
logger (1.7.0)
|
|
14
16
|
parallel (1.27.0)
|
|
15
17
|
parser (3.3.10.0)
|
|
16
18
|
ast (~> 2.4.1)
|
data/README.md
CHANGED
|
@@ -91,6 +91,48 @@ result = Backspin.run(["echo", "hello"], name: "echo_test", mode: :verify)
|
|
|
91
91
|
expect(result.verified?).to be true
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
+
### Environment Variable Mode Override
|
|
95
|
+
|
|
96
|
+
Set `BACKSPIN_MODE` to globally force a recording mode without changing any test code:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Re-record all fixtures
|
|
100
|
+
BACKSPIN_MODE=record bundle exec rspec
|
|
101
|
+
|
|
102
|
+
# Verify-only (CI, no accidental re-records)
|
|
103
|
+
BACKSPIN_MODE=verify bundle exec rspec
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Precedence (highest to lowest):
|
|
107
|
+
|
|
108
|
+
1. Explicit `mode:` kwarg (`:record` or `:verify`)
|
|
109
|
+
2. `BACKSPIN_MODE` environment variable
|
|
110
|
+
3. Auto-detection (record if no file exists, verify if it does)
|
|
111
|
+
|
|
112
|
+
Allowed values: `auto`, `record`, `verify` (case-insensitive). Invalid values raise `ArgumentError`.
|
|
113
|
+
|
|
114
|
+
### Record Metadata
|
|
115
|
+
|
|
116
|
+
Backspin writes records using `format_version: "4.1"` with top-level metadata:
|
|
117
|
+
|
|
118
|
+
```yaml
|
|
119
|
+
---
|
|
120
|
+
format_version: "4.1"
|
|
121
|
+
first_recorded_at: "2026-01-01T10:00:00Z" # immutable
|
|
122
|
+
recorded_at: "2026-02-01T10:00:00Z" # updates on each write
|
|
123
|
+
record_count: 3 # increments on each write
|
|
124
|
+
snapshot:
|
|
125
|
+
command_type: "Open3::Capture3"
|
|
126
|
+
args: ["echo", "hello"]
|
|
127
|
+
stdout: "hello\n"
|
|
128
|
+
stderr: ""
|
|
129
|
+
status: 0
|
|
130
|
+
recorded_at: "2026-02-01T10:00:00Z"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
When re-recording with `mode: :record`, Backspin preserves `first_recorded_at`, updates `recorded_at`, and increments `record_count`.
|
|
134
|
+
Existing `4.0` records still load and are upgraded to `4.1` metadata on the next write.
|
|
135
|
+
|
|
94
136
|
### Environment Variables
|
|
95
137
|
|
|
96
138
|
```ruby
|
|
@@ -221,6 +263,34 @@ result = Backspin.run(["echo", "different"], name: "my_test")
|
|
|
221
263
|
Backspin.reset_configuration!
|
|
222
264
|
```
|
|
223
265
|
|
|
266
|
+
### Logging
|
|
267
|
+
|
|
268
|
+
Backspin includes a configurable logger for diagnostics. By default it is set to WARN level; but most messages are logged at DEBUG level.
|
|
269
|
+
So if you are looking for more detailed logs, you can set the logger to DEBUG level:
|
|
270
|
+
|
|
271
|
+
```ruby
|
|
272
|
+
Backspin.configure do |config|
|
|
273
|
+
config.logger = Logger.new($stdout)
|
|
274
|
+
config.logger.level = Logger::DEBUG
|
|
275
|
+
end
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
To replace the logger entirely:
|
|
279
|
+
|
|
280
|
+
```ruby
|
|
281
|
+
Backspin.configure do |config|
|
|
282
|
+
config.logger = Logger.new("log/backspin.log")
|
|
283
|
+
end
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
To disable Backspin logging entirely (for example in tests):
|
|
287
|
+
|
|
288
|
+
```ruby
|
|
289
|
+
Backspin.configure do |config|
|
|
290
|
+
config.logger = nil
|
|
291
|
+
end
|
|
292
|
+
```
|
|
293
|
+
|
|
224
294
|
### Credential Scrubbing
|
|
225
295
|
|
|
226
296
|
If the CLI interaction you are recording contains sensitive data in stdout/stderr, you should be careful to make sure it is not recorded to YAML.
|
|
@@ -252,6 +322,18 @@ Automatic scrubbing includes:
|
|
|
252
322
|
|
|
253
323
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
|
|
254
324
|
|
|
325
|
+
This repo also includes a decoupled full-stack fixture gem at `fixtures/projects/dummy_cli_gem` that uses Backspin the way downstream projects do. Run it with:
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
bundle exec rake full_stack:dummy_app
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
To re-record that fixture's committed YAML snapshots:
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
bundle exec rake full_stack:record_dummy_app
|
|
335
|
+
```
|
|
336
|
+
|
|
255
337
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
256
338
|
|
|
257
339
|
## Contributing
|
data/Rakefile
CHANGED
|
@@ -6,6 +6,33 @@ require "standard/rake"
|
|
|
6
6
|
|
|
7
7
|
RSpec::Core::RakeTask.new(:spec)
|
|
8
8
|
|
|
9
|
+
def run_in_fake_gem(command)
|
|
10
|
+
dummy_app_dir = File.expand_path("fixtures/projects/dummy_cli_gem", __dir__)
|
|
11
|
+
|
|
12
|
+
Bundler.with_unbundled_env do
|
|
13
|
+
Dir.chdir(dummy_app_dir) do
|
|
14
|
+
sh "bundle check || bundle install"
|
|
15
|
+
sh command
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
namespace :spec do
|
|
21
|
+
desc "Run the dummy fixture gem specs with Backspin (decoupled from main suite)"
|
|
22
|
+
task :fake_gem do
|
|
23
|
+
run_in_fake_gem("bundle exec rspec")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
desc "Re-record Backspin YAML fixtures for the dummy fixture gem"
|
|
27
|
+
task :fake_gem_record do
|
|
28
|
+
original_record_mode = ENV["RECORD_MODE"]
|
|
29
|
+
ENV["RECORD_MODE"] = "record"
|
|
30
|
+
run_in_fake_gem("bundle exec rspec")
|
|
31
|
+
ensure
|
|
32
|
+
ENV["RECORD_MODE"] = original_record_mode
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
9
36
|
task default: %i[spec standard]
|
|
10
37
|
|
|
11
38
|
load "release.rake" if File.exist?("release.rake")
|
data/backspin.gemspec
CHANGED
|
@@ -141,14 +141,16 @@ result.expected.stdout
|
|
|
141
141
|
- Error message is generated from `BackspinResult#error_message`.
|
|
142
142
|
- Do not duplicate `diff` content in exception formatting.
|
|
143
143
|
|
|
144
|
-
## Record Format Sketch (v4)
|
|
144
|
+
## Record Format Sketch (v4.1)
|
|
145
145
|
|
|
146
146
|
Single-snapshot format to match single-snapshot runtime model:
|
|
147
147
|
|
|
148
148
|
```yaml
|
|
149
149
|
---
|
|
150
|
-
format_version: "4.
|
|
150
|
+
format_version: "4.1"
|
|
151
|
+
first_recorded_at: "2026-01-01T00:00:00Z"
|
|
151
152
|
recorded_at: "2026-02-11T00:00:00Z"
|
|
153
|
+
record_count: 3
|
|
152
154
|
snapshot:
|
|
153
155
|
command_type: "Open3::Capture3"
|
|
154
156
|
args: ["echo", "hello"]
|
|
@@ -184,11 +186,11 @@ Status date: 2026-02-11
|
|
|
184
186
|
|
|
185
187
|
1. `Snapshot` and `BackspinResult` classes are implemented and wired into runtime paths.
|
|
186
188
|
2. `Backspin.run` and `Backspin.capture` now return `BackspinResult`.
|
|
187
|
-
3. `Record` persistence moved to v4 single-snapshot format (`snapshot` key, no `commands` array).
|
|
189
|
+
3. `Record` persistence moved to v4 single-snapshot format (`snapshot` key, no `commands` array), with v4.1 top-level metadata.
|
|
188
190
|
4. `Matcher` and `CommandDiff` now operate on expected/actual snapshots.
|
|
189
191
|
5. Legacy result/command layering was removed from `lib/`.
|
|
190
|
-
6. Specs have been migrated to the new result contract and v4 format.
|
|
191
|
-
7. Validation is green: `
|
|
192
|
+
6. Specs have been migrated to the new result contract and v4.1 format.
|
|
193
|
+
7. Validation is green: `89 examples, 0 failures` and Standard lint passes.
|
|
192
194
|
8. Public docs now use `result.actual` / `result.expected` terminology.
|
|
193
195
|
|
|
194
196
|
## Success Criteria
|
|
@@ -198,7 +200,7 @@ Status date: 2026-02-11
|
|
|
198
200
|
3. In `:verify` mode, `result.expected` is present, `result.verified?` is boolean, and mismatch cases populate `result.diff` plus `result.error_message`.
|
|
199
201
|
4. No multi-command result API remains in the public result contract.
|
|
200
202
|
5. Snapshot object exposes a stable single-command shape: `stdout`, `stderr`, `status`, `args`, `env`, `command_type`.
|
|
201
|
-
6. Record format uses one snapshot (v4), not a commands array.
|
|
203
|
+
6. Record format uses one snapshot (v4.x), not a commands array.
|
|
202
204
|
7. Existing strict verification behavior remains: default raises `Backspin::VerificationError`, while `raise_on_verification_failure = false` returns a failed result without raising.
|
|
203
205
|
8. End-to-end Unix command examples are covered in specs: `echo` record/verify, `ls -1` record/verify, `date` mismatch behavior (or matcher override), and captured `grep | wc` pipeline output via `Backspin.capture`.
|
|
204
206
|
9. Matcher behavior is preserved: default matching remains stdout/stderr/status, and custom `matcher:` contract (Proc, hash fields, `:all`) continues to work for both run and capture verification.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: ../../..
|
|
3
|
+
specs:
|
|
4
|
+
backspin (0.11.0)
|
|
5
|
+
logger
|
|
6
|
+
|
|
7
|
+
PATH
|
|
8
|
+
remote: .
|
|
9
|
+
specs:
|
|
10
|
+
dummy_cli_gem (0.1.0)
|
|
11
|
+
|
|
12
|
+
GEM
|
|
13
|
+
remote: https://rubygems.org/
|
|
14
|
+
specs:
|
|
15
|
+
diff-lcs (1.6.2)
|
|
16
|
+
logger (1.7.0)
|
|
17
|
+
rake (13.3.1)
|
|
18
|
+
rspec (3.13.2)
|
|
19
|
+
rspec-core (~> 3.13.0)
|
|
20
|
+
rspec-expectations (~> 3.13.0)
|
|
21
|
+
rspec-mocks (~> 3.13.0)
|
|
22
|
+
rspec-core (3.13.6)
|
|
23
|
+
rspec-support (~> 3.13.0)
|
|
24
|
+
rspec-expectations (3.13.5)
|
|
25
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
26
|
+
rspec-support (~> 3.13.0)
|
|
27
|
+
rspec-mocks (3.13.7)
|
|
28
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
29
|
+
rspec-support (~> 3.13.0)
|
|
30
|
+
rspec-support (3.13.7)
|
|
31
|
+
|
|
32
|
+
PLATFORMS
|
|
33
|
+
aarch64-linux
|
|
34
|
+
ruby
|
|
35
|
+
|
|
36
|
+
DEPENDENCIES
|
|
37
|
+
backspin!
|
|
38
|
+
dummy_cli_gem!
|
|
39
|
+
rake (~> 13)
|
|
40
|
+
rspec (~> 3)
|
|
41
|
+
|
|
42
|
+
CHECKSUMS
|
|
43
|
+
backspin (0.11.0)
|
|
44
|
+
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
|
|
45
|
+
dummy_cli_gem (0.1.0)
|
|
46
|
+
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
47
|
+
rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
|
|
48
|
+
rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
|
|
49
|
+
rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
|
|
50
|
+
rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
|
|
51
|
+
rspec-mocks (3.13.7) sha256=0979034e64b1d7a838aaaddf12bf065ea4dc40ef3d4c39f01f93ae2c66c62b1c
|
|
52
|
+
rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
|
|
53
|
+
|
|
54
|
+
BUNDLED WITH
|
|
55
|
+
4.0.5
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
MIT License
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# DummyCliGem Fixture
|
|
2
|
+
|
|
3
|
+
This is a minimal fixture gem used by Backspin's full-stack integration checks.
|
|
4
|
+
|
|
5
|
+
- It shells out to standard unix utilities (`echo`, `ls`).
|
|
6
|
+
- Its RSpec suite verifies command output through `Backspin.run`.
|
|
7
|
+
- Snapshot YAML records are stored in-repo under `spec/fixtures/backspin`.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/dummy_cli_gem/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "dummy_cli_gem"
|
|
7
|
+
spec.version = DummyCliGem::VERSION
|
|
8
|
+
spec.authors = ["Backspin"]
|
|
9
|
+
spec.email = ["noreply@example.com"]
|
|
10
|
+
spec.summary = "Dummy CLI gem fixture for Backspin full-stack verification"
|
|
11
|
+
spec.description = "Fixture gem that shells out to unix utilities and is tested via Backspin snapshots."
|
|
12
|
+
spec.homepage = "https://example.com/dummy_cli_gem"
|
|
13
|
+
spec.license = "MIT"
|
|
14
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 3.1.0")
|
|
15
|
+
|
|
16
|
+
spec.files = Dir.chdir(__dir__) do
|
|
17
|
+
Dir["lib/**/*.rb", "exe/*", "spec/**/*", "script/**/*", "README.md", "LICENSE.txt"]
|
|
18
|
+
end
|
|
19
|
+
spec.bindir = "exe"
|
|
20
|
+
spec.executables = ["dummy_cli_gem"]
|
|
21
|
+
spec.require_paths = ["lib"]
|
|
22
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
format_version: '4.1'
|
|
3
|
+
first_recorded_at: '2026-02-11T01:57:42-06:00'
|
|
4
|
+
recorded_at: '2026-02-11T01:57:42-06:00'
|
|
5
|
+
record_count: 1
|
|
6
|
+
snapshot:
|
|
7
|
+
command_type: Open3::Capture3
|
|
8
|
+
args:
|
|
9
|
+
- ruby
|
|
10
|
+
- exe/dummy_cli_gem
|
|
11
|
+
- echo
|
|
12
|
+
- hello from dummy gem
|
|
13
|
+
stdout: 'hello from dummy gem
|
|
14
|
+
|
|
15
|
+
'
|
|
16
|
+
stderr: ''
|
|
17
|
+
status: 0
|
|
18
|
+
recorded_at: '2026-02-11T01:57:42-06:00'
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
format_version: '4.1'
|
|
3
|
+
first_recorded_at: '2026-02-11T01:57:42-06:00'
|
|
4
|
+
recorded_at: '2026-02-11T01:57:42-06:00'
|
|
5
|
+
record_count: 1
|
|
6
|
+
snapshot:
|
|
7
|
+
command_type: Open3::Capture3
|
|
8
|
+
args:
|
|
9
|
+
- ruby
|
|
10
|
+
- exe/dummy_cli_gem
|
|
11
|
+
- list
|
|
12
|
+
- spec/fixtures/listing_target
|
|
13
|
+
stdout: 'alpha.txt
|
|
14
|
+
|
|
15
|
+
'
|
|
16
|
+
stderr: ''
|
|
17
|
+
status: 0
|
|
18
|
+
recorded_at: '2026-02-11T01:57:42-06:00'
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
|
|
5
|
+
module DummyCliGem
|
|
6
|
+
module CLI
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def run(argv, out: $stdout, err: $stderr)
|
|
10
|
+
command = argv.first
|
|
11
|
+
|
|
12
|
+
case command
|
|
13
|
+
when "echo"
|
|
14
|
+
text = argv[1]
|
|
15
|
+
return usage(err) if text.nil? || text.empty?
|
|
16
|
+
|
|
17
|
+
stdout, stderr, status = Open3.capture3("echo", text)
|
|
18
|
+
when "list"
|
|
19
|
+
target = argv[1] || "."
|
|
20
|
+
stdout, stderr, status = Open3.capture3("ls", "-1", target)
|
|
21
|
+
else
|
|
22
|
+
return usage(err)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
out.print(stdout)
|
|
26
|
+
err.print(stderr)
|
|
27
|
+
status.exitstatus
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def usage(err)
|
|
31
|
+
err.puts("usage: dummy_cli_gem echo <text> | dummy_cli_gem list <path>")
|
|
32
|
+
1
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "DummyCliGem Backspin full-stack fixture" do
|
|
6
|
+
let(:project_root) { Pathname(__dir__).join("..").expand_path }
|
|
7
|
+
|
|
8
|
+
it "verifies echo command output from committed YAML" do
|
|
9
|
+
expect(BACKSPIN_DIR.join("dummy_echo.yml")).to exist
|
|
10
|
+
|
|
11
|
+
result = Backspin.run(
|
|
12
|
+
["ruby", "exe/dummy_cli_gem", "echo", "hello from dummy gem"],
|
|
13
|
+
name: "dummy_echo"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
expect(result).to be_verified
|
|
17
|
+
expect(result.actual.stdout).to eq("hello from dummy gem\n")
|
|
18
|
+
expect(result.actual.stderr).to eq("")
|
|
19
|
+
expect(result.actual.status).to eq(0)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "verifies list command output from committed YAML" do
|
|
23
|
+
expect(BACKSPIN_DIR.join("dummy_ls.yml")).to exist
|
|
24
|
+
|
|
25
|
+
result = Backspin.run(
|
|
26
|
+
["ruby", "exe/dummy_cli_gem", "list", "spec/fixtures/listing_target"],
|
|
27
|
+
name: "dummy_ls"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
expect(result).to be_verified
|
|
31
|
+
expect(result.actual.stdout).to eq("alpha.txt\n")
|
|
32
|
+
expect(result.actual.stderr).to eq("")
|
|
33
|
+
expect(result.actual.status).to eq(0)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "uses current Backspin record format for fixture YAML files" do
|
|
37
|
+
%w[dummy_echo dummy_ls].each do |record_name|
|
|
38
|
+
record_path = BACKSPIN_DIR.join("#{record_name}.yml")
|
|
39
|
+
expect(record_path).to exist
|
|
40
|
+
|
|
41
|
+
record_data = YAML.load_file(record_path)
|
|
42
|
+
expect(record_data["format_version"]).to eq("4.1")
|
|
43
|
+
expect(record_data["snapshot"]["command_type"]).to eq("Open3::Capture3")
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
format_version: '4.1'
|
|
3
|
+
first_recorded_at: '2026-02-11T07:43:21+00:00'
|
|
4
|
+
recorded_at: '2026-02-11T07:43:21+00:00'
|
|
5
|
+
record_count: 1
|
|
6
|
+
snapshot:
|
|
7
|
+
command_type: Open3::Capture3
|
|
8
|
+
args:
|
|
9
|
+
- ruby
|
|
10
|
+
- exe/dummy_cli_gem
|
|
11
|
+
- echo
|
|
12
|
+
- hello from dummy gem
|
|
13
|
+
stdout: 'hello from dummy gem
|
|
14
|
+
|
|
15
|
+
'
|
|
16
|
+
stderr: ''
|
|
17
|
+
status: 0
|
|
18
|
+
recorded_at: '2026-02-11T07:43:21+00:00'
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
format_version: '4.1'
|
|
3
|
+
first_recorded_at: '2026-02-11T07:43:21+00:00'
|
|
4
|
+
recorded_at: '2026-02-11T07:43:21+00:00'
|
|
5
|
+
record_count: 1
|
|
6
|
+
snapshot:
|
|
7
|
+
command_type: Open3::Capture3
|
|
8
|
+
args:
|
|
9
|
+
- ruby
|
|
10
|
+
- exe/dummy_cli_gem
|
|
11
|
+
- list
|
|
12
|
+
- spec/fixtures/listing_target
|
|
13
|
+
stdout: 'alpha.txt
|
|
14
|
+
|
|
15
|
+
'
|
|
16
|
+
stderr: ''
|
|
17
|
+
status: 0
|
|
18
|
+
recorded_at: '2026-02-11T07:43:21+00:00'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
alpha fixture file
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "pathname"
|
|
5
|
+
require "yaml"
|
|
6
|
+
require "backspin"
|
|
7
|
+
require "dummy_cli_gem"
|
|
8
|
+
|
|
9
|
+
BACKSPIN_DIR = Pathname(__dir__).join("fixtures", "backspin")
|
|
10
|
+
|
|
11
|
+
Backspin.configure do |config|
|
|
12
|
+
config.backspin_dir = BACKSPIN_DIR
|
|
13
|
+
config.logger = nil
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
RSpec.configure do |config|
|
|
17
|
+
config.disable_monkey_patching!
|
|
18
|
+
|
|
19
|
+
config.expect_with :rspec do |expectations|
|
|
20
|
+
expectations.syntax = :expect
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "logger"
|
|
3
4
|
require "pathname"
|
|
4
5
|
|
|
5
6
|
module Backspin
|
|
@@ -10,6 +11,8 @@ module Backspin
|
|
|
10
11
|
attr_accessor :backspin_dir
|
|
11
12
|
# Whether to raise an exception when verification fails in `run`/`capture` - defaults to true
|
|
12
13
|
attr_accessor :raise_on_verification_failure
|
|
14
|
+
# Logger for Backspin diagnostics - defaults to WARN level, logfmt-lite format
|
|
15
|
+
attr_accessor :logger
|
|
13
16
|
# Regex patterns to scrub from saved output
|
|
14
17
|
attr_reader :credential_patterns
|
|
15
18
|
|
|
@@ -18,6 +21,7 @@ module Backspin
|
|
|
18
21
|
@raise_on_verification_failure = true
|
|
19
22
|
@credential_patterns = default_credential_patterns
|
|
20
23
|
@backspin_dir = Pathname(Dir.pwd).join("fixtures", "backspin")
|
|
24
|
+
@logger = default_logger
|
|
21
25
|
end
|
|
22
26
|
|
|
23
27
|
def add_credential_pattern(pattern)
|
|
@@ -34,6 +38,15 @@ module Backspin
|
|
|
34
38
|
|
|
35
39
|
private
|
|
36
40
|
|
|
41
|
+
def default_logger
|
|
42
|
+
logger = Logger.new($stdout)
|
|
43
|
+
logger.level = Logger::WARN
|
|
44
|
+
logger.formatter = proc { |severity, _time, _progname, msg|
|
|
45
|
+
"level=#{severity.downcase} lib=backspin #{msg}\n"
|
|
46
|
+
}
|
|
47
|
+
logger
|
|
48
|
+
end
|
|
49
|
+
|
|
37
50
|
# Some default patterns for common credential types
|
|
38
51
|
def default_credential_patterns
|
|
39
52
|
[
|