stoplight 0.2.1 → 0.3.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.
@@ -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