stoplight 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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