suture 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://travis-ci.org/testdouble/suture.svg?branch=master)](https://travis-ci.org/testdouble/suture) [![Code Climate](https://codeclimate.com/github/testdouble/suture/badges/gpa.svg)](https://codeclimate.com/github/testdouble/suture) [![Test Coverage](https://codeclimate.com/github/testdouble/suture/badges/coverage.svg)](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
|