stoplight 0.1.0 → 0.2.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,187 +4,8 @@ require 'spec_helper'
4
4
  require 'fakeredis'
5
5
 
6
6
  describe Stoplight::DataStore::Redis do
7
- let(:error) { error_class.new }
8
- let(:error_class) { Class.new(StandardError) }
9
- let(:name) { SecureRandom.hex }
10
- let(:state) { Stoplight::DataStore::STATES.to_a.sample }
11
- let(:threshold) { rand(10) }
7
+ subject(:data_store) { described_class.new(redis) }
8
+ let(:redis) { Redis.new }
12
9
 
13
- subject(:data_store) { described_class.new }
14
-
15
- describe '#attempts' do
16
- subject(:result) { data_store.attempts(name) }
17
-
18
- it 'returns 0' do
19
- expect(result).to eql(0)
20
- end
21
-
22
- context 'with an attempt' do
23
- before { data_store.record_attempt(name) }
24
-
25
- it 'returns 1' do
26
- expect(result).to eql(1)
27
- end
28
- end
29
- end
30
-
31
- describe '#clear_attempts' do
32
- subject(:result) { data_store.clear_attempts(name) }
33
-
34
- it 'returns 0' do
35
- expect(result).to eql(0)
36
- end
37
-
38
- context 'with an attempt' do
39
- before { data_store.record_attempt(name) }
40
-
41
- it 'returns 1' do
42
- expect(result).to eql(1)
43
- end
44
-
45
- it 'clears the attempts' do
46
- result
47
- expect(data_store.attempts(name)).to eql(0)
48
- end
49
- end
50
- end
51
-
52
- describe '#clear_failures' do
53
- subject(:result) { data_store.clear_failures(name) }
54
-
55
- it 'returns 0' do
56
- expect(result).to eql(0)
57
- end
58
-
59
- context 'with a failure' do
60
- before { data_store.record_failure(name, error) }
61
-
62
- it 'returns 1' do
63
- expect(result).to eql(1)
64
- end
65
-
66
- it 'clears the failures' do
67
- result
68
- expect(data_store.failures(name)).to be_empty
69
- end
70
- end
71
- end
72
-
73
- describe '#failures' do
74
- subject(:result) { data_store.failures(name) }
75
-
76
- it 'returns an empty array' do
77
- expect(result).to be_an(Array)
78
- expect(result).to be_empty
79
- end
80
-
81
- context 'with a failure' do
82
- before { data_store.record_failure(name, error) }
83
-
84
- it 'returns a non-empty array' do
85
- expect(result).to be_an(Array)
86
- expect(result).to_not be_empty
87
- end
88
- end
89
- end
90
-
91
- describe '#names' do
92
- subject(:result) { data_store.names }
93
-
94
- it 'returns an array' do
95
- expect(result).to be_an(Array)
96
- end
97
-
98
- context 'with a name' do
99
- before { data_store.set_threshold(name, threshold) }
100
-
101
- it 'includes the name' do
102
- expect(result).to include(name)
103
- end
104
- end
105
- end
106
-
107
- describe '#record_attempt' do
108
- subject(:result) { data_store.record_attempt(name) }
109
-
110
- it 'returns 1' do
111
- expect(result).to eql(1)
112
- end
113
-
114
- it 'records the attempt' do
115
- result
116
- expect(data_store.attempts(name)).to eql(1)
117
- end
118
- end
119
-
120
- describe '#record_failure' do
121
- subject(:result) { data_store.record_failure(name, error) }
122
-
123
- it 'returns 1' do
124
- expect(result).to eql(1)
125
- end
126
-
127
- it 'records the failure' do
128
- result
129
- expect(data_store.failures(name)).to_not be_empty
130
- end
131
- end
132
-
133
- describe '#set_state' do
134
- subject(:result) { data_store.set_state(name, state) }
135
-
136
- it 'returns the state' do
137
- expect(result).to eql(state)
138
- end
139
-
140
- it 'sets the state' do
141
- result
142
- expect(data_store.state(name)).to eql(state)
143
- end
144
- end
145
-
146
- describe '#set_threshold' do
147
- subject(:result) { data_store.set_threshold(name, threshold) }
148
-
149
- it 'returns the threshold' do
150
- expect(result).to eql(threshold)
151
- end
152
-
153
- it 'sets the threshold' do
154
- result
155
- expect(data_store.threshold(name)).to eql(threshold)
156
- end
157
- end
158
-
159
- describe '#state' do
160
- subject(:result) { data_store.state(name) }
161
-
162
- it 'returns the default state' do
163
- expect(result).to eql(Stoplight::DataStore::STATE_UNLOCKED)
164
- end
165
-
166
- context 'with a state' do
167
- before { data_store.set_state(name, state) }
168
-
169
- it 'returns the state' do
170
- expect(result).to eql(state)
171
- end
172
- end
173
- end
174
-
175
- describe '#threshold' do
176
- subject(:result) { data_store.threshold(name) }
177
-
178
- it 'returns nil' do
179
- expect(result).to be(nil)
180
- end
181
-
182
- context 'with a threshold' do
183
- before { data_store.set_threshold(name, threshold) }
184
-
185
- it 'returns the threshold' do
186
- expect(result).to eql(threshold)
187
- end
188
- end
189
- end
10
+ it_behaves_like 'a data store'
190
11
  end
