suture 1.0.0 → 1.1.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/.codeclimate.yml +1 -0
- data/CHANGELOG.md +19 -2
- data/README.md +39 -23
- data/Rakefile +1 -1
- data/lib/suture/create/validates_plan.rb +55 -6
- data/lib/suture/surgeon/auditor.rb +28 -13
- data/lib/suture/value/plan.rb +3 -1
- data/lib/suture/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 03b931eb888e1ce5e2d66f44c82063095b2d77b7
|
4
|
+
data.tar.gz: 395821b0b2c7075bfebddec65debbe8dc1860f4b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f909ce361c7a0b62da87a7332b022725d1c38e02d52c6aa0b4e7f884be841750e2d8b763f8b805cc48f6eff09ebe639d26d330254ef6a04933bba81076e3a335
|
7
|
+
data.tar.gz: f054acf5f1bb4a577b67973df7f093d26a93258cd2435c86c93885c7e6f4f4c5e7b17c5b5d5a516c4756cbf7816e2cdef990a4fd04191da8f41d519de16cc938
|
data/.codeclimate.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,25 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## [
|
3
|
+
## [v1.0.0](https://github.com/testdouble/suture/tree/v1.0.0) (2016-09-07)
|
4
|
+
[Full Changelog](https://github.com/testdouble/suture/compare/v0.5.0...v1.0.0)
|
4
5
|
|
5
|
-
|
6
|
+
**Implemented enhancements:**
|
7
|
+
|
8
|
+
- API to drop Suture's database [\#31](https://github.com/testdouble/suture/issues/31)
|
9
|
+
|
10
|
+
**Closed issues:**
|
11
|
+
|
12
|
+
- Document expected\_error\_types feature [\#55](https://github.com/testdouble/suture/issues/55)
|
13
|
+
- Document rails env-specific settings [\#44](https://github.com/testdouble/suture/issues/44)
|
14
|
+
- Rails project [\#35](https://github.com/testdouble/suture/issues/35)
|
15
|
+
- Add sensible defaults to active record objects [\#30](https://github.com/testdouble/suture/issues/30)
|
16
|
+
- Document how to use Coverage [\#26](https://github.com/testdouble/suture/issues/26)
|
17
|
+
- Support awesome\_print [\#23](https://github.com/testdouble/suture/issues/23)
|
18
|
+
- Document Value-ifying methods with side effects [\#18](https://github.com/testdouble/suture/issues/18)
|
19
|
+
- Document configuration [\#17](https://github.com/testdouble/suture/issues/17)
|
20
|
+
|
21
|
+
## [v0.5.0](https://github.com/testdouble/suture/tree/v0.5.0) (2016-09-05)
|
22
|
+
[Full Changelog](https://github.com/testdouble/suture/compare/v0.4.0...v0.5.0)
|
6
23
|
|
7
24
|
**Implemented enhancements:**
|
8
25
|
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Suture
|
1
|
+
# Suture 🏥
|
2
2
|
|
3
3
|
[](https://travis-ci.org/testdouble/suture) [](https://codeclimate.com/github/testdouble/suture) [](https://codeclimate.com/github/testdouble/suture/coverage)
|
4
4
|
|
@@ -65,7 +65,7 @@ much easier to verify return values.
|
|
65
65
|
Since any changes to the code while it's untested are very dangerous, it's
|
66
66
|
important to minimize changes made for the sake of creating a clear seam.
|
67
67
|
|
68
|
-
### 2. Create our
|
68
|
+
### 2. Create our seam
|
69
69
|
|
70
70
|
Next, we introduce Suture to the call site so we can start analyzing its
|
71
71
|
behavior:
|
@@ -392,7 +392,9 @@ In general, most configuration options can be set in several places:
|
|
392
392
|
to an expected ENV var named `SUTURE_RECORD_CALLS` and can be set from the
|
393
393
|
command line like so: `SUTURE_RECORD_CALLS=true bundle exec rails server`, to
|
394
394
|
tell Suture to record all your interactions with your seams without touching the
|
395
|
-
source code.
|
395
|
+
source code. (Note: this is really only appropriate if your codebase only has one
|
396
|
+
Suture seam in progress at a time, since using a global env var configuration
|
397
|
+
for one seam's sake will erroneously impact the other.)
|
396
398
|
|
397
399
|
* Globally, via the top-level `Suture.config` method. Most variables can be set
|
398
400
|
via this top-level configuration, like
|
@@ -430,28 +432,35 @@ the `new` path at the exclusion of the `old` path (unless a mode flag like
|
|
430
432
|
current working directory to the Sqlite3 database Suture uses to record and
|
431
433
|
playback calls
|
432
434
|
|
433
|
-
* _record_calls_ - (Default: false) - when set to true, the `old` path is called
|
435
|
+
* _record_calls_ - (Default: `false`) - when set to true, the `old` path is called
|
434
436
|
(regardless of whether `new` is set) and its arguments and result (be it a return
|
435
437
|
value or an expected raised error) is recorded into the Suture database for the
|
436
438
|
purpose of more coverage for calls to `Suture.verify`. [Read
|
437
439
|
more](#3-record-the-current-behavior)
|
438
440
|
|
439
|
-
* _call_both_ - (Default: false) - when set to true, the `new` path is invoked,
|
441
|
+
* _call_both_ - (Default: `false`) - when set to true, the `new` path is invoked,
|
440
442
|
then the `old` path is invoked, each with the seam's `args`. The return value
|
441
443
|
from each is compared with the `comparator`, and if they are not equivalent, then
|
442
444
|
a `Suture::Error::ResultMismatch` is raised. Intended after the `new` path is
|
443
445
|
initially developed and to be run in pre-production environments. [Read
|
444
446
|
more](#staging)
|
445
447
|
|
446
|
-
* _fallback_on_error_ - (Default: false) - designed to be run in production after
|
448
|
+
* _fallback_on_error_ - (Default: `false`) - designed to be run in production after
|
447
449
|
the initial development of the new code path, when set to true, Suture will
|
448
450
|
invoke the `new` code path. If `new` raises an error that isn't an
|
449
451
|
`expected_error_type`, then Suture will invoke the `old` path with the same args
|
450
452
|
in an attempt to recover a working state for the user. [Read more](#production)
|
451
453
|
|
452
|
-
* _raise_on_result_mismatch_ - (Default: true) - when set to true, the
|
454
|
+
* _raise_on_result_mismatch_ - (Default: `true`) - when set to true, the
|
453
455
|
`call_both` mode will merely log incidents of result mismatches, as opposed to
|
454
|
-
raising `Suture::Error::ResultMismatch
|
456
|
+
raising `Suture::Error::ResultMismatch`
|
457
|
+
|
458
|
+
* _return_old_on_result_mismatch_ - (Default: `false`) - when set to true, the
|
459
|
+
`call_both` mode will return the result of the `old` code path instead of the
|
460
|
+
`new` code path. This is useful when you want to log mismatches in production
|
461
|
+
(i.e. when you're very confident it's safe and fast enough to use `call_both` in
|
462
|
+
production), but want to fallback to the `old` path in the case of a mismatch
|
463
|
+
to minimize disruption to your users
|
455
464
|
|
456
465
|
* _comparator_ - (Default: `Suture::Comparator.new`) - determines how return
|
457
466
|
values from the Suture are compared when invoking `Suture.verify` or when
|
@@ -471,16 +480,14 @@ certain cases, setting `:expected_error_types => [WidgetError]` will result in:
|
|
471
480
|
* `Suture.create`, when `fallback_on_error` is enabled, will allow expected
|
472
481
|
errors raised by the `new` path to propogate, as opposed to logging &
|
473
482
|
rescuing them before calling the `old` path as a fallback
|
474
|
-
* Additionally, `Suture.verify` can be passed `expected_error_types` to squelch
|
475
|
-
warning logs that result from unexpectedly raised errors
|
476
483
|
|
477
|
-
* _disable_ - (Default: false) - when enabled, Suture will attempt to revert to
|
484
|
+
* _disable_ - (Default: `false`) - when enabled, Suture will attempt to revert to
|
478
485
|
the original behavior of the `old` path and take no special action. Useful in
|
479
486
|
cases where a bug is discovered in a deployed environment and you simply want
|
480
487
|
to hit the brakes on any new code path experiments by setting
|
481
488
|
`SUTURE_DISABLE=true` globally
|
482
489
|
|
483
|
-
* _dup_args_ - (Default: false) - when enabled, Suture will call `dup` on each
|
490
|
+
* _dup_args_ - (Default: `false`) - when enabled, Suture will call `dup` on each
|
484
491
|
of the args passed to the `old` and/or `new` code paths. Useful when the code
|
485
492
|
path(s) mutate the arguments in such a way as to prevent `call_both` or
|
486
493
|
`fallback_on_error` from being effective
|
@@ -499,9 +506,13 @@ unexpected error (see `expected_error_types`).
|
|
499
506
|
|
500
507
|
#### Suture.verify
|
501
508
|
|
502
|
-
|
503
|
-
|
504
|
-
|
509
|
+
`Suture.verify(name, [options hash])`
|
510
|
+
|
511
|
+
Many of the settings for `Suture.verify` mirror the settings available to
|
512
|
+
`Suture.create`. In general, the two methods' common options should be configured
|
513
|
+
identically for a given seam; this is necessary, because the `Suture.verify` call
|
514
|
+
site doesn't depend on (or know about) any `Suture.create` call site of the same
|
515
|
+
name; the only resource they share is the recorded calls in Suture's database.
|
505
516
|
|
506
517
|
* _name_ - (Required) - should be the same name as a seam for which some number
|
507
518
|
of recorded calls exist
|
@@ -511,27 +522,31 @@ set of `args` and have its result compared to that of each recording. This is
|
|
511
522
|
used in lieu of `old` or `new`, since the subject of a `Suture.verify` test might
|
512
523
|
be either (or neither!)
|
513
524
|
|
514
|
-
*
|
525
|
+
* _database_path_ - (Default: `"db/suture.sqlite3"`) - as with `Suture.create`, a
|
526
|
+
custom database path can be set for almost any invocation of Suture, and
|
527
|
+
`Suture.verify is no exception`
|
528
|
+
|
529
|
+
* _verify_only_ - (Default: `nil`) - when set to an ID, Suture.verify` will only
|
515
530
|
run against recorded calls for the matching ID. This option is meant to be used
|
516
531
|
to focus work on resolving a single verification failure
|
517
532
|
|
518
|
-
* _fail_fast_ - (Default: false) - `Suture.verify` will, by default, run against
|
533
|
+
* _fail_fast_ - (Default: `false`) - `Suture.verify` will, by default, run against
|
519
534
|
every single recording, aggregating and reporting on all errors (just like, say,
|
520
535
|
RSpec or Minitest would). However, if the seam is slow to invoke or if you
|
521
536
|
confidently expect all of the recordings to pass verification, `fail_fast` is an
|
522
537
|
appropriate option to set.
|
523
538
|
|
524
|
-
* _call_limit_ - (Default: nil) - when set to a number, Suture will only verify
|
539
|
+
* _call_limit_ - (Default: `nil`) - when set to a number, Suture will only verify
|
525
540
|
up to the set number of recorded calls. Because Suture randomizes the order of
|
526
541
|
verifications by default, you can see this as setting Suture.verify to sample a
|
527
542
|
random smattering of `call_limit` recordings as a smell test. Potentially useful
|
528
543
|
when a seam is very slow
|
529
544
|
|
530
|
-
* _time_limit_ - (Default: nil) - when set to a number (in seconds), Suture will
|
545
|
+
* _time_limit_ - (Default: `nil`) - when set to a number (in seconds), Suture will
|
531
546
|
stop running verifications against recordings once `time_limit` seconds has
|
532
547
|
elapsed. Useful when a seam is very slow to invoke
|
533
548
|
|
534
|
-
* _error_message_limit_ - (Default: nil) - when set to a number, Suture will only
|
549
|
+
* _error_message_limit_ - (Default: `nil`) - when set to a number, Suture will only
|
535
550
|
print up to `error_message_limit` failure messages. That way, if you currently
|
536
551
|
have hundreds of verifications failing, your console isn't overwhelmed by them on
|
537
552
|
each run of `Suture.verify`
|
@@ -548,9 +563,10 @@ used by `Suture.verify` to ensure the results are comparable. [Read
|
|
548
563
|
more](#creating-a-custom-comparator) on creating custom comparators
|
549
564
|
)
|
550
565
|
|
551
|
-
*
|
552
|
-
|
553
|
-
|
566
|
+
* _expected_error_types_ - (Default: `[]`) - this option has little impact on
|
567
|
+
`Suture.verify` (since each recording will either verify a return value or an
|
568
|
+
error in its own right), however it can be set to squelch log messages warning
|
569
|
+
that errors were raised when invoking the `subject`
|
554
570
|
|
555
571
|
* _after_subject_ - a `call`-able hook that runs after `subject` is invoked. If
|
556
572
|
`subject` raises an error, it is not invoked
|
data/Rakefile
CHANGED
@@ -47,7 +47,7 @@ if Gem.ruby_version >= Gem::Version.new("2.2.2")
|
|
47
47
|
puts "-------> #{cmd}"
|
48
48
|
system cmd
|
49
49
|
end
|
50
|
-
Rake::Task["release:
|
50
|
+
Rake::Task["release:rubygem_push"].enhance([:changelog, :changelog_commit])
|
51
51
|
end
|
52
52
|
|
53
53
|
|
@@ -26,32 +26,81 @@ module Suture
|
|
26
26
|
CONFLICTS = [
|
27
27
|
lambda { |plan|
|
28
28
|
if plan.record_calls && !plan.database_path
|
29
|
-
|
29
|
+
<<-MSG.gsub(/^ {12}/,'')
|
30
|
+
:record_calls is enabled, but :database_path is nil, so Suture
|
31
|
+
doesn't know where to record calls to the seam.
|
32
|
+
MSG
|
30
33
|
end
|
31
34
|
},
|
32
35
|
lambda { |plan|
|
33
36
|
if plan.record_calls && plan.call_both
|
34
|
-
|
37
|
+
<<-MSG.gsub(/^ {12}/,'')
|
38
|
+
:record_calls & :call_both are both enabled and conflict with one
|
39
|
+
another. :record_calls will only invoke the old code path (intended
|
40
|
+
for characterization of the old code path and initial development
|
41
|
+
of the new code path), whereas :call_both will invoke the new path
|
42
|
+
and the old to compare their results after development of the new
|
43
|
+
code path is initially complete (typically in a pre-production
|
44
|
+
environment to validate the behavior of the new code path is
|
45
|
+
consistent with the old). If you're still actively developing the
|
46
|
+
new code path and need more recordings to feed Suture.verify,
|
47
|
+
disable :call_both; otherwise, it's likely time to turn off
|
48
|
+
:record_calls on this seam.
|
49
|
+
MSG
|
35
50
|
end
|
36
51
|
},
|
37
52
|
lambda { |plan|
|
38
53
|
if plan.record_calls && plan.fallback_on_error
|
39
|
-
|
54
|
+
<<-MSG.gsub(/^ {12}/,'')
|
55
|
+
:record_calls & :fallback_on_error are both enabled and conflict with
|
56
|
+
one another. :record_calls will only invoke the old code path
|
57
|
+
(intended for characterization of the old code path and initial
|
58
|
+
development of the new code path), whereas :fallback_on_error will
|
59
|
+
call the new code path unless an error is raised, in which case it
|
60
|
+
will fall back on the old code path.
|
61
|
+
MSG
|
40
62
|
end
|
41
63
|
},
|
42
64
|
lambda { |plan|
|
43
65
|
if plan.call_both && plan.fallback_on_error
|
44
|
-
|
66
|
+
<<-MSG.gsub(/^ {12}/,'')
|
67
|
+
:call_both & :fallback_on_error are both enabled and conflict with
|
68
|
+
one another. :call_both is designed for pre-production environments
|
69
|
+
and will call both the old and new code paths to compare their
|
70
|
+
results, whereas :fallback_on_error is designed for production
|
71
|
+
environments where it is safe to call the old code path in the
|
72
|
+
event that the new code path fails unexpectedly
|
73
|
+
MSG
|
45
74
|
end
|
46
75
|
},
|
47
76
|
lambda { |plan|
|
48
77
|
if plan.call_both && !plan.new.respond_to?(:call)
|
49
|
-
|
78
|
+
<<-MSG.gsub(/^ {12}/,'')
|
79
|
+
:call_both is set but :new is either not set or is not callable. In
|
80
|
+
order to call both code paths, both :old and :new must be set and
|
81
|
+
callable
|
82
|
+
MSG
|
50
83
|
end
|
51
84
|
},
|
52
85
|
lambda { |plan|
|
53
86
|
if plan.fallback_on_error && !plan.new.respond_to?(:call)
|
54
|
-
|
87
|
+
<<-MSG.gsub(/^ {12}/,'')
|
88
|
+
:fallback_on_error is set but :new is either not set or is not
|
89
|
+
callable. This mode is designed for after the :new code path has
|
90
|
+
been developed and run in production-like environments, where :old
|
91
|
+
is only kept around as a fallback to retry in the event that
|
92
|
+
:new raises an unexpected error. Either specify a :new code path or
|
93
|
+
disable :fallback_on_error.
|
94
|
+
MSG
|
95
|
+
end
|
96
|
+
},
|
97
|
+
lambda { |plan|
|
98
|
+
if !plan.raise_on_result_mismatch && !plan.call_both
|
99
|
+
<<-MSG.gsub(/^ {12}/,'')
|
100
|
+
:raise_on_result_mismatch was disabled but :call_both is not enabled.
|
101
|
+
This option only applies to the :call_both mode, and will have no
|
102
|
+
impact when set for other modes
|
103
|
+
MSG
|
55
104
|
end
|
56
105
|
}
|
57
106
|
]
|
@@ -10,20 +10,35 @@ module Suture::Surgeon
|
|
10
10
|
new_result = scalpel.cut(plan, :new)
|
11
11
|
old_result = scalpel.cut(plan, :old)
|
12
12
|
if !plan.comparator.call(old_result, new_result)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
#{new_result.inspect}
|
17
|
-
```
|
18
|
-
The old result was: ```
|
19
|
-
#{old_result.inspect}
|
20
|
-
```
|
21
|
-
MSG
|
22
|
-
if plan.raise_on_result_mismatch
|
23
|
-
raise Suture::Error::ResultMismatch.new(plan, new_result, old_result)
|
24
|
-
end
|
13
|
+
handle_mismatch(plan, old_result, new_result)
|
14
|
+
else
|
15
|
+
new_result
|
25
16
|
end
|
26
|
-
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def handle_mismatch(plan, old_result, new_result)
|
22
|
+
log_warning(plan, old_result, new_result)
|
23
|
+
if plan.raise_on_result_mismatch
|
24
|
+
raise Suture::Error::ResultMismatch.new(plan, new_result, old_result)
|
25
|
+
elsif plan.return_old_on_result_mismatch
|
26
|
+
old_result
|
27
|
+
else
|
28
|
+
new_result
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def log_warning(plan, old_result, new_result)
|
33
|
+
log_warn <<-MSG.gsub(/^ {8}/,'')
|
34
|
+
Seam #{plan.name.inspect} is set to :call_both the :new and :old code
|
35
|
+
paths, but they did not match. The new result was: ```
|
36
|
+
#{new_result.inspect}
|
37
|
+
```
|
38
|
+
The old result was: ```
|
39
|
+
#{old_result.inspect}
|
40
|
+
```
|
41
|
+
MSG
|
27
42
|
end
|
28
43
|
end
|
29
44
|
end
|
data/lib/suture/value/plan.rb
CHANGED
@@ -2,7 +2,8 @@ module Suture::Value
|
|
2
2
|
class Plan
|
3
3
|
attr_reader :name, :old, :new, :args, :after_new, :after_old, :on_new_error,
|
4
4
|
:on_old_error, :database_path, :record_calls, :comparator,
|
5
|
-
:call_both, :raise_on_result_mismatch,
|
5
|
+
:call_both, :raise_on_result_mismatch,
|
6
|
+
:return_old_on_result_mismatch, :fallback_on_error,
|
6
7
|
:expected_error_types, :disable, :dup_args
|
7
8
|
|
8
9
|
def initialize(attrs = {})
|
@@ -19,6 +20,7 @@ module Suture::Value
|
|
19
20
|
@comparator = attrs[:comparator]
|
20
21
|
@call_both = !!attrs[:call_both]
|
21
22
|
@raise_on_result_mismatch = !!attrs[:raise_on_result_mismatch]
|
23
|
+
@return_old_on_result_mismatch = !!attrs[:return_old_on_result_mismatch]
|
22
24
|
@fallback_on_error = !!attrs[:fallback_on_error]
|
23
25
|
@expected_error_types = attrs[:expected_error_types] || []
|
24
26
|
@disable = !!attrs[:disable]
|
data/lib/suture/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: suture
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Searls
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-09-
|
11
|
+
date: 2016-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sqlite3
|