stoplight 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,23 +4,21 @@ module Stoplight
4
4
  module Notifier
5
5
  # @note hipchat ~> 1.3.0
6
6
  class HipChat < Base
7
+ DEFAULT_FORMAT = '@all %s'
8
+ DEFAULT_OPTIONS = { color: 'red', message_format: 'text', notify: true }
9
+
7
10
  # @param client [HipChat::Client]
8
11
  # @param room [String]
9
12
  # @param options [Hash]
10
- def initialize(client, room, options = {})
13
+ def initialize(client, room, format = nil, options = {})
11
14
  @client = client
12
15
  @room = room
13
- @options = default_options.merge(options)
16
+ @format = format || DEFAULT_FORMAT
17
+ @options = DEFAULT_OPTIONS.merge(options)
14
18
  end
15
19
 
16
20
  def notify(message)
17
- @client[@room].send('Stoplight', "@all #{message}", @options)
18
- end
19
-
20
- private
21
-
22
- def default_options
23
- { color: 'red', message_format: 'text', notify: true }
21
+ @client[@room].send('Stoplight', @format % message, @options)
24
22
  end
25
23
  end
26
24
  end
@@ -3,8 +3,14 @@
3
3
  module Stoplight
4
4
  module Notifier
5
5
  class StandardError < Base
6
+ DEFAULT_FORMAT = '%s'
7
+
8
+ def initialize(format = nil)
9
+ @format = format || DEFAULT_FORMAT
10
+ end
11
+
6
12
  def notify(message)
7
- warn(message)
13
+ warn(@format % message)
8
14
  end
9
15
  end
10
16
  end
@@ -6,19 +6,29 @@ describe Stoplight::DataStore::Base do
6
6
  subject(:data_store) { described_class.new }
7
7
 
8
8
  %w(
9
- attempts
10
- clear_attempts
11
- clear_failures
12
- delete
13
- failures
14
9
  names
15
- purge
10
+ clear_stale
11
+ clear
12
+ sync
13
+ green?
14
+ yellow?
15
+ red?
16
+ get_color
17
+ get_attempts
16
18
  record_attempt
19
+ clear_attempts
20
+ get_failures
17
21
  record_failure
22
+ clear_failures
23
+ get_state
18
24
  set_state
25
+ clear_state
26
+ get_threshold
19
27
  set_threshold
20
- state
21
- threshold
28
+ clear_threshold
29
+ get_timeout
30
+ set_timeout
31
+ clear_timeout
22
32
  ).each do |method|
23
33
  it "responds to #{method}" do
24
34
  expect(data_store).to respond_to(method)
@@ -0,0 +1,60 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Stoplight::DataStore do
6
+ describe '.validate_color!' do
7
+ subject(:result) { described_class.validate_color!(color) }
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
+ end
49
+
50
+ describe '.validate_timeout!' do
51
+ subject(:result) { described_class.validate_timeout!(timeout) }
52
+ let(:timeout) { nil }
53
+
54
+ context 'with an invalid timeout' do
55
+ it 'raises an error' do
56
+ expect { result }.to raise_error(Stoplight::Error::InvalidTimeout)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -3,21 +3,47 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Stoplight::Failure do
6
- let(:error) { double }
6
+ subject(:failure) { described_class.new(error, time) }
7
+ let(:error) { error_class.new(error_message) }
8
+ let(:error_class) { StandardError }
9
+ let(:error_message) { SecureRandom.hex }
10
+ let(:time) { Time.now }
7
11
 
8
- subject(:failure) { described_class.new(error) }
12
+ describe '.from_json' do
13
+ subject(:result) { described_class.from_json(json) }
14
+ let(:json) { failure.to_json }
9
15
 
10
- describe '#to_json' do
11
- let(:json) { JSON.parse(result) }
16
+ it do
17
+ expect(result.error).to eq(failure.error)
18
+ expect(result.time).to be_within(1).of(failure.time)
19
+ end
20
+ end
21
+
22
+ describe '#initialize' do
23
+ it 'sets the error' do
24
+ expect(failure.error).to eql(error)
25
+ end
12
26
 