@@ -89,6 +89,9 @@ describe Stoplight::Light do
89
89
  before do
90
90
  light.with_fallback(&fallback)
91
91
  allow(light).to receive(:green?).and_return(false)
92
+ Stoplight.notifiers.each do |notifier|
93
+ allow(notifier).to receive(:notify)
94
+ end
92
95
  end
93
96
 
94
97
  it 'runs the fallback' do
@@ -99,6 +102,24 @@ describe Stoplight::Light do
99
102
  result
100
103
  expect(Stoplight.data_store.attempts(name)).to eql(1)
101
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
102
123
  end
103
124
  end
104
125
 
@@ -149,7 +170,7 @@ describe Stoplight::Light do
149
170
  subject(:result) { light.fallback }
150
171
 
151
172
  it 'uses the default fallback' do
152
- expect { result }.to raise_error(Stoplight::Error::NoFallback)
173
+ expect { result }.to raise_error(Stoplight::Error::RedLight)
153
174
  end
154
175
  end
155
176
 
@@ -215,8 +236,7 @@ describe Stoplight::Light do
215
236
  subject(:result) { light.threshold }
216
237
 
217
238
  it 'uses the default threshold' do
218
- expect(result).to eql(
219
- described_class.const_get(:DEFAULT_THRESHOLD))
239
+ expect(result).to eql(Stoplight::DEFAULT_THRESHOLD)
220
240
  end
221
241
  end
222
242
  end
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Stoplight::Mixin do
6
+ subject(:klass) { Class.new.extend(described_class) }
7
+
8
+ describe '#stoplight' do
9
+ subject(:result) { klass.stoplight(name, &code) }
10
+ let(:name) { SecureRandom.hex }
11
+ let(:code) { proc { code_result } }
12
+ let(:code_result) { double }
13
+
14
+ let(:light) { double }
15
+
16
+ before do
17
+ allow(Stoplight::Light).to receive(:new).and_return(light)
18
+ allow(light).to receive(:run).and_return(code.call)
19
+ end
20
+
21
+ it 'calls .new' do
22
+ expect(Stoplight::Light).to receive(:new)
23
+ result
24
+ end
25
+
26
+ it 'calls #run' do
27
+ expect(light).to receive(:run)
28
+ result
29
+ end
30
+
31
+ it 'returns the result of #run' do
32
+ expect(result).to eql(code_result)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Stoplight::Notifier::Base do
6
+ subject(:notifier) { described_class.new }
7
+
8
+ %w(
9
+ notify
10
+ ).each do |method|
11
+ it "responds to #{method}" do
12
+ expect(notifier).to respond_to(method)
13
+ end
14
+
15
+ it "does not implement #{method}" do
16
+ args = [nil] * notifier.method(method).arity
17
+ expect { notifier.public_send(method, *args) }.to raise_error(
18
+ NotImplementedError)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Stoplight::Notifier::HipChat do
6
+ subject(:notifier) { described_class.new(client, room, options) }
7
+ let(:client) { double }
8
+ let(:room) { SecureRandom.hex }
9
+ let(:options) { {} }
10
+
11
+ describe '#notify' do
12
+ subject(:result) { notifier.notify(message) }
13
+ let(:message) { SecureRandom.hex }
14
+
15
+ it 'sends the message to HipChat' do
16
+ expect(client).to receive(:[]).with(room).and_return(client)
17
+ expect(client).to receive(:send)
18
+ result
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Stoplight::Notifier::StandardError do
6
+ subject(:notifier) { described_class.new }
7
+
8
+ describe '#notify' do
9
+ let(:message) { SecureRandom.hex }
10
+
11
+ subject(:result) { notifier.notify(message) }
12
+
13
+ it 'emits the message as a warning' do
14
+ expect(notifier).to receive(:warn).with(message)
15
+ result
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,6 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Stoplight::Notifier do
6
+ end
@@ -35,10 +35,10 @@ describe Stoplight do
35
35
 
