stoplight 4.1.1 → 5.0.1

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.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +288 -354
  3. data/lib/stoplight/admin/actions/action.rb +24 -0
  4. data/lib/stoplight/admin/actions/lock.rb +23 -0
  5. data/lib/stoplight/admin/actions/lock_all_green.rb +18 -0
  6. data/lib/stoplight/admin/actions/lock_green.rb +23 -0
  7. data/lib/stoplight/admin/actions/lock_red.rb +23 -0
  8. data/lib/stoplight/admin/actions/stats.rb +27 -0
  9. data/lib/stoplight/admin/actions/unlock.rb +23 -0
  10. data/lib/stoplight/admin/dependencies.rb +50 -0
  11. data/lib/stoplight/admin/helpers.rb +27 -0
  12. data/lib/stoplight/admin/lights_repository/light.rb +155 -0
  13. data/lib/stoplight/admin/lights_repository.rb +74 -0
  14. data/lib/stoplight/admin/lights_stats.rb +77 -0
  15. data/lib/stoplight/admin/views/_card.erb +120 -0
  16. data/lib/stoplight/admin/views/index.erb +36 -0
  17. data/lib/stoplight/admin/views/layout.erb +66 -0
  18. data/lib/stoplight/admin.rb +68 -0
  19. data/lib/stoplight/color.rb +3 -3
  20. data/lib/stoplight/config/config_provider.rb +62 -0
  21. data/lib/stoplight/config/library_default_config.rb +29 -0
  22. data/lib/stoplight/config/user_default_config.rb +83 -0
  23. data/lib/stoplight/data_store/base.rb +59 -33
  24. data/lib/stoplight/data_store/fail_safe.rb +105 -0
  25. data/lib/stoplight/data_store/memory.rb +257 -50
  26. data/lib/stoplight/data_store/redis/get_metadata.lua +38 -0
  27. data/lib/stoplight/data_store/redis/lua.rb +23 -0
  28. data/lib/stoplight/data_store/redis/record_failure.lua +36 -0
  29. data/lib/stoplight/data_store/redis/record_success.lua +35 -0
  30. data/lib/stoplight/data_store/redis/transition_to_green.lua +10 -0
  31. data/lib/stoplight/data_store/redis/transition_to_red.lua +10 -0
  32. data/lib/stoplight/data_store/redis/transition_to_yellow.lua +9 -0
  33. data/lib/stoplight/data_store/redis.rb +345 -106
  34. data/lib/stoplight/default.rb +11 -9
  35. data/lib/stoplight/error.rb +1 -13
  36. data/lib/stoplight/failure.rb +14 -13
  37. data/lib/stoplight/light/config.rb +118 -0
  38. data/lib/stoplight/light/configuration_builder_interface.rb +128 -0
  39. data/lib/stoplight/light/green_run_strategy.rb +53 -0
  40. data/lib/stoplight/light/red_run_strategy.rb +26 -0
  41. data/lib/stoplight/light/run_strategy.rb +30 -0
  42. data/lib/stoplight/light/yellow_run_strategy.rb +78 -0
  43. data/lib/stoplight/light.rb +164 -84
  44. data/lib/stoplight/metadata.rb +71 -0
  45. data/lib/stoplight/notifier/base.rb +14 -7
  46. data/lib/stoplight/notifier/fail_safe.rb +67 -0
  47. data/lib/stoplight/notifier/generic.rb +54 -5
  48. data/lib/stoplight/rspec/generic_notifier.rb +11 -12
  49. data/lib/stoplight/rspec.rb +1 -1
  50. data/lib/stoplight/state.rb +3 -3
  51. data/lib/stoplight/traffic_control/base.rb +35 -0
  52. data/lib/stoplight/traffic_control/consecutive_failures.rb +43 -0
  53. data/lib/stoplight/traffic_recovery/base.rb +51 -0
  54. data/lib/stoplight/traffic_recovery/single_success.rb +35 -0
  55. data/lib/stoplight/version.rb +1 -1
  56. data/lib/stoplight.rb +111 -51
  57. metadata +49 -98
  58. data/lib/stoplight/builder.rb +0 -70
  59. data/lib/stoplight/circuit_breaker.rb +0 -102
  60. data/lib/stoplight/configurable.rb +0 -95
  61. data/lib/stoplight/configuration.rb +0 -126
  62. data/lib/stoplight/light/deprecated.rb +0 -44
  63. data/lib/stoplight/light/lockable.rb +0 -45
  64. data/lib/stoplight/light/runnable.rb +0 -127
  65. data/lib/stoplight/notifier.rb +0 -6
  66. data/spec/spec_helper.rb +0 -22
  67. data/spec/stoplight/builder_spec.rb +0 -165
  68. data/spec/stoplight/circuit_breaker_spec.rb +0 -43
  69. data/spec/stoplight/color_spec.rb +0 -39
  70. data/spec/stoplight/configurable_spec.rb +0 -25
  71. data/spec/stoplight/data_store/base_spec.rb +0 -71
  72. data/spec/stoplight/data_store/memory_spec.rb +0 -22
  73. data/spec/stoplight/data_store/redis_spec.rb +0 -45
  74. data/spec/stoplight/data_store_spec.rb +0 -9
  75. data/spec/stoplight/default_spec.rb +0 -80
  76. data/spec/stoplight/error_spec.rb +0 -39
  77. data/spec/stoplight/failure_spec.rb +0 -108
  78. data/spec/stoplight/light/lockable_spec.rb +0 -93
  79. data/spec/stoplight/light/runnable_spec.rb +0 -38
  80. data/spec/stoplight/light_spec.rb +0 -156
  81. data/spec/stoplight/notifier/base_spec.rb +0 -18
  82. data/spec/stoplight/notifier/generic_spec.rb +0 -50
  83. data/spec/stoplight/notifier/io_spec.rb +0 -41
  84. data/spec/stoplight/notifier/logger_spec.rb +0 -75
  85. data/spec/stoplight/notifier_spec.rb +0 -9
  86. data/spec/stoplight/state_spec.rb +0 -39
  87. data/spec/stoplight/version_spec.rb +0 -9
  88. data/spec/stoplight_spec.rb +0 -32
  89. data/spec/support/configurable.rb +0 -69
  90. data/spec/support/data_store/base/clear_failures.rb +0 -24
  91. data/spec/support/data_store/base/clear_state.rb +0 -20
  92. data/spec/support/data_store/base/get_all.rb +0 -44
  93. data/spec/support/data_store/base/get_failures.rb +0 -30
  94. data/spec/support/data_store/base/get_state.rb +0 -7
  95. data/spec/support/data_store/base/names.rb +0 -29
  96. data/spec/support/data_store/base/record_failures.rb +0 -70
  97. data/spec/support/data_store/base/set_state.rb +0 -15
  98. data/spec/support/data_store/base/with_notification_lock.rb +0 -27
  99. data/spec/support/data_store/base.rb +0 -21
  100. data/spec/support/database_cleaner.rb +0 -26
  101. data/spec/support/exception_helpers.rb +0 -9
  102. data/spec/support/light/runnable/color.rb +0 -79
  103. data/spec/support/light/runnable/run.rb +0 -247
  104. data/spec/support/light/runnable/state.rb +0 -31
  105. data/spec/support/light/runnable.rb +0 -5