13
- subject(:result) { failure.to_json }
27
+ it 'sets the time' do
28
+ expect(failure.time).to eql(time)
29
+ end
30
+
31
+ context 'without a time' do
32
+ let(:time) { nil }
14
33
 
15
- it 'includes the error' do
16
- expect(json['error']).to eql(error.inspect)
34
+ it 'uses the default time' do
35
+ expect(failure.time).to be_within(1).of(Time.now)
36
+ end
17
37
  end
38
+ end
39
+
40
+ describe '#to_json' do
41
+ subject(:json) { failure.to_json }
42
+ let(:data) { JSON.load(json) }
18
43
 
19
- it 'includes the time' do
20
- expect(json['time']).to eql(Time.now.to_s)
44
+ it 'converts to JSON' do
45
+ expect(data['error']).to eql(error.inspect)
46
+ expect(data['time']).to eql(time.inspect)
21
47
  end
22
48
  end
23
49
  end
@@ -1,242 +1,122 @@
1
1
  # coding: utf-8
2
+ # rubocop:disable Metrics/LineLength
2
3
 
3
4
  require 'spec_helper'
4
5
 
5
6
  describe Stoplight::Light do
6
- let(:code) { proc { code_result } }
7
- let(:code_result) { double }
8
- let(:name) { SecureRandom.hex }
9
-
10
- subject(:light) { described_class.new(name, &code) }
11
-
12
- describe '#initialize' do
13
- it 'uses the default allowed errors' do
14
- expect(light.allowed_errors).to eql([])
15
- end
16
-
17
- it 'sets the code' do
18
- expect(light.code).to eql(code.to_proc)
19
- end
20
-
21
- it 'sets the name' do
22
- expect(light.name).to eql(name.to_s)
23
- end
7
+ before do
8
+ @notifiers = Stoplight.notifiers
9
+ Stoplight.notifiers = []
24
10
  end
11
+ after { Stoplight.notifiers = @notifiers }
25
12
 