36
36
  before do
37
37
  @data_store = described_class.data_store
38
- described_class.data_store(data_store)
38
+ described_class.data_store = data_store
39
39
  end
40
40
 
41
- after { described_class.data_store(@data_store) }
41
+ after { described_class.data_store = @data_store }
42
42
 
43
43
  it 'returns the data store' do
44
44
  expect(result).to eql(data_store)
@@ -46,6 +46,36 @@ describe Stoplight do
46
46
  end
47
47
  end
48
48
 
49
+ describe '.notifiers' do
50
+ subject(:result) { described_class.notifiers }
51
+
52
+ it 'uses the default notifier' do
53
+ expect(result).to be_an(Array)
54
+ expect(result.size).to eql(1)
55
+ expect(result.first).to be_a(Stoplight::Notifier::StandardError)
56
+ end
57
+
58
+ it 'memoizes the result' do
59
+ expect(result).to be described_class.notifiers
60
+ end
61
+
62
+ context 'with custom notifiers' do
63
+ let(:notifiers) { [notifier] }
64
+ let(:notifier) { double }
65
+
66
+ before do
67
+ @notifiers = described_class.notifiers
68
+ described_class.notifiers = notifiers
69
+ end
70
+
71
+ after { described_class.notifiers = @notifiers }
72
+
73
+ it 'returns the notifiers' do
74
+ expect(result).to eql(notifiers)
75
+ end
76
+ end
77
+ end
78
+
49
79
  describe '.green?' do
50
80
  subject(:result) { described_class.green?(name) }
51
81
 
@@ -112,7 +142,7 @@ describe Stoplight do
112
142
  subject(:result) { described_class.threshold(name) }
113
143
 
114
144
  it 'uses the default threshold' do
115
- expect(result).to eql(Stoplight::Light::DEFAULT_THRESHOLD)
145
+ expect(result).to eql(Stoplight::DEFAULT_THRESHOLD)
116
146
  end
117
147
 
118
148
  context 'with a custom threshold' do
