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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 65850d2b933384f85c0cd13ec67fe92efcb08320
4
- data.tar.gz: ba005e2f0e87b693f89948102559dfccfba7720b
3
+ metadata.gz: 03b931eb888e1ce5e2d66f44c82063095b2d77b7
4
+ data.tar.gz: 395821b0b2c7075bfebddec65debbe8dc1860f4b
5
5
  SHA512:
6
- metadata.gz: 7132a9cfa0499d89c786c7ccea1c7ed3da896ec786d3249b1aa3d00433eb0387d3be8917e3bb589f917f888624ab4bab71dc3c2f751a599a100422c09b717f2f
7
- data.tar.gz: cd7f1fea872d8887a51f8b65d03cd5bff5189d9d4032141c0862c02265aec1c2acd4552e24e77e611fbb10e57ebd62796d2a956edf754c6dcde57df43438f0ff
6
+ metadata.gz: f909ce361c7a0b62da87a7332b022725d1c38e02d52c6aa0b4e7f884be841750e2d8b763f8b805cc48f6eff09ebe639d26d330254ef6a04933bba81076e3a335
7
+ data.tar.gz: f054acf5f1bb4a577b67973df7f093d26a93258cd2435c86c93885c7e6f4f4c5e7b17c5b5d5a516c4756cbf7816e2cdef990a4fd04191da8f41d519de16cc938
data/.codeclimate.yml CHANGED
@@ -26,3 +26,4 @@ ratings:
26
26
  exclude_paths:
27
27
  - test/
28
28
  - safe/fixtures/
29
+ - example/
data/CHANGELOG.md CHANGED
@@ -1,8 +1,25 @@
1
1
  # Change Log
2
2
 
3
- ## [Unreleased](https://github.com/testdouble/suture/tree/HEAD)
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
- [Full Changelog](https://github.com/testdouble/suture/compare/v0.4.0...HEAD)
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 suture
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
- Many of the settings for `Suture.verify` are analogous to the same settings in
503
- `Suture.create` and are generally expected to be configured in the same way, as
504
- if symmetrically with the `Suture.create` call of the seam under test:
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
- * _verify_only_ - (Default: nil) - when set to an ID, Suture.verify` will only
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
- * _database_path_ - (Default: `"db/suture.sqlite3"`) - as with `Suture.create`, a
552
- custom database path can be set for almost any invocation of Suture, and
553
- `Suture.verify is no exception`
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:source_control_push"].enhance([:changelog, :changelog_commit])
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
- ":record_calls is enabled, but :database_path is nil, so Suture doesn't know where to record calls to the seam."
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
- ":record_calls & :call_both are both enabled and conflict with one another. :record_calls will only invoke the old code path (intended for characterization of the old code path and initial development of the new code path), whereas :call_both will invoke the new path and the old to compare their results after development of the new code path is initially complete (typically in a pre-production environment to validate the behavior of the new code path is consistent with the old). If you're still actively developing the new code path and need more recordings to feed Suture.verify, disable :call_both; otherwise, it's likely time to turn off :record_calls on this seam."
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
- ":record_calls & :fallback_on_error are both enabled and conflict with one another. :record_calls will only invoke the old code path (intended for characterization of the old code path and initial development of the new code path), whereas :fallback_on_error will call the new code path unless an error is raised, in which case it will fall back on the old code path."
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
- ":call_both & :fallback_on_error are both enabled and conflict with one another. :call_both is designed for pre-production environments and will call both the old and new code paths to compare their results, whereas :fallback_on_error is designed for production environments where it is safe to call the old code path in the event that the new code path fails unexpectedly"
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
- ":call_both is set but :new is either not set or is not callable. In order to call both code paths, both :old and :new must be set and callable."
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
- ":fallback_on_error is set but :new is either not set or is not callable. This mode is designed for after the :new code path has been developed and run in production-like environments, where :old is only kept around as a fallback to retry in the event that :new raises an unexpected error. Either specify a :new code path or disable :fallback_on_error."
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
- log_warn <<-MSG.gsub(/^ {10}/,'')
14
- Seam #{plan.name.inspect} is set to :call_both the :new and :old code
15
- paths, but they did not match. The new result was: ```
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
- new_result
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
@@ -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, :fallback_on_error,
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]
@@ -1,3 +1,3 @@
1
1
  module Suture
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
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.0.0
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-07 00:00:00.000000000 Z
11
+ date: 2016-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sqlite3