@@ -1,95 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Stoplight
4
- # @api private
5
- # @abstract include the module and define +#reconfigure+ method
6
- module Configurable
7
- # @!attribute [r] configuration
8
- # @return [Stoplight::Configuration]
9
- # @api private
10
- attr_reader :configuration
11
-
12
- # Configures data store to be used with this circuit breaker
13
- #
14
- # @example
15
- # Stoplight('example')
16
- # .with_data_store(Stoplight::DataStore::Memory.new)
17
- #
18
- # @param data_store [DataStore::Base]
19
- # @return [Stoplight::CircuitBreaker]
20
- def with_data_store(data_store)
21
- reconfigure(configuration.with(data_store: data_store))
22
- end
23
-
24
- # Configures cool off time. Stoplight automatically tries to recover
25
- # from the red state after the cool off time.
26
- #
27
- # @example
28
- # Stoplight('example')
29
- # .cool_off_time(60)
30
- #
31
- # @param cool_off_time [Numeric] number of seconds
32
- # @return [Stoplight::CircuitBreaker]
33
- def with_cool_off_time(cool_off_time)
34
- reconfigure(configuration.with(cool_off_time: cool_off_time))
35
- end
36
-
37
- # Configures custom threshold. After this number of failures Stoplight
38
- # switches to the red state:
39
- #
40
- # @example
41
- # Stoplight('example')
42
- # .with_threshold(5)
43
- #
44
- # @param threshold [Numeric]
45
- # @return [Stoplight::CircuitBreaker]
46
- def with_threshold(threshold)
47
- reconfigure(configuration.with(threshold: threshold))
48
- end
49
-
50
- # Configures custom window size which Stoplight uses to count failures. For example,
51
- #
52
- # @example
53
- # Stoplight('example')
54
- # .with_threshold(5)
55
- # .with_window_size(60)
56
- #
57
- # The above example will turn to red light only when 5 errors happen
58
- # within 60 seconds period.
59
- #
60
- # @param window_size [Numeric] number of seconds
61
- # @return [Stoplight::CircuitBreaker]
62
- def with_window_size(window_size)
63
- reconfigure(configuration.with(window_size: window_size))
64
- end
65
-
66
- # Configures custom notifier
67
- #
68
- # @example
69
- # io = StringIO.new
70
- # notifier = Stoplight::Notifier::IO.new(io)
71
- # Stoplight('example')
72
- # .with_notifiers([notifier])
73
- #
74
- # @param notifiers [Array<Notifier::Base>]
75
- # @return [Stoplight::CircuitBreaker]
76
- def with_notifiers(notifiers)
77
- reconfigure(configuration.with(notifiers: notifiers))
78
- end
79
-
80
- # @param error_notifier [Proc]
81
- # @return [Stoplight::CircuitBreaker]
82
- # @api private
83
- def with_error_notifier(&error_notifier)
84
- reconfigure(configuration.with(error_notifier: error_notifier))
85
- end
86
-
87
- private
88
-
89
- # @param [Stoplight::Configuration]
90
- # @return [Stoplight::CircuitBreaker]
91
- def reconfigure(_configuration)
92
- raise NotImplementedError, "#{self.class.name}#reconfigure is not implemented"
93
- end
94
- end
95
- end
@@ -1,126 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Stoplight
4
- # A +Stoplight::Light+ configuration object.
5
- class Configuration
6
- class << self
7
- alias __new_without_defaults__ new
8
-
9
- # It overrides the +Configuration.new+ to inject default settings
10
- # @see +Stoplight::Configuration#initialize+
11
- def new(**settings)
12
- __new_without_defaults__(
13
- **default_settings.merge(settings)
14
- )
15
- end
16
-
17
- private
18
-
19
- # @return [Hash]
20
- def default_settings
21
- {
22
- cool_off_time: Default::COOL_OFF_TIME,
23
- data_store: Stoplight.default_data_store,
24
- error_notifier: Stoplight.default_error_notifier,
25
- notifiers: Stoplight.default_notifiers,
26
- threshold: Default::THRESHOLD,
27
- window_size: Default::WINDOW_SIZE
28
- }
29
- end
30
- end
31
-
32
- # @!attribute [r] name
33
- # @return [String]
34
- attr_reader :name
35
-
36
- # @!attribute [r] cool_off_time
37
- # @return [Numeric]
38
- attr_reader :cool_off_time
39
-
40
- # @!attribute [r] data_store
41
- # @return [Stoplight::DataStore::Base]
42
- attr_reader :data_store
43
-
44
- # @!attribute [r] error_notifier
45
- # # @return [StandardError => void]
46
- attr_reader :error_notifier
47
-
48
- # @!attribute [r] notifiers
49
- # # @return [Array<Notifier::Base>]
50
- attr_reader :notifiers
51
-
52
- # @!attribute [r] threshold
53
- # @return [Numeric]
54
- attr_reader :threshold
55
-
56
- # @!attribute [r] window_size
57
- # @return [Numeric]
58
- attr_reader :window_size
59
-
60
- # @param name [String]
61
- # @param cool_off_time [Numeric]
62
- # @param data_store [Stoplight::DataStore::Base]
63
- # @param error_notifier [Proc]
64
- # @param notifiers [Stoplight::Notifier::Base]
65
- # @param threshold [Numeric]
66
- # @param window_size [Numeric]
67
- def initialize(name:, cool_off_time:, data_store:, error_notifier:, notifiers:, threshold:, window_size:)
68
- @name = name
69
- @cool_off_time = cool_off_time
70
- @data_store = data_store
71
- @error_notifier = error_notifier
72
- @notifiers = notifiers
73
- @threshold = threshold
74
- @window_size = window_size
75
- end
76
-
77
- # @param other [any]
78
- # @return [Boolean]
79
- def ==(other)
80
- other.is_a?(self.class) && settings == other.settings
81
- end
82
-
83
- # @param cool_off_time [Numeric]
84
- # @param data_store [Stoplight::DataStore::Base]
85
- # @param error_notifier [Proc]
86
- # @param name [String]
87
- # @param notifiers [Stoplight::Notifier::Base]
88
- # @param threshold [Numeric]
89
- # @param window_size [Numeric]
90
- # @return [Stoplight::Configuration]
91
- def with(
92
- cool_off_time: self.cool_off_time,
93
- data_store: self.data_store,
94
- error_notifier: self.error_notifier,
95
- name: self.name,
96
- notifiers: self.notifiers,
97
- threshold: self.threshold,
98
- window_size: self.window_size
99
- )
100
- Configuration.new(
101
- cool_off_time: cool_off_time,
102
- data_store: data_store,
103
- error_notifier: error_notifier,
104
- name: name,
105
- notifiers: notifiers,
106
- threshold: threshold,
107
- window_size: window_size
108
- )
109
- end
110
-
111
- protected
112
-
113
- # @return [Hash]
114
- def settings
115
- {
116
- cool_off_time: cool_off_time,
117
- data_store: data_store,
118
- error_notifier: error_notifier,
119
- name: name,
120
- notifiers: notifiers,
121
- threshold: threshold,
122
- window_size: window_size
123
- }
124
- end
125
- end
126
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Stoplight
4
- class Light
5
- # @api private
6
- module Deprecated
7
- def default_data_store
8
- warn '[DEPRECATED] `Stoplight::Light.default_data_store` is deprecated. ' \
9
- 'Please use `Stoplight.default_data_store` instead.'
10
- Stoplight.default_data_store
11
- end
12
-
13
- def default_data_store=(value)
14
- warn '[DEPRECATED] `Stoplight::Light.default_data_store=` is deprecated. ' \
15
- 'Please use `Stoplight.default_data_store=` instead.'
16
- Stoplight.default_data_store = value
17
- end
18
-
19
- def default_notifiers
20
- warn '[DEPRECATED] `Stoplight::Light.default_notifiers` is deprecated. ' \
21
- 'Please use `Stoplight.default_notifiers` instead.'
22
- Stoplight.default_notifiers
23
- end
24
-
25
- def default_notifiers=(value)
26
- warn '[DEPRECATED] `Stoplight::Light.default_notifiers=` is deprecated. ' \
27
- 'Please use `Stoplight.default_notifiers=` instead.'
28
- Stoplight.default_notifiers = value
29
- end
30
-
31
- def default_error_notifier
32
- warn '[DEPRECATED] `Stoplight::Light.default_error_notifier` is deprecated. ' \
33
- 'Please use `Stoplight.default_error_notifier` instead.'
34
- Stoplight.default_error_notifier
35
- end
36
-
37
- def default_error_notifier=(value)
38
- warn '[DEPRECATED] `Stoplight::Light.default_error_notifier=` is deprecated. ' \
39
- 'Please use `Stoplight.default_error_notifier=` instead.'
40
- Stoplight.default_error_notifier = value
41
- end
42
- end
43
- end
44
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Stoplight
4
- class Light
5
- # The Lockable module implements the behavior of locking and unlocking the light.
6
- # Light can be locked in either a State::LOCKED_RED or State::LOCKED_GREEN state.
7
- # By locking the light, you force it always to run code with the chosen light color.
8
- #
9
- # @example
10
- # light = Stoplight('example-locked') { true }
11
- # # => #<Stoplight::Light:..>
12
- # light.run
13
- # # => true
14
- # light.lock(Stoplight::Color::RED)
15
- # # => #<Stoplight::Light:..>
16
- # light.run
17
- # # => Stoplight::Error::RedLight: example-locked
18
- # light.unlock
19
- # # => #<Stoplight::Light:..>
20
- # light.run
21
- # # => true
22
- module Lockable
23
- # @param color [String] should be either Color::RED or Color::GREEN
24
- # @return [Stoplight::Light] returns locked light
25
- def lock(color)
26
- state = case color
27
- when Color::RED then State::LOCKED_RED
28
- when Color::GREEN then State::LOCKED_GREEN
29
- else raise Error::IncorrectColor
30
- end
31
-
32
- safely { data_store.set_state(self, state) }
33
-
34
- self
35
- end
36
-
37
- # @return [Stoplight::Light] returns unlocked light
38
- def unlock
39
- safely { data_store.set_state(self, Stoplight::State::UNLOCKED) }
40
-
41
- self
42
- end
43
- end
44
- end
45
- end
@@ -1,127 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Stoplight
4
- class Light
5
- module Runnable # rubocop:disable Style/Documentation
6
- # @return [String]
7
- def state
8
- _, state = failures_and_state
9
- state
10
- end
11
-
12
- # @return [String]
13
- def color
14
- failures, state = failures_and_state
15
- failure = failures.first
16
-
17
- if state == State::LOCKED_GREEN then Color::GREEN
18
- elsif state == State::LOCKED_RED then Color::RED
19
- elsif failures.size < threshold then Color::GREEN
20
- elsif failure && Time.now - failure.time >= cool_off_time
21
- Color::YELLOW
22
- else
23
- Color::RED
24
- end
25
- end
26
-
27
- # @raise [Error::RedLight]
28
- def run(&code)
29
- code = validate_code(&code)
30
- case color
31
- when Color::GREEN then run_green(&code)
32
- when Color::YELLOW then run_yellow(&code)
33
- else run_red
34
- end
35
- end
36
-
37
- private
38
-
39
- def validate_code(&code)
40
- raise ArgumentError, <<~ERROR if block_given? && self.code
41
- passing code block into both `Light.new` and `Light#run` is not allowed
42
- ERROR
43
-
44
- raise ArgumentError, <<~ERROR unless block_given? || self.code
45
- nothing to run. Please, pass a block into `Light#run`
46
- ERROR
47
-
48
- code || self.code
49
- end
50
-
51
- def run_green(&code)
52
- on_failure = lambda do |size, error|
53
- notify(Color::GREEN, Color::RED, error) if failures_threshold_breached?(size, threshold)
54
- end
55
- run_code(nil, on_failure, &code)
56
- end
57
-
58
- def failures_threshold_breached?(current_failures_count, max_errors_threshold)
59
- current_failures_count == max_errors_threshold
60
- end
61
-
62
- def run_yellow(&code)
63
- on_success = lambda do |failures|
64
- notify(Color::RED, Color::GREEN) unless failures.empty?
65
- end
66
- run_code(on_success, nil, &code)
67
- end
68
-
69
- def run_red
70
- raise Error::RedLight, name unless fallback
71
-
72
- fallback.call(nil)
73
- end
74
-
75
- def run_code(on_success, on_failure, &code)
76
- result = code.call
77
- failures = clear_failures
78
- on_success&.call(failures)
79
- result
80
- rescue Exception => e # rubocop:disable Lint/RescueException
81
- handle_error(e, on_failure)
82
- end
83
-
84
- def handle_error(error, on_failure)
85
- error_handler.call(error, Error::HANDLER)
86
- size = record_failure(error)
87
- on_failure&.call(size, error)
88
- raise error unless fallback
89
-
90
- fallback.call(error)
91
- end
92
-
93
- def clear_failures
94
- safely([]) { data_store.clear_failures(self) }
95
- end
96
-
97
- def failures_and_state
98
- safely([[], State::UNLOCKED]) { data_store.get_all(self) }
99
- end
100
-
101
- def notify(from_color, to_color, error = nil)
102
- data_store.with_notification_lock(self, from_color, to_color) do
103
- notifiers.each do |notifier|
104
- safely { notifier.notify(self, from_color, to_color, error) }
105
- end
106
- end
107
- end
108
-
109
- def record_failure(error)
110
- failure = Failure.from_error(error)
111
- safely(0) { data_store.record_failure(self, failure) }
112
- end
113
-
114
- def safely(default = nil, &code)
115
- return yield if data_store == Default::DATA_STORE
116
-
117
- Stoplight("#{name}-safely")
118
- .with_data_store(Default::DATA_STORE)
119
- .with_fallback do |error|
120
- error_notifier.call(error) if error
121
- default
122
- end
123
- .run(&code)
124
- end
125
- end
126
- end
127
- end
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Stoplight
4
- module Notifier # rubocop:disable Style/Documentation
5
- end
6
- end
data/spec/spec_helper.rb DELETED
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'simplecov'
4
-
5
- require 'stoplight'
6
- require 'timecop'
7
- require_relative 'support/data_store/base'
8
- require_relative 'support/light/runnable'
9
- require_relative 'support/configurable'
10
- require_relative 'support/database_cleaner'
11
- require_relative 'support/exception_helpers'
12
-
13
- Timecop.safe_mode = true
14
-
15
- RSpec.configure do |rspec|
16
- rspec.include ExceptionHelpers
17
-
18
- rspec.color = true
19
- rspec.disable_monkey_patching!
20
- rspec.order = :random
21
- rspec.warnings = true
22
- end
@@ -1,165 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
- require 'securerandom'
5
-
6
- RSpec.describe Stoplight::Builder do
7
- let(:name) { SecureRandom.uuid }
8
-
9
- describe '.with' do
10
- context 'with only name' do
11
- subject(:configuration) { builder.configuration }
12
-
13
- let(:builder) { described_class.with(name: name) }
14
-
15
- it 'sets configuration to default values' do
16
- expect(configuration).to have_attributes(
17
- name: name,
18
- data_store: Stoplight.default_data_store,
19
- notifiers: Stoplight.default_notifiers,
20
- cool_off_time: Stoplight::Default::COOL_OFF_TIME,
21
- threshold: Stoplight::Default::THRESHOLD,
22
- window_size: Stoplight::Default::WINDOW_SIZE
23
- )
24
- end
25
- end
26
-
27
- context 'with configured parameters' do
28
- subject(:configuration) { builder.configuration }
29
-
30
- let(:builder) { described_class.with(**configured_parameters) }
31
- let(:configured_parameters) do
32
- {
33
- name: name,
34
- data_store: 42,
35
- notifiers: [43],
36
- cool_off_time: 44,
37
- threshold: 45,
38
- window_size: 46
39
- }
40
- end
41
-
42
- it 'sets configuration parameters' do
43
- expect(configuration).to have_attributes(**configured_parameters)
44
- end
45
- end
46
- end
47
-
48
- describe '.build' do
49
- let(:builder) { described_class.new(configuration) }
50
- let(:configuration) { instance_double(Stoplight::Configuration, name: name) }
51
-
52
- context 'with code' do
53
- subject(:light) { builder.build(&code) }
54
-
55
- let(:code) { -> { 42 } }
56
-
57
- it 'builds new light' do
58
- expect(light.configuration).to eq(configuration)
59
- expect(light.code).to eq(code)
60
- end
61
- end
62
-
63
- context 'without code' do
64
- subject(:light) { builder.build }
65
-
66
- it 'builds new light' do
67
- expect(light.configuration).to eq(configuration)
68
- expect(light.code).to eq(nil)
69
- end
70
- end
71
- end
72
-
73
- context 'methods building an instance of light' do
74
- let(:builder) { described_class.new(configuration) }
75
- let(:configuration) do
76
- Stoplight::Configuration.new(
77
- name: name,
78
- data_store: Stoplight.default_data_store,
79
- notifiers: Stoplight.default_notifiers,
80
- cool_off_time: Stoplight::Default::COOL_OFF_TIME,
81
- threshold: Stoplight::Default::THRESHOLD,
82
- window_size: Stoplight::Default::WINDOW_SIZE,
83
- error_notifier: Stoplight.default_error_notifier
84
- )
85
- end
86
-
87
- describe '#with_error_handler' do
88
- subject(:light) { builder.with_error_handler(&error_handler) }
89
-
90
- let(:error_handler) { ->(error, handle) {} }
91
-
92
- it 'returns an instance of the Light class with this configuration set' do
93
- expect(light.configuration).to be(configuration)
94
- expect(light.error_handler).to eq(error_handler)
95
- end
96
- end
97
-
98
- describe '#with_fallback' do
99
- subject(:light) { builder.with_fallback(&fallback) }
100
-
101
- let(:fallback) { ->(error) {} }
102
-
103
- it 'returns an instance of the Light class with this configuration set' do
104
- expect(light.configuration).to be(configuration)
105
- expect(light.fallback).to eq(fallback)
106
- end
107
- end
108
-
109
- describe '#run' do
110
- it 'yields the block' do
111
- expect do |code|
112
- builder.run(&code)
113
- end.to yield_control
114
- end
115
- end
116
-
117
- describe '#lock' do
118
- context 'when the light is not locked' do
119
- it 'locks the light' do
120
- expect { builder.lock(Stoplight::Color::RED) }
121
- .to change(builder, :color)
122
- .from(Stoplight::Color::GREEN)
123
- .to(Stoplight::Color::RED)
124
- end
125
- end
126
-
127
- context 'when the light is locked' do
128
- before do
129
- builder.lock(Stoplight::Color::RED)
130
- end
131
-
132
- it 'does not change the light' do
133
- expect { builder.lock(Stoplight::Color::RED) }
134
- .not_to change(builder, :color)
135
- .from(Stoplight::Color::RED)
136
- end
137
- end
138
- end
139
-
140
- describe '#unlock' do
141
- context 'when the light is not locked' do
142
- it 'does nothing' do
143
- expect { builder.unlock }
144
- .not_to change(builder, :color)
145
- .from(Stoplight::Color::GREEN)
146
- end
147
- end
148
-
149
- context 'when the light is locked' do
150
- before do
151
- builder.lock(Stoplight::Color::RED)
152
- end
153
-
154
- it 'unlocks the light' do
155
- expect { builder.unlock }
156
- .to change(builder, :color)
157
- .from(Stoplight::Color::RED)
158
- .to(Stoplight::Color::GREEN)
159
- end
160
- end
161
- end
162
- end
163
-
164
- it_behaves_like Stoplight::Configurable
165
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe Stoplight::CircuitBreaker do
6
- subject(:circuit_breaker) { klass.new }
7
-
8
- let(:klass) do
9
- Class.new { include Stoplight::CircuitBreaker }
10
- end
11
-
12
- specify '#with_error_handler' do
13
- expect { circuit_breaker.with_error_handler {} }.to raise_error(NotImplementedError)
14
- end
15
-
16
- specify '#with_fallback' do
17
- expect { circuit_breaker.with_fallback {} }.to raise_error(NotImplementedError)
18
- end
19
-
20
- specify '#color' do
21
- expect { circuit_breaker.color }.to raise_error(NotImplementedError)
22
- end
23
-
24
- specify '#state' do
25
- expect { circuit_breaker.state }.to raise_error(NotImplementedError)
26
- end
27
-
28
- specify '#name' do
29
- expect { circuit_breaker.name }.to raise_error(NotImplementedError)
30
- end
31
-
32
- specify '#run' do
33
- expect { circuit_breaker.run {} }.to raise_error(NotImplementedError)
34
- end
35
-
36
- specify '#lock' do
37
- expect { circuit_breaker.lock('red') }.to raise_error(NotImplementedError)
38
- end
39
-
40
- specify '#unlock' do
41
- expect { circuit_breaker.unlock }.to raise_error(NotImplementedError)
42
- end
43
- end