26
- describe '#run' do
27
- subject(:result) { light.run }
28
-
29
- it 'syncs settings' do
30
- expect(Stoplight.data_store.threshold(name)).to be nil
31
- result
32
- expect(Stoplight.data_store.threshold(name)).to eql(
33
- light.threshold)
34
- end
35
-
36
- context 'green' do
37
- before { allow(light).to receive(:green?).and_return(true) }
38
-
39
- it 'runs the code' do
40
- expect(result).to eql(code_result)
41
- end
42
-
43
- it 'clears failures' do
44
- Stoplight.record_failure(name, nil)
45
- expect(Stoplight.failures(name)).to_not be_empty
46
- result
47
- expect(Stoplight.failures(name)).to be_empty
48
- end
49
-
50
- context 'with failing code' do
51
- let(:code_result) { fail error }
52
- let(:error) { klass.new }
53
- let(:klass) { Class.new(StandardError) }
54
- let(:safe_result) do
55
- begin
56
- result
57
- rescue klass
58
- nil
59
- end
60
- end
61
-
62
- it 'raises the error' do
63
- expect { result }.to raise_error(error)
64
- end
65
-
66
- it 'records the failure' do
67
- expect(Stoplight.failures(name)).to be_empty
68
- safe_result
69
- expect(Stoplight.failures(name)).to_not be_empty
70
- end
71
-
72
- context do
73
- before { light.with_allowed_errors([klass]) }
74
-
75
- it 'clears failures' do
76
- Stoplight.record_failure(name, nil)
77
- expect(Stoplight.failures(name)).to_not be_empty
78
- safe_result
79
- expect(Stoplight.failures(name)).to be_empty
80
- end
81
- end
82
- end
83
- end
84
-
85
- context 'not green' do
86
- let(:fallback) { proc { fallback_result } }
87
- let(:fallback_result) { double }
88
-
89
- before do
90
- light.with_fallback(&fallback)
91
- allow(light).to receive(:green?).and_return(false)
92
- Stoplight.notifiers.each do |notifier|
93
- allow(notifier).to receive(:notify)
94
- end
95
- end
96
-
97
- it 'runs the fallback' do
98
- expect(result).to eql(fallback_result)
99
- end
100
-
101
- it 'records the attempt' do
102
- result
103
- expect(Stoplight.data_store.attempts(name)).to eql(1)
104
- end
105
-
106
- it 'notifies' do
107
- result
108
- Stoplight.notifiers.each do |notifier|
109
- expect(notifier).to have_received(:notify)
110
- end
111
- end
112
-
113
- context 'with an attempt' do
114
- before { allow(Stoplight).to receive(:attempts).and_return(1) }
115
-
116
- it 'does not notify' do
117
- result
118
- Stoplight.notifiers.each do |notifier|
119
- expect(notifier).to_not have_received(:notify)
120
- end
121
- end
122
- end
123
- end
124
- end
125
-
126
- describe '#with_allowed_errors' do
127
- let(:allowed_errors) { [double] }
128
-
129
- subject(:result) { light.with_allowed_errors(allowed_errors) }
130
-
131
- it 'returns self' do
132
- expect(result).to be light
133
- end
134
-
135
- it 'sets the allowed errors' do
136
- expect(result.allowed_errors).to eql(allowed_errors)
137
- end
13
+ subject(:light) { described_class.new(name, &code) }
14
+ let(:allowed_errors) { [error_class] }
15
+ let(:code_result) { double }
16
+ let(:code) { -> { code_result } }
17
+ let(:error_class) { Class.new(StandardError) }
18
+ let(:error) { error_class.new(message) }
19
+ let(:fallback_result) { double }
20
+ let(:fallback) { -> { fallback_result } }
21
+ let(:message) { SecureRandom.hex }
22
+ let(:name) { SecureRandom.hex }
23
+ let(:threshold) { rand(100) }
24
+ let(:timeout) { rand(100) }
25
+
26
+ it { expect(light.run).to eql(code_result) }
27
+ it { expect(light.with_allowed_errors(allowed_errors)).to equal(light) }
28
+ it { expect(light.with_fallback(&fallback)).to equal(light) }
29
+ it { expect(light.with_threshold(threshold)).to equal(light) }
30
+ it { expect(light.with_timeout(timeout)).to equal(light) }
31
+ it { expect { light.fallback }.to raise_error(Stoplight::Error::RedLight) }
32
+ it { expect(light.allowed_errors).to eql([]) }
33
+ it { expect(light.code).to eql(code) }
34
+ it { expect(light.name).to eql(name) }
35
+ it { expect(light.color).to eql(Stoplight::DataStore::COLOR_GREEN) }
36
+ it { expect(light.green?).to eql(true) }
37
+ it { expect(light.yellow?).to eql(false) }
38
+ it { expect(light.red?).to eql(false) }
39
+ it { expect(light.threshold).to eql(Stoplight::DataStore::DEFAULT_THRESHOLD) }
40
+ it { expect(light.timeout).to eql(Stoplight::DataStore::DEFAULT_TIMEOUT) }
41
+
42
+ it 'sets the allowed errors' do
43
+ light.with_allowed_errors(allowed_errors)
44
+ expect(light.allowed_errors).to eql(allowed_errors)
138
45
  end
139
46
 
140
- describe '#with_fallback' do
141
- let(:fallback) { proc { fallback_result } }
142
- let(:fallback_result) { double }
143
-
144
- subject(:result) { light.with_fallback(&fallback) }
145
-
146
- it 'returns self' do
147
- expect(result).to be light
148
- end
149
-
150
- it 'sets the fallback' do
151
- expect(result.fallback).to eql(fallback)
152
- end
47
+ it 'sets the fallback' do
48
+ light.with_fallback(&fallback)
49
+ expect(light.fallback).to eql(fallback)
153
50
  end
154
51
 
155
- describe '#with_threshold' do
156
- let(:threshold) { rand(10) }
157
-
158
- subject(:result) { light.with_threshold(threshold) }
159
-
160
- it 'returns self' do
161
- expect(result).to be light
162
- end
163
-
164
- it 'sets the threshold' do
165
- expect(result.threshold).to eql(threshold)
166
- end
52
+ it 'sets the threshold' do
53
+ light.with_threshold(threshold)
54
+ expect(light.threshold).to eql(threshold)
167
55
  end
