circuitbox 1.1.1 → 2.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +60 -122
  3. data/lib/circuitbox.rb +15 -52
  4. data/lib/circuitbox/circuit_breaker.rb +131 -141
  5. data/lib/circuitbox/circuit_breaker/logger_messages.rb +31 -0
  6. data/lib/circuitbox/configuration.rb +53 -0
  7. data/lib/circuitbox/errors/error.rb +1 -2
  8. data/lib/circuitbox/errors/open_circuit_error.rb +0 -1
  9. data/lib/circuitbox/errors/service_failure_error.rb +2 -1
  10. data/lib/circuitbox/excon_middleware.rb +15 -24
  11. data/lib/circuitbox/faraday_middleware.rb +35 -61
  12. data/lib/circuitbox/memory_store.rb +76 -0
  13. data/lib/circuitbox/memory_store/compactor.rb +35 -0
  14. data/lib/circuitbox/memory_store/container.rb +28 -0
  15. data/lib/circuitbox/memory_store/monotonic_time.rb +11 -0
  16. data/lib/circuitbox/notifier/active_support.rb +19 -0
  17. data/lib/circuitbox/notifier/null.rb +11 -0
  18. data/lib/circuitbox/timer/monotonic.rb +17 -0
  19. data/lib/circuitbox/timer/null.rb +9 -0
  20. data/lib/circuitbox/timer/simple.rb +13 -0
  21. data/lib/circuitbox/version.rb +1 -1
  22. metadata +78 -150
  23. data/.gitignore +0 -20
  24. data/.ruby-version +0 -1
  25. data/.travis.yml +0 -9
  26. data/Gemfile +0 -6
  27. data/Rakefile +0 -30
  28. data/benchmark/circuit_store_benchmark.rb +0 -114
  29. data/circuitbox.gemspec +0 -48
  30. data/lib/circuitbox/notifier.rb +0 -34
  31. data/test/circuit_breaker_test.rb +0 -436
  32. data/test/circuitbox_test.rb +0 -45
  33. data/test/excon_middleware_test.rb +0 -131
  34. data/test/faraday_middleware_test.rb +0 -175
  35. data/test/integration/circuitbox_cross_process_open_test.rb +0 -56
  36. data/test/integration/faraday_middleware_test.rb +0 -78
  37. data/test/integration_helper.rb +0 -48
  38. data/test/notifier_test.rb +0 -21
  39. data/test/service_failure_error_test.rb +0 -23
  40. data/test/test_helper.rb +0 -15
