stoplight 0.4.1 → 0.5.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/CHANGELOG.md +15 -0
- data/LICENSE.md +1 -1
- data/README.md +66 -63
- data/lib/stoplight.rb +10 -15
- data/lib/stoplight/color.rb +9 -0
- data/lib/stoplight/data_store.rb +0 -146
- data/lib/stoplight/data_store/base.rb +7 -130
- data/lib/stoplight/data_store/memory.rb +25 -100
- data/lib/stoplight/data_store/redis.rb +61 -119
- data/lib/stoplight/default.rb +34 -0
- data/lib/stoplight/error.rb +0 -42
- data/lib/stoplight/failure.rb +21 -25
- data/lib/stoplight/light.rb +42 -127
- data/lib/stoplight/light/runnable.rb +97 -0
- data/lib/stoplight/notifier/base.rb +1 -4
- data/lib/stoplight/notifier/hip_chat.rb +17 -32
- data/lib/stoplight/notifier/io.rb +9 -9
- data/lib/stoplight/state.rb +9 -0
- data/spec/spec_helper.rb +2 -3
- data/spec/stoplight/color_spec.rb +39 -0
- data/spec/stoplight/data_store/base_spec.rb +56 -36
- data/spec/stoplight/data_store/memory_spec.rb +120 -2
- data/spec/stoplight/data_store/redis_spec.rb +123 -24
- data/spec/stoplight/data_store_spec.rb +2 -69
- data/spec/stoplight/default_spec.rb +86 -0
- data/spec/stoplight/error_spec.rb +29 -0
- data/spec/stoplight/failure_spec.rb +61 -51
- data/spec/stoplight/light/runnable_spec.rb +234 -0
- data/spec/stoplight/light_spec.rb +143 -191
- data/spec/stoplight/notifier/base_spec.rb +8 -11
- data/spec/stoplight/notifier/hip_chat_spec.rb +66 -55
- data/spec/stoplight/notifier/io_spec.rb +49 -21
- data/spec/stoplight/notifier_spec.rb +3 -0
- data/spec/stoplight/state_spec.rb +39 -0
- data/spec/stoplight_spec.rb +2 -65
- metadata +55 -19
- data/spec/support/data_store.rb +0 -36
- data/spec/support/fakeredis.rb +0 -3
- data/spec/support/hipchat.rb +0 -3
@@ -3,74 +3,7 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe Stoplight::DataStore do
|
6
|
-
|
7
|
-
|
8
|
-
let(:color) { nil }
|
9
|
-
|
10
|
-
context 'with an invalid color' do
|
11
|
-
it 'raises an error' do
|
12
|
-
expect { result }.to raise_error(Stoplight::Error::InvalidColor)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe '.validate_failure!' do
|
18
|
-
subject(:result) { described_class.validate_failure!(failure) }
|
19
|
-
let(:failure) { nil }
|
20
|
-
|
21
|
-
context 'with an invalid failure' do
|
22
|
-
it 'raises an error' do
|
23
|
-
expect { result }.to raise_error(Stoplight::Error::InvalidFailure)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
describe '.validate_state!' do
|
29
|
-
subject(:result) { described_class.validate_state!(state) }
|
30
|
-
let(:state) { nil }
|
31
|
-
|
32
|
-
context 'with an invalid state' do
|
33
|
-
it 'raises an error' do
|
34
|
-
expect { result }.to raise_error(Stoplight::Error::InvalidState)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
describe '.validate_threshold!' do
|
40
|
-
subject(:result) { described_class.validate_threshold!(threshold) }
|
41
|
-
let(:threshold) { nil }
|
42
|
-
|
43
|
-
context 'with an invalid threshold' do
|
44
|
-
it 'raises an error' do
|
45
|
-
expect { result }.to raise_error(Stoplight::Error::InvalidThreshold)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
context 'with a negative threshold' do
|
50
|
-
let(:threshold) { -1 }
|
51
|
-
|
52
|
-
it 'raises an error' do
|
53
|
-
expect { result }.to raise_error(Stoplight::Error::InvalidThreshold)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
context 'with a zero threshold' do
|
58
|
-
let(:threshold) { 0 }
|
59
|
-
|
60
|
-
it 'raises an error' do
|
61
|
-
expect { result }.to raise_error(Stoplight::Error::InvalidThreshold)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
describe '.validate_timeout!' do
|
67
|
-
subject(:result) { described_class.validate_timeout!(timeout) }
|
68
|
-
let(:timeout) { nil }
|
69
|
-
|
70
|
-
context 'with an invalid timeout' do
|
71
|
-
it 'raises an error' do
|
72
|
-
expect { result }.to raise_error(Stoplight::Error::InvalidTimeout)
|
73
|
-
end
|
74
|
-
end
|
6
|
+
it 'is a module' do
|
7
|
+
expect(described_class).to be_a(Module)
|
75
8
|
end
|
76
9
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Stoplight::Default do
|
6
|
+
it 'is a module' do
|
7
|
+
expect(described_class).to be_a(Module)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '::ALLOWED_ERRORS' do
|
11
|
+
it 'is an array' do
|
12
|
+
expect(Stoplight::Default::ALLOWED_ERRORS).to be_an(Array)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'contains exception classes' do
|
16
|
+
Stoplight::Default::ALLOWED_ERRORS.each do |allowed_error|
|
17
|
+
expect(allowed_error).to be < Exception
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'is frozen' do
|
22
|
+
expect(Stoplight::Default::ALLOWED_ERRORS).to be_frozen
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '::DATA_STORE' do
|
27
|
+
it 'is a data store' do
|
28
|
+
expect(Stoplight::Default::DATA_STORE).to be_a(Stoplight::DataStore::Base)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '::ERROR_NOTIFIER' do
|
33
|
+
it 'is a proc' do
|
34
|
+
expect(Stoplight::Default::ERROR_NOTIFIER).to be_a(Proc)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'has an arity of 1' do
|
38
|
+
expect(Stoplight::Default::ERROR_NOTIFIER.arity).to eql(1)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '::FALLBACK' do
|
43
|
+
it 'is nil' do
|
44
|
+
expect(Stoplight::Default::FALLBACK).to eql(nil)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '::FORMATTER' do
|
49
|
+
it 'is a proc' do
|
50
|
+
expect(Stoplight::Default::FORMATTER).to be_a(Proc)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'has the same arity as #notify' do
|
54
|
+
notify = Stoplight::Notifier::Base.new.method(:notify)
|
55
|
+
expect(Stoplight::Default::FORMATTER.arity).to eql(notify.arity)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '::NOTIFIERS' do
|
60
|
+
it 'is an array' do
|
61
|
+
expect(Stoplight::Default::NOTIFIERS).to be_an(Array)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'contains notifiers' do
|
65
|
+
Stoplight::Default::NOTIFIERS.each do |notifier|
|
66
|
+
expect(notifier).to be_a(Stoplight::Notifier::Base)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'is frozen' do
|
71
|
+
expect(Stoplight::Default::NOTIFIERS).to be_frozen
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '::THRESHOLD' do
|
76
|
+
it 'is an integer' do
|
77
|
+
expect(Stoplight::Default::THRESHOLD).to be_a(Fixnum)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '::TIMEOUT' do
|
82
|
+
it 'is a float' do
|
83
|
+
expect(Stoplight::Default::TIMEOUT).to be_a(Float)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Stoplight::Error do
|
6
|
+
it 'is a module' do
|
7
|
+
expect(described_class).to be_a(Module)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '::Base' do
|
11
|
+
it 'is a class' do
|
12
|
+
expect(Stoplight::Error::Base).to be_a(Class)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'is a subclass of StandardError' do
|
16
|
+
expect(Stoplight::Error::Base).to be < StandardError
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '::RedLight' do
|
21
|
+
it 'is a class' do
|
22
|
+
expect(Stoplight::Error::RedLight).to be_a(Class)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'is a subclass of StandardError' do
|
26
|
+
expect(Stoplight::Error::RedLight).to be < Stoplight::Error::Base
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -3,80 +3,90 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe Stoplight::Failure do
|
6
|
-
|
7
|
-
let(:error_class) {
|
8
|
-
let(:error_message) {
|
9
|
-
let(:time) { Time.
|
6
|
+
let(:error) { ZeroDivisionError.new('divided by 0') }
|
7
|
+
let(:error_class) { error.class.name }
|
8
|
+
let(:error_message) { error.message }
|
9
|
+
let(:time) { Time.new(2001, 2, 3, 4, 5, 6, '+07:08') }
|
10
|
+
let(:json) do
|
11
|
+
JSON.generate(
|
12
|
+
error: { class: error_class, message: error_message },
|
13
|
+
time: time.strftime('%Y-%m-%dT%H:%M:%S.%N%:z'))
|
14
|
+
end
|
10
15
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
let(:error_class) { Class.new(StandardError) }
|
16
|
+
it 'is a class' do
|
17
|
+
expect(described_class).to be_a(Class)
|
18
|
+
end
|
15
19
|
|
20
|
+
describe '.from_error' do
|
16
21
|
it 'creates a failure' do
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
22
|
+
Timecop.freeze do
|
23
|
+
failure = described_class.from_error(error)
|
24
|
+
expect(failure.error_class).to eql(error_class)
|
25
|
+
expect(failure.error_message).to eql(error_message)
|
26
|
+
expect(failure.time).to eql(Time.new)
|
27
|
+
end
|
21
28
|
end
|
22
29
|
end
|
23
30
|
|
24
31
|
describe '.from_json' do
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
expect(
|
30
|
-
expect(result.error_message).to eq(failure.error_message)
|
31
|
-
expect(result.time).to be_within(1).of(failure.time)
|
32
|
+
it 'parses JSON' do
|
33
|
+
failure = described_class.from_json(json)
|
34
|
+
expect(failure.error_class).to eql(error_class)
|
35
|
+
expect(failure.error_message).to eql(error_message)
|
36
|
+
expect(failure.time).to eql(time)
|
32
37
|
end
|
38
|
+
end
|
33
39
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
+
describe '#==' do
|
41
|
+
it 'is true when they are equal' do
|
42
|
+
failure = described_class.new(error_class, error_message, time)
|
43
|
+
other = described_class.new(error_class, error_message, time)
|
44
|
+
expect(failure).to eq(other)
|
45
|
+
end
|
40
46
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
47
|
+
it 'is false when they have different error classes' do
|
48
|
+
failure = described_class.new(error_class, error_message, time)
|
49
|
+
other = described_class.new(nil, error_message, time)
|
50
|
+
expect(failure).to_not eq(other)
|
46
51
|
end
|
47
|
-
end
|
48
52
|
|
49
|
-
|
50
|
-
|
51
|
-
|
53
|
+
it 'is false when they have different error messages' do
|
54
|
+
failure = described_class.new(error_class, error_message, time)
|
55
|
+
other = described_class.new(error_class, nil, time)
|
56
|
+
expect(failure).to_not eq(other)
|
52
57
|
end
|
53
58
|
|
54
|
-
it '
|
55
|
-
|
59
|
+
it 'is false when they have different times' do
|
60
|
+
failure = described_class.new(error_class, error_message, time)
|
61
|
+
other = described_class.new(error_class, error_message, nil)
|
62
|
+
expect(failure).to_not eq(other)
|
56
63
|
end
|
64
|
+
end
|
57
65
|
|
58
|
-
|
59
|
-
|
66
|
+
describe '#error_class' do
|
67
|
+
it 'reads the error class' do
|
68
|
+
expect(described_class.new(error_class, nil, nil).error_class)
|
69
|
+
.to eql(error_class)
|
60
70
|
end
|
71
|
+
end
|
61
72
|
|
62
|
-
|
63
|
-
|
73
|
+
describe '#error_message' do
|
74
|
+
it 'reads the error message' do
|
75
|
+
expect(described_class.new(nil, error_message, nil).error_message)
|
76
|
+
.to eql(error_message)
|
77
|
+
end
|
78
|
+
end
|
64
79
|
|
65
|
-
|
66
|
-
|
67
|
-
|
80
|
+
describe '#time' do
|
81
|
+
it 'reads the time' do
|
82
|
+
expect(described_class.new(nil, nil, time).time).to eql(time)
|
68
83
|
end
|
69
84
|
end
|
70
85
|
|
71
86
|
describe '#to_json' do
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
it 'converts to JSON' do
|
77
|
-
expect(data['error']['class']).to eql(error_class)
|
78
|
-
expect(data['error']['message']).to eql(error_message)
|
79
|
-
expect(data['time']).to eql('2001-02-03T04:05:06Z')
|
87
|
+
it 'generates JSON' do
|
88
|
+
expect(described_class.new(error_class, error_message, time).to_json)
|
89
|
+
.to eql(json)
|
80
90
|
end
|
81
91
|
end
|
82
92
|
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Stoplight::Light::Runnable do
|
6
|
+
subject { Stoplight::Light.new(name, &code) }
|
7
|
+
|
8
|
+
let(:code) { -> { code_result } }
|
9
|
+
let(:code_result) { random_string }
|
10
|
+
let(:fallback) { -> _ { fallback_result } }
|
11
|
+
let(:fallback_result) { random_string }
|
12
|
+
let(:name) { random_string }
|
13
|
+
|
14
|
+
let(:failure) do
|
15
|
+
Stoplight::Failure.new(error.class.name, error.message, time)
|
16
|
+
end
|
17
|
+
let(:error) { error_class.new(error_message) }
|
18
|
+
let(:error_class) { Class.new(StandardError) }
|
19
|
+
let(:error_message) { random_string }
|
20
|
+
let(:time) { Time.new }
|
21
|
+
|
22
|
+
def random_number
|
23
|
+
rand(1_000_000)
|
24
|
+
end
|
25
|
+
|
26
|
+
def random_string
|
27
|
+
('a'..'z').to_a.shuffle.first(8).join
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#color' do
|
31
|
+
it 'is initially green' do
|
32
|
+
expect(subject.color).to eql(Stoplight::Color::GREEN)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'is green when locked green' do
|
36
|
+
subject.data_store.set_state(subject, Stoplight::State::LOCKED_GREEN)
|
37
|
+
expect(subject.color).to eql(Stoplight::Color::GREEN)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'is red when locked red' do
|
41
|
+
subject.data_store.set_state(subject, Stoplight::State::LOCKED_RED)
|
42
|
+
expect(subject.color).to eql(Stoplight::Color::RED)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'is red when there are many failures' do
|
46
|
+
subject.threshold.times do
|
47
|
+
subject.data_store.record_failure(subject, failure)
|
48
|
+
end
|
49
|
+
expect(subject.color).to eql(Stoplight::Color::RED)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'is yellow when the most recent failure is old' do
|
53
|
+
(subject.threshold - 1).times do
|
54
|
+
subject.data_store.record_failure(subject, failure)
|
55
|
+
end
|
56
|
+
other = Stoplight::Failure.new(
|
57
|
+
error.class.name, error.message, Time.new - subject.timeout)
|
58
|
+
subject.data_store.record_failure(subject, other)
|
59
|
+
expect(subject.color).to eql(Stoplight::Color::YELLOW)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#run' do
|
64
|
+
let(:notifiers) { [notifier] }
|
65
|
+
let(:notifier) { Stoplight::Notifier::IO.new(io) }
|
66
|
+
let(:io) { StringIO.new }
|
67
|
+
|
68
|
+
before { subject.with_notifiers(notifiers) }
|
69
|
+
|
70
|
+
context 'when the light is green' do
|
71
|
+
before { subject.data_store.clear_failures(subject) }
|
72
|
+
|
73
|
+
it 'runs the code' do
|
74
|
+
expect(subject.run).to eql(code_result)
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'with some failures' do
|
78
|
+
before { subject.data_store.record_failure(subject, failure) }
|
79
|
+
|
80
|
+
it 'clears the failures' do
|
81
|
+
subject.run
|
82
|
+
expect(subject.data_store.get_failures(subject).size).to eql(0)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'when the code is failing' do
|
87
|
+
let(:code_result) { fail error }
|
88
|
+
|
89
|
+
it 're-raises the error' do
|
90
|
+
expect { subject.run }.to raise_error(error.class)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'records the failure' do
|
94
|
+
expect(subject.data_store.get_failures(subject).size).to eql(0)
|
95
|
+
begin
|
96
|
+
subject.run
|
97
|
+
rescue error.class
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
expect(subject.data_store.get_failures(subject).size).to eql(1)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'notifies when transitioning to red' do
|
104
|
+
subject.threshold.times do
|
105
|
+
expect(io.string).to eql('')
|
106
|
+
begin
|
107
|
+
subject.run
|
108
|
+
rescue error.class
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
end
|
112
|
+
expect(io.string).to_not eql('')
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'when the error is allowed' do
|
116
|
+
let(:allowed_errors) { [error.class] }
|
117
|
+
|
118
|
+
before { subject.with_allowed_errors(allowed_errors) }
|
119
|
+
|
120
|
+
it 'does not record the failure' do
|
121
|
+
expect(subject.data_store.get_failures(subject).size).to eql(0)
|
122
|
+
begin
|
123
|
+
subject.run
|
124
|
+
rescue error.class
|
125
|
+
nil
|
126
|
+
end
|
127
|
+
expect(subject.data_store.get_failures(subject).size).to eql(0)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'with a fallback' do
|
132
|
+
before { subject.with_fallback(&fallback) }
|
133
|
+
|
134
|
+
it 'runs the fallback' do
|
135
|
+
expect(subject.run).to eql(fallback_result)
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'passes the error to the fallback' do
|
139
|
+
subject.with_fallback do |e|
|
140
|
+
expect(e).to eql(error)
|
141
|
+
fallback_result
|
142
|
+
end
|
143
|
+
expect(subject.run).to eql(fallback_result)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'when the data store is failing' do
|
149
|
+
let(:data_store) { Object.new }
|
150
|
+
let(:error_notifier) { -> _ {} }
|
151
|
+
|
152
|
+
before do
|
153
|
+
subject
|
154
|
+
.with_data_store(data_store)
|
155
|
+
.with_error_notifier(&error_notifier)
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'runs the code' do
|
159
|
+
expect(subject.run).to eql(code_result)
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'notifies about the error' do
|
163
|
+
has_notified = false
|
164
|
+
subject.with_error_notifier do |e|
|
165
|
+
has_notified = true
|
166
|
+
expect(e).to be_a(NoMethodError)
|
167
|
+
end
|
168
|
+
subject.run
|
169
|
+
expect(has_notified).to eql(true)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context 'when the light is yellow' do
|
175
|
+
before do
|
176
|
+
(subject.threshold - 1).times do
|
177
|
+
subject.data_store.record_failure(subject, failure)
|
178
|
+
end
|
179
|
+
|
180
|
+
other = Stoplight::Failure.new(
|
181
|
+
error.class.name, error.message, time - subject.timeout)
|
182
|
+
subject.data_store.record_failure(subject, other)
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'runs the code' do
|
186
|
+
expect(subject.run).to eql(code_result)
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'notifies when transitioning to green' do
|
190
|
+
expect(io.string).to eql('')
|
191
|
+
subject.run
|
192
|
+
expect(io.string).to_not eql('')
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
context 'when the light is red' do
|
197
|
+
before do
|
198
|
+
subject.threshold.times do
|
199
|
+
subject.data_store.record_failure(subject, failure)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'raises an error' do
|
204
|
+
expect { subject.run }.to raise_error(Stoplight::Error::RedLight)
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'uses the name as the error message' do
|
208
|
+
e =
|
209
|
+
begin
|
210
|
+
subject.run
|
211
|
+
rescue Stoplight::Error::RedLight => e
|
212
|
+
e
|
213
|
+
end
|
214
|
+
expect(e.message).to eql(subject.name)
|
215
|
+
end
|
216
|
+
|
217
|
+
context 'with a fallback' do
|
218
|
+
before { subject.with_fallback(&fallback) }
|
219
|
+
|
220
|
+
it 'runs the fallback' do
|
221
|
+
expect(subject.run).to eql(fallback_result)
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'does not pass anything to the fallback' do
|
225
|
+
subject.with_fallback do |e|
|
226
|
+
expect(e).to eql(nil)
|
227
|
+
fallback_result
|
228
|
+
end
|
229
|
+
expect(subject.run).to eql(fallback_result)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|