168
56
 
169
- describe '#fallback' do
170
- subject(:result) { light.fallback }
171
-
172
- it 'uses the default fallback' do
173
- expect { result }.to raise_error(Stoplight::Error::RedLight)
174
- end
57
+ it 'sets the timeout' do
58
+ light.with_timeout(timeout)
59
+ expect(light.timeout).to eql(timeout)
175
60
  end
176
61
 
177
- describe '#green?' do
178
- subject(:result) { light.green? }
62
+ context 'failing' do
63
+ let(:code_result) { fail error }
179
64
 
180
- it 'is true' do
181
- expect(result).to be true
182
- end
183
-
184
- context 'locked green' do
185
- before do
186
- Stoplight.set_state(name, Stoplight::DataStore::STATE_LOCKED_GREEN)
187
- end
188
-
189
- it 'is true' do
190
- expect(result).to be true
65
+ it 'switches to red' do
66
+ light.threshold.times do
67
+ expect(light.green?).to eql(true)
68
+ expect { light.run }.to raise_error(error_class)
191
69
  end
70
+ expect(light.red?).to eql(true)
71
+ expect { light.run }.to raise_error(Stoplight::Error::RedLight)
192
72
  end
193
73
 
194
- context 'locked red' do
195
- before do
196
- Stoplight.set_state(name, Stoplight::DataStore::STATE_LOCKED_RED)
197
- end
74
+ context 'with allowed errors' do
75
+ before { light.with_allowed_errors(allowed_errors) }
198
76
 
199
- it 'is false' do
200
- expect(result).to be false
77
+ it 'stays green' do
78
+ light.threshold.times do
79
+ expect(light.green?).to eql(true)
80
+ expect { light.run }.to raise_error(error_class)
81
+ end
82
+ expect(light.green?).to eql(true)
83
+ expect { light.run }.to raise_error(error_class)
201
84
  end
202
85
  end
203
86
 
204
- context 'with failures' do
205
- before do
206
- light.threshold.times { Stoplight.record_failure(name, nil) }
207
- end
87
+ context 'with fallback' do
88
+ before { light.with_fallback(&fallback) }
208
89
 
209
- it 'is false' do
210
- expect(result).to be false
90
+ it 'calls the fallback' do
91
+ light.threshold.times do
92
+ expect(light.green?).to eql(true)
93
+ expect { light.run }.to raise_error(error_class)
94
+ end
95
+ expect(light.red?).to eql(true)
96
+ expect(light.run).to eql(fallback_result)
211
97
  end
212
98
  end
213
- end
214
-
215
- describe '#red?' do
216
- subject(:result) { light.red? }
217
99
 
218
- context 'green' do
219
- before { allow(light).to receive(:green?).and_return(true) }
100
+ context 'with threshold' do
101
+ before { light.with_threshold(0) }
220
102
 
221
- it 'is false' do
222
- expect(result).to be false
103
+ it 'stays red' do
104
+ expect(light.red?).to eql(true)
105
+ expect { light.run }.to raise_error(Stoplight::Error::RedLight)
223
106
  end
224
107
  end
225
108
 
226
- context 'not green' do
227
- before { allow(light).to receive(:green?).and_return(false) }
109
+ context 'with timeout' do
110
+ before { light.with_timeout(-1) }
228
111
 
229
- it 'is true' do
230
- expect(result).to be true
112
+ it 'switch to yellow' do
113
+ light.threshold.times do
114
+ expect(light.green?).to eql(true)
115
+ expect { light.run }.to raise_error(error_class)
116
+ end
117
+ expect(light.yellow?).to eql(true)
118
+ expect { light.run }.to raise_error(error_class)
231
119
  end
232
120
  end
233
121
  end
234
-
235
- describe '#threshold' do
236
- subject(:result) { light.threshold }
237
-
238
- it 'uses the default threshold' do
239
- expect(result).to eql(Stoplight::DEFAULT_THRESHOLD)
240
- end
241
- end
242
122
  end