data/.gitignore DELETED
@@ -1,20 +0,0 @@
1
- *.gem
2
- .vagrant
3
- *.rbc
4
- .bundle
5
- .config
6
- .yardoc
7
- Gemfile.lock
8
- InstalledFiles
9
- _yardoc
10
- coverage
11
- doc/
12
- lib/bundler/man
13
- pkg
14
- rdoc
15
- spec/reports
16
- test/tmp
17
- test/reports
18
- test/version_tmp
19
- tmp
20
- .idea
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 2.2.2
data/.travis.yml DELETED
@@ -1,9 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 1.9.3
4
- - 2.0.0
5
- - 2.1
6
- - 2.2
7
- - 2.2.2
8
- before_install:
9
- - gem install bundler
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in circuitbox.gemspec
4
- gemspec
5
-
6
- gem "pry"
data/Rakefile DELETED
@@ -1,30 +0,0 @@
1
- require "rake/testtask"
2
- require "bundler/gem_version_tasks"
3
-
4
- namespace :test do
5
- desc "Run complete test suite"
6
- task :all => [:unit, :integration]
7
-
8
- desc "Run unit tests"
9
- Rake::TestTask.new(:unit) do |t|
10
- t.libs << "test"
11
- t.test_files = FileList["test/*_test.rb"]
12
- end
13
-
14
- desc "Run integration tests"
15
- Rake::TestTask.new(:integration) do |t|
16
- t.libs << "test"
17
- t.test_files = FileList["test/integration/*_test.rb"]
18
- end
19
-
20
- desc "Run the circuitbox benchmarks"
21
- task :benchmark do
22
- benchmark_scripts = FileList.new("./benchmark/*_benchmark.rb")
23
- benchmark_scripts.each do |script|
24
- system "bundle exec ruby #{script}"
25
- end
26
- end
27
- end
28
-
29
- desc "Run tests"
30
- task :default => ["test:all"]
@@ -1,114 +0,0 @@
1
- require 'circuitbox'
2
- require 'benchmark'
3
- require 'pstore'
4
- require 'tempfile'
5
- require 'tmpdir'
6
- require 'lmdb'
7
- require 'pry'
8
-
9
-
10
- class Circuitbox
11
- class CircuitBreaker
12
- # silence the circuitbreaker logger
13
- DEV_NULL = (RUBY_PLATFORM =~ /mswin|mingw/ ? "NUL" : "/dev/null")
14
- def logger
15
- @_dev_null_logger ||= Logger.new DEV_NULL
16
- end
17
- end
18
- end
19
-
20
- def service
21
- # 10% success rate to make the circuitbreaker flip flop
22
- if rand(10) <= 0
23
- "success"
24
- else
25
- raise RuntimeError, "fail"
26
- end
27
- end
28
-
29
- def run_flip_flopping circuit
30
- circuit.run { service }
31
- circuit.try_close_next_time if circuit.open?
32
- end
33
-
34
- def without_gc
35
- GC.start
36
- GC.disable
37
- yield
38
- GC.enable
39
- end
40
-
41
- def benchmark_circuitbox_method_with_reporter method, reporter
42
- without_gc { send(method, reporter) }
43
- Circuitbox.reset
44
- end
45
-
46
- def circuit_with_cache cache
47
- Circuitbox.circuit :performance, CIRCUIT_OPTIONS.merge(cache: cache)
48
- end
49
-
50
- CIRCUIT_OPTIONS = {
51
- exceptions: [RuntimeError],
52
- sleep_window: 0,
53
- time_window: 1
54
- }
55
-
56
- RUNS = 10000
57
-
58
- def circuit_store_memory_one_process reporter
59
- circuit = circuit_with_cache Moneta.new(:Memory)
60
-
61
- reporter.report "memory:" do
62
- RUNS.times { run_flip_flopping circuit }
63
- end
64
-
65
- circuit.circuit_store.close
66
- end
67
-
68
- def circuit_store_pstore_one_process reporter
69
- Tempfile.create("test_circuit_store_pstore_one_process") do |dbfile|
70
- circuit = circuit_with_cache Moneta.new(:PStore, file: dbfile)
71
-
72
- reporter.report "pstore:" do
73
- RUNS.times { run_flip_flopping circuit }
74
- end
75
-
76
- circuit.circuit_store.close
77
- end
78
- end
79
-
80
- def circuit_store_lmdb_one_process reporter
81
- Dir.mktmpdir("test_circuit_store_lmdb_one_process") do |dbdir|
82
- circuit = circuit_with_cache Moneta.new(:LMDB, dir: dbdir, db: "circuitbox_lmdb")
83
-
84
- reporter.report "lmdb:" do
85
- RUNS.times { run_flip_flopping circuit }
86
- end
87
-
88
- circuit.circuit_store.close
89
- end
90
- end
91
-
92
- def circuit_store_daybreak_one_process reporter
93
- Tempfile.create("test_circuit_store_daybreak_one_process") do |dbfile|
94
- circuit = circuit_with_cache Moneta.new(:Daybreak, file: dbfile)
95
-
96
- reporter.report "daybreak:" do
97
- RUNS.times { run_flip_flopping circuit }
98
- end
99
-
100
- circuit.circuit_store.close
101
- end
102
- end
103
-
104
- Benchmark.bm(8) do |x|
105
- benchmark_circuitbox_method_with_reporter :circuit_store_memory_one_process, x
106
- benchmark_circuitbox_method_with_reporter :circuit_store_lmdb_one_process, x
107
- benchmark_circuitbox_method_with_reporter :circuit_store_pstore_one_process, x
108
- benchmark_circuitbox_method_with_reporter :circuit_store_daybreak_one_process, x
109
- end
110
-
111
-
112
-
113
-
114
-
data/circuitbox.gemspec DELETED
@@ -1,48 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'circuitbox/version'
5
-
6
- ruby_2_2_2_plus = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.2.2")
7
-
8
- Gem::Specification.new do |spec|
9
- spec.name = "circuitbox"
10
- spec.version = Circuitbox::VERSION
11
- spec.authors = ["Fahim Ferdous"]
12
- spec.email = ["fahimfmf@gmail.com"]
13
- spec.description = %q{A robust circuit breaker that manages failing external services.}
14
- spec.summary = %q{A robust circuit breaker that manages failing external services.}
15
- spec.homepage = ""
16
- spec.license = "MIT"
17
-
18
- spec.files = `git ls-files`.split($/)
19
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
- spec.require_paths = ["lib"]
22
-
23
- spec.add_development_dependency "bundler"
24
- spec.add_development_dependency "rake"
25
- if ruby_2_2_2_plus
26
- spec.add_development_dependency "rack"
27
- else
28
- spec.add_development_dependency "rack", '< 2'
29
- end
30
- spec.add_development_dependency "gimme"
31
- spec.add_development_dependency "minitest"
32
- spec.add_development_dependency "mocha"
33
- spec.add_development_dependency "typhoeus"
34
- spec.add_development_dependency "timecop"
35
- spec.add_development_dependency "faraday"
36
- spec.add_development_dependency "excon"
37
- spec.add_development_dependency "logger"
38
- spec.add_development_dependency "bundler-gem_version_tasks"
39
- spec.add_development_dependency "lmdb"
40
- spec.add_development_dependency "daybreak"
41
-
42
- if ruby_2_2_2_plus
43
- spec.add_dependency "activesupport"
44
- else
45
- spec.add_dependency "activesupport", '< 5'
46
- end
47
- spec.add_dependency "moneta"
48
- end
@@ -1,34 +0,0 @@
1
- class Circuitbox
2
- class Notifier
3
- def initialize(service, partition=nil)
4
- @service = service
5
- @partition = partition
6
- end
7
-
8
- def notify(event)
9
- return unless notification_available?
10
- ActiveSupport::Notifications.instrument("circuit_#{event}", circuit: circuit_name)
11
- end
12
-
13
- def notify_warning(message)
14
- return unless notification_available?
15
- ActiveSupport::Notifications.instrument("circuit_warning", { circuit: circuit_name, message: message})
16
- end
17
-
18
- def metric_gauge(gauge, value)
19
- return unless notification_available?
20
- ActiveSupport::Notifications.instrument("circuit_gauge", { circuit: circuit_name, gauge: gauge.to_s, value: value })
21
- end
22
-
23
- private
24
- def circuit_name
25
- circuit_name = @service.to_s
26
- circuit_name += ":#{@partition}" if @partition
27
- circuit_name
28
- end
29
-
30
- def notification_available?
31
- defined? ActiveSupport::Notifications
32
- end
33
- end
34
- end
@@ -1,436 +0,0 @@
1
- require 'test_helper'
2
-
3
- class CircuitBreakerTest < Minitest::Test
4
- class ConnectionError < StandardError; end;
5
-
6
- def setup
7
- Circuitbox::CircuitBreaker.reset
8
- end
9
-
10
- def test_sleep_window_is_forced_to_equal_time_window
11
- circuit = Circuitbox::CircuitBreaker.new(:yammer, sleep_window: 1, time_window: 10)
12
- assert_equal circuit.option_value(:sleep_window), circuit.option_value(:time_window)
13
- end
14
-
15
- def test_goes_into_half_open_state_on_sleep
16
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
17
- circuit.send(:open!)
18
- assert circuit.send(:half_open?)
19
- end
20
-
21
- class Ratio < Minitest::Test
22
- def setup
23
- Circuitbox::CircuitBreaker.reset
24
- @circuit = Circuitbox::CircuitBreaker.new(:yammer,
25
- sleep_window: 300,
26
- volume_threshold: 5,
27
- error_threshold: 33,
28
- timeout_seconds: 1)
29
- end
30
-
31
- def test_open_circuit_on_100_percent_failure
32
- run_counter = 0
33
- 10.times do
34
- @circuit.run do
35
- run_counter += 1
36
- raise Timeout::Error
37
- end
38
- end
39
- assert_equal 6, run_counter, 'the circuit did not open after 6 failures (5 failures + 10%)'
40
- end
41
-
42
- def test_keep_circuit_closed_on_success
43
- run_counter = 0
44
- 10.times do
45
- @circuit.run do
46
- run_counter += 1
47
- 'sucess'
48
- end
49
- end
50
- assert_equal 10, run_counter, 'run block was not executed 10 times'
51
- end
52
-
53
- def test_open_circuit_on_low_success_rate_below_limit
54
- run_counter = 0
55
- 5.times do
56
- @circuit.run do
57
- run_counter += 1
58
- raise Timeout::Error
59
- end
60
- end
61
-
62
- # one success
63
- @circuit.run { 'success'}
64
- assert_equal 5, @circuit.failure_count, 'the total count of failures is not 5'
65
-
66
- 5.times do
67
- @circuit.run do
68
- run_counter += 1
69
- raise Timeout::Error
70
- end
71
- end
72
- assert_equal 5, run_counter, 'the circuit did not open after 5 failures (5 failures + 10%)'
73
- end
74
-
75
- def test_keep_circuit_closed_on_low_failure_rate_below_failure_limit
76
- run_counter = 0
77
- 7.times do
78
- @circuit.run do
79
- run_counter += 1
80
- 'sucess'
81
- end
82
- end
83
- assert_equal 0, @circuit.failure_count, 'some errors were counted'
84
-
85
- 3.times do
86
- @circuit.run do
87
- run_counter += 1
88
- raise Timeout::Error
89
- end
90
- end
91
- assert_equal 10, run_counter, 'block was not executed 10 times'
92
- assert @circuit.error_rate < 33, 'error_rate pass over 33%'
93
- end
94
-
95
- def test_open_circuit_on_high_failure_rate_exceeding_failure_limit
96
- run_counter = 0
97
- 10.times do
98
- @circuit.run do
99
- run_counter += 1
100
- 'sucess'
101
- end
102
- end
103
- assert_equal 0, @circuit.failure_count, 'some errors were counted'
104
-
105
- 10.times do
106
- @circuit.run do
107
- run_counter += 1
108
- raise Timeout::Error
109
- end
110
- end
111
- # 5 failure on 15 run is 33%
112
- assert_equal 15, run_counter, 'block was not executed 10 times'
113
- assert @circuit.error_rate >= 33, 'error_rate pass over 33%'
114
- end
115
- end
116
-
117
- class Exceptions < Minitest::Test
118
- class SentinalError < StandardError; end
119
-
120
- def setup
121
- Circuitbox::CircuitBreaker.reset
122
- @circuit = Circuitbox::CircuitBreaker.new(:yammer, exceptions: [SentinalError])
123
- end
124
-
125
- def test_raises_when_circuit_is_open
126
- @circuit.stubs(open_flag?: true)
127
- assert_raises(Circuitbox::OpenCircuitError) { @circuit.run! {} }
128
- end
129
-
130
- def test_raises_on_service_failure
131
- assert_raises(Circuitbox::ServiceFailureError) { @circuit.run! { raise SentinalError } }
132
- end
133
-
134
- def test_sets_original_error_on_service_failure
135
- @circuit.run! { raise SentinalError }
136
- rescue Circuitbox::ServiceFailureError => service_failure_error
137
- assert_instance_of SentinalError, service_failure_error.original
138
- end
139
- end
140
-
141
- class CloseAfterSleep < Minitest::Test
142
- def setup
143
- Circuitbox::CircuitBreaker.reset
144
- @circuit = Circuitbox::CircuitBreaker.new(:yammer,
145
- sleep_window: 1,
146
- time_window: 2,
147
- volume_threshold: 5,
148
- error_threshold: 5,
149
- timeout_seconds: 1)
150
- end
151
-
152
- def test_circuit_closes_after_sleep_time_window
153
- open_circuit!
154
- run_count = 0
155
- @circuit.run { run_count += 1 }
156
- assert_equal 0, run_count, 'circuit has not opened prior'
157
- # it is + 2 on purpose, because + 1 is flaky here
158
- sleep @circuit.option_value(:sleep_window) + 2
159
-
160
- @circuit.run { run_count += 1 }
161
- assert_equal 1, run_count, 'circuit did not close after sleep'
162
- end
163
-
164
- def open_circuit!
165
- (@circuit.option_value(:error_threshold) + 1).times { @circuit.run { raise Timeout::Error } }
166
- end
167
- end
168
-
169
- class HalfOpenState < Minitest::Test
170
- def setup
171
- Circuitbox::CircuitBreaker.reset
172
- @circuit = Circuitbox::CircuitBreaker.new(:yammer)
173
- end
174
-
175
- def test_when_in_half_open_state_circuit_opens_on_failure
176
- @circuit.stubs(half_open?: true)
177
- @circuit.expects(:open!)
178
- @circuit.run { raise Timeout::Error }
179
- end
180
-
181
- def test_when_in_half_open_state_circuit_closes_on_success
182
- @circuit.send(:half_open!)
183
- @circuit.run { 'success' }
184
- refute @circuit.send(:half_open?)
185
- refute @circuit.send(:open?)
186
- end
187
- end
188
-
189
- def test_should_use_timeout_class_if_exceptions_are_not_defined
190
- circuit = Circuitbox::CircuitBreaker.new(:yammer, timeout_seconds: 45)
191
- circuit.expects(:timeout).with(45).once
192
- emulate_circuit_run(circuit, :success, StandardError)
193
- end
194
-
195
- def test_should_not_use_timeout_class_if_custom_exceptions_are_defined
196
- circuit = Circuitbox::CircuitBreaker.new(:yammer, exceptions: [ConnectionError])
197
- circuit.expects(:timeout).never
198
- emulate_circuit_run(circuit, :success, StandardError)
199
- end
200
-
201
- def test_should_return_response_if_it_doesnt_timeout
202
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
203
- response = emulate_circuit_run(circuit, :success, "success")
204
- assert_equal "success", response
205
- end
206
-
207
- def test_timeout_seconds_run_options_overrides_circuit_options
208
- circuit = Circuitbox::CircuitBreaker.new(:yammer, timeout_seconds: 60)
209
- circuit.expects(:timeout).with(30).once
210
- circuit.run(timeout_seconds: 30) { true }
211
- end
212
-
213
- def test_catches_connection_error_failures_if_defined
214
- circuit = Circuitbox::CircuitBreaker.new(:yammer, :exceptions => [ConnectionError])
215
- response = emulate_circuit_run(circuit, :failure, ConnectionError)
216
- assert_equal nil, response
217
- end
218
-
219
- def test_doesnt_catch_out_of_scope_exceptions
220
- sentinal = Class.new(StandardError)
221
- circuit = Circuitbox::CircuitBreaker.new(:yammer, :exceptions => [ConnectionError, Timeout::Error])
222
-
223
- assert_raises(sentinal) do
224
- emulate_circuit_run(circuit, :failure, sentinal)
225
- end
226
- end
227
-
228
- def test_records_response_failure
229
- circuit = Circuitbox::CircuitBreaker.new(:yammer, :exceptions => [Timeout::Error])
230
- circuit.expects(:log_event).with(:failure)
231
- emulate_circuit_run(circuit, :failure, Timeout::Error)
232
- end
233
-
234
- def test_records_response_skipped
235
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
236
- circuit.stubs(:open? => true)
237
- circuit.stubs(:log_event)
238
- circuit.expects(:log_event).with(:skipped)
239
- emulate_circuit_run(circuit, :failure, Timeout::Error)
240
- end
241
-
242
- def test_records_response_success
243
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
244
- circuit.expects(:log_event).with(:success)
245
- emulate_circuit_run(circuit, :success, "success")
246
- end
247
-
248
- def test_does_not_send_request_if_circuit_is_open
249
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
250
- circuit.stubs(:open? => true)
251
- circuit.expects(:yield).never
252
- response = emulate_circuit_run(circuit, :failure, Timeout::Error)
253
- assert_equal nil, response
254
- end
255
-
256
- def test_returns_nil_response_on_failed_request
257
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
258
- response = emulate_circuit_run(circuit, :failure, Timeout::Error)
259
- assert_equal nil, response
260
- end
261
-
262
- def test_puts_circuit_to_sleep_once_opened
263
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
264
- circuit.stubs(:open? => true)
265
-
266
- assert !circuit.send(:open_flag?)
267
- emulate_circuit_run(circuit, :failure, Timeout::Error)
268
- assert circuit.send(:open_flag?)
269
-
270
- circuit.expects(:open!).never
271
- emulate_circuit_run(circuit, :failure, Timeout::Error)
272
- end
273
-
274
- def test_open_is_true_if_open_flag
275
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
276
- circuit.stubs(:open_flag? => true)
277
- assert circuit.open?
278
- end
279
-
280
- def test_open_checks_if_volume_threshold_has_passed
281
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
282
- circuit.stubs(:open_flag? => false)
283
-
284
- circuit.expects(:passed_volume_threshold?).once
285
- circuit.open?
286
- end
287
-
288
- def test_open_checks_error_rate_threshold
289
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
290
- circuit.stubs(:open_flag? => false,
291
- :passed_volume_threshold? => true)
292
-
293
- circuit.expects(:passed_rate_threshold?).once
294
- circuit.open?
295
- end
296
-
297
- def test_open_is_false_if_awake_and_under_rate_threshold
298
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
299
- circuit.stubs(:open_flag? => false,
300
- :passed_volume_threshold? => false,
301
- :passed_rate_threshold => false)
302
-
303
- assert !circuit.open?
304
- end
305
-
306
- def test_error_rate_threshold_calculation
307
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
308
- circuit.stubs(:failure_count => 3, :success_count => 2)
309
- assert circuit.send(:passed_rate_threshold?)
310
-
311
- circuit.stubs(:failure_count => 2, :success_count => 3)
312
- assert !circuit.send(:passed_rate_threshold?)
313
- end
314
-
315
- def test_logs_and_retrieves_success_events
316
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
317
- 5.times { circuit.send(:log_event, :success) }
318
- assert_equal 5, circuit.send(:success_count)
319
- end
320
-
321
- def test_logs_and_retrieves_failure_events
322
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
323
- 5.times { circuit.send(:log_event, :failure) }
324
- assert_equal 5, circuit.send(:failure_count)
325
- end
326
-
327
- def test_logs_events_by_minute
328
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
329
-
330
- Timecop.travel(Time.now.change(sec: 5))
331
- 4.times { circuit.send(:log_event, :success) }
332
- assert_equal 4, circuit.send(:success_count)
333
-
334
- Timecop.travel(1.minute.from_now)
335
- 7.times { circuit.send(:log_event, :success) }
336
- assert_equal 7, circuit.send(:success_count)
337
-
338
- Timecop.travel(30.seconds.from_now)
339
- circuit.send(:log_event, :success)
340
- assert_equal 8, circuit.send(:success_count)
341
-
342
- Timecop.travel(50.seconds.from_now)
343
- assert_equal 0, circuit.send(:success_count)
344
- end
345
-
346
- class Notifications < Minitest::Test
347
- def setup
348
- Circuitbox::CircuitBreaker.reset
349
- end
350
-
351
- def test_notification_on_open
352
- notifier = gimme_notifier
353
- circuit = Circuitbox::CircuitBreaker.new(:yammer, notifier_class: notifier)
354
- 10.times { circuit.run { raise Timeout::Error }}
355
- assert notifier.notified?, 'no notification sent'
356
- end
357
-
358
- def test_notification_on_close
359
- notifier = gimme_notifier
360
- circuit = Circuitbox::CircuitBreaker.new(:yammer, notifier_class: notifier)
361
- 5.times { circuit.run { raise Timeout::Error }}
362
- notifier.clear_notified!
363
- 10.times { circuit.run { 'success' }}
364
- assert notifier.notified?, 'no notification sent'
365
- end
366
-
367
- def test_warning_when_sleep_window_is_shorter_than_time_window
368
- notifier = gimme_notifier
369
- Circuitbox::CircuitBreaker.new(:yammer,
370
- notifier_class: notifier,
371
- sleep_window: 1,
372
- time_window: 10)
373
- assert notifier.notified?, 'no notification sent'
374
- end
375
-
376
- def test_does_not_warn_on_sleep_window_being_correctly_sized
377
- notifier = gimme_notifier
378
- Circuitbox::CircuitBreaker.new(:yammer,
379
- notifier_class: notifier,
380
- sleep_window: 11,
381
- time_window: 10)
382
- assert_equal false, notifier.notified?, 'no notification sent'
383
- end
384
-
385
- def test_notifies_on_success_rate_calculation
386
- notifier = gimme_notifier(metric: :error_rate, metric_value: 0.0)
387
- circuit = Circuitbox::CircuitBreaker.new(:yammer, notifier_class: notifier)
388
- 10.times { circuit.run { "success" } }
389
- assert notifier.notified?, "no notification sent"
390
- end
391
-
392
- def test_notifies_on_error_rate_calculation
393
- notifier = gimme_notifier(metric: :failure_count, metric_value: 1)
394
- circuit = Circuitbox::CircuitBreaker.new(:yammer, notifier_class: notifier)
395
- 10.times { circuit.run { raise Timeout::Error }}
396
- assert notifier.notified?, 'no notification sent'
397
- end
398
-
399
- def test_success_count_on_error_rate_calculation
400
- notifier = gimme_notifier(metric: :success_count, metric_value: 6)
401
- circuit = Circuitbox::CircuitBreaker.new(:yammer, notifier_class: notifier)
402
- 10.times { circuit.run { 'success' }}
403
- assert notifier.notified?, 'no notification sent'
404
- end
405
-
406
- def gimme_notifier(opts={})
407
- metric = opts.fetch(:metric,:error_rate)
408
- metric_value = opts.fetch(:metric_value, 0.0)
409
- warning_msg = opts.fetch(:warning_msg, '')
410
- fake_notifier = gimme
411
- notified = false
412
- give(fake_notifier).notify(:open) { notified = true }
413
- give(fake_notifier).notify(:close) { notified = true }
414
- give(fake_notifier).notify_warning(Gimme::Matchers::Anything.new) { notified = true }
415
- give(fake_notifier).metric_gauge(metric, metric_value) { notified = true }
416
- fake_notifier_class = gimme
417
- give(fake_notifier_class).new(:yammer,nil) { fake_notifier }
418
- give(fake_notifier_class).notified? { notified }
419
- give(fake_notifier_class).clear_notified! { notified = false }
420
- fake_notifier_class
421
- end
422
- end
423
-
424
- def emulate_circuit_run(circuit, response_type, response_value)
425
- circuit.run do
426
- case response_type
427
- when :failure
428
- raise response_value
429
- when :success
430
- response_value
431
- end
432
- end
433
- rescue Timeout::Error
434
- nil
435
- end
436
- end