@@ -0,0 +1,178 @@
1
+ # coding: utf-8
2
+
3
+ shared_examples_for 'a data store' do
4
+ let(:error) { error_class.new }
5
+ let(:error_class) { Class.new(StandardError) }
6
+ let(:name) { SecureRandom.hex }
7
+ let(:state) { Stoplight::DataStore::STATES.to_a.sample }
8
+ let(:threshold) { rand(10) }
9
+
10
+ it 'is a DataStore::Base' do
11
+ expect(data_store).to be_a(Stoplight::DataStore::Base)
12
+ end
13
+
14
+ describe '#names' do
15
+ subject(:result) { data_store.names }
16
+
17
+ it 'returns an array' do
18
+ expect(result).to be_an(Array)
19
+ end
20
+
21
+ context 'with a name' do
22
+ before do
23
+ @data_store = Stoplight.data_store
24
+ Stoplight.data_store = data_store
25
+ Stoplight::Light.new(name) {}.run
26
+ end
27
+ after { Stoplight.data_store = @data_store }
28
+
29
+ it 'includes the name' do
30
+ expect(result).to include(name)
31
+ end
32
+ end
33
+ end
34
+
35
+ context 'attempts' do
36
+ describe '#attempts' do
37
+ subject(:result) { data_store.attempts(name) }
38
+
39
+ it 'returns an integer' do
40
+ expect(result).to be_an(Integer)
41
+ end
42
+
43
+ context 'with an attempt' do
44
+ it 'includes the attempt' do
45
+ attempts = data_store.attempts(name)
46
+ data_store.record_attempt(name)
47
+ expect(result).to be > attempts
48
+ end
49
+ end
50
+ end
51
+
52
+ describe '#clear_attempts' do
53
+ subject(:result) { data_store.clear_attempts(name) }
54
+
55
+ context 'with an attempt' do
56
+ before { data_store.record_attempt(name) }
57
+
58
+ it 'clears the attempts' do
59
+ result
60
+ expect(data_store.attempts(name)).to eql(0)
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '#record_attempt' do
66
+ subject(:result) { data_store.record_attempt(name) }
67
+
68
+ it 'records the attempt' do
69
+ attempts = data_store.attempts(name)
70
+ result
71
+ expect(data_store.attempts(name)).to eql(attempts + 1)
72
+ end
73
+ end
74
+ end
75
+
76
+ context 'failures' do
77
+ describe '#clear_failures' do
78
+ subject(:result) { data_store.clear_failures(name) }
79
+
80
+ context 'with a failure' do
81
+ before { data_store.record_failure(name, error) }
82
+
83
+ it 'clears the failures' do
84
+ result
85
+ expect(data_store.failures(name)).to be_empty
86
+ end
87
+ end
88
+ end
89
+
90
+ describe '#failures' do
91
+ subject(:result) { data_store.failures(name) }
92
+
93
+ it 'returns an array' do
94
+ expect(result).to be_an(Array)
95
+ end
96
+
97
+ context 'with a failure' do
98
+ it 'includes the failure' do
99
+ failures = data_store.failures(name)
100
+ data_store.record_failure(name, error)
101
+ expect(result.size).to be > failures.size
102
+ end
103
+ end
104
+ end
105
+
106
+ describe '#record_failure' do
107
+ subject(:result) { data_store.record_failure(name, error) }
108
+
109
+ it 'records the failure' do
110
+ failures = data_store.failures(name)
111
+ result
112
+ expect(data_store.failures(name).size).to eql(failures.size + 1)
113
+ end
114
+ end
115
+ end
116
+
117
+ context 'state' do
118
+ describe '#set_state' do
119
+ subject(:result) { data_store.set_state(name, state) }
120
+
121
+ it 'returns the state' do
122
+ expect(result).to eql(state)
123
+ end
124
+
125
+ it 'sets the state' do
126
+ result
127
+ expect(data_store.state(name)).to eql(state)
128
+ end
129
+ end
130
+
131
+ describe '#state' do
132
+ subject(:result) { data_store.state(name) }
133
+
134
+ it 'returns the default state' do
135
+ expect(result).to eql(Stoplight::DataStore::STATE_UNLOCKED)
136
+ end
137
+
138
+ context 'with a state' do
139
+ before { data_store.set_state(name, state) }
140
+
141
+ it 'returns the state' do
142
+ expect(result).to eql(state)
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ context 'threshold' do
149
+ describe '#set_threshold' do
150
+ subject(:result) { data_store.set_threshold(name, threshold) }
151
+
152
+ it 'returns the threshold' do
153
+ expect(result).to eql(threshold)
154
+ end
155
+
156
+ it 'sets the threshold' do
157
+ result
158
+ expect(data_store.threshold(name)).to eql(threshold)
159
+ end
160
+ end
161
+
162
+ describe '#threshold' do
163
+ subject(:result) { data_store.threshold(name) }
164
+
165
+ it 'returns nil' do
166
+ expect(result).to eql(nil)
167
+ end
168
+
169
+ context 'with a threshold' do
170
+ before { data_store.set_threshold(name, threshold) }
171
+
172
+ it 'returns the threshold' do
173
+ expect(result).to eql(threshold)
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end