health-reporter 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bec795aa467f18c19a4082de1a606a7f9f85a1f1
4
- data.tar.gz: 6cc065ff3b203db1b5ec80be22c143f98fc2ad70
3
+ metadata.gz: 90f655ef277bd77b6f90e6129976868c5c3f934f
4
+ data.tar.gz: d905978f2768c0472a238e4b5c8c3c0b3831e882
5
5
  SHA512:
6
- metadata.gz: 9efeb9f05cbb9c332e41a742061b771a1e0fd396c93a98faa9dd98ad035e2dfdacf45e818cf5ff068d308bc9d41852bc96023d7aea6f490f5258b4716364a184
7
- data.tar.gz: 6b5048d5c2ef58ae49363915fd62efa39307c13f01f0a05c272c7e1fdb958538190f43dd096de1a8d8e8eecdd6b6bf1d467e660fe3946b377a6b84ce75111363
6
+ metadata.gz: 9ab4e6974bcc5ffad2e8d454e28f21e87d1528c6bd3fbfc4fd13bdcd1850d57a0502df6e7000b9d2fa3d21fa0eabfcca69564b0495f2972f25d256aa8262fd0d
7
+ data.tar.gz: df47ccab910847e661cc3769594344408635bdced26e8c93563bfceb672fa6445ad332010ae94dc05cf0ede58be9fb7bcf80fec1cd13b8afb28a77eae4e935ac
data/.gitignore CHANGED
@@ -50,3 +50,4 @@ build-iPhoneSimulator/
50
50
  .rvmrc
51
51
 
52
52
  Gemfile.lock
53
+ .byebug_history
data/README.md CHANGED
@@ -33,6 +33,7 @@ docker-compose run --rm health-reporter bundle exec rspec -cfd spec/*
33
33
 
34
34
  ## Usage
35
35
 
36
+ #TODO add dependency registration and calling
36
37
 
37
38
  ## Contributing
38
39
 
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
20
  spec.require_paths = ["lib"]
21
21
 
22
+ spec.add_development_dependency 'timecop', '~> 0.8.0'
22
23
  spec.add_development_dependency 'bundler', '~> 1.3'
23
24
  spec.add_development_dependency 'rake', '~> 10.0'
24
25
  spec.add_development_dependency 'rspec', '~> 2.13'
@@ -1,4 +1,4 @@
1
- module HealthReporter
1
+ class HealthReporter
2
2
  end
3
3
 
4
4
  require 'health_reporter/reporter'
@@ -1,15 +1,74 @@
1
- require 'json'
1
+ require 'singleton'
2
2
 
3
- module HealthReporter
4
- class Reporter
5
- def initialize(healthy: true)
6
- @healthy = healthy
7
- end
3
+ class HealthReporter
4
+ include Singleton
5
+
6
+ def self.self_test
7
+ @@self_test
8
+ end
9
+
10
+ def self.self_test=(self_test)
11
+ @@self_test = self_test
12
+ end
13
+
14
+ def self.unhealthy_cache_ttl
15
+ @@unhealthy_cache_ttl
16
+ end
17
+
18
+ def self.unhealthy_cache_ttl=(unhealthy_cache_ttl)
19
+ @@unhealthy_cache_ttl = unhealthy_cache_ttl
20
+ end
21
+
22
+ def self.healthy_cache_ttl
23
+ @@healthy_cache_ttl
24
+ end
8
25
 
26
+ def self.healthy_cache_ttl=(healthy_cache_ttl)
27
+ @@healthy_cache_ttl = healthy_cache_ttl
28
+ end
29
+
30
+ @@self_test = lambda{ true }
31
+ @@unhealthy_cache_ttl = 30
32
+ @@healthy_cache_ttl = 60
33
+ @@last_check_time = nil
34
+ @@healthy = nil #Initialized as nil so that first call will set it
35
+ @@semaphore = Mutex.new
36
+
37
+ # TODO
38
+ # def register_dependency_check(url:, code: 200)
39
+ # raise "Configured URL #{url} is invalid" unless url =~ URI::regexp
40
+ #
41
+ # dependencies[]
42
+ # end
9
43
 
44
+ def self.healthy?
45
+ @@semaphore.synchronize {
46
+ perform_health_check if @@healthy.nil? or cache_ttl_expired
47
+ @@healthy
48
+ }
49
+ end
50
+
51
+ private
10
52
 
11
- def healthy
12
- @healthy
53
+ def self.perform_health_check
54
+ @@healthy = sanitize(@@self_test.call)
55
+ @@last_check_time = Time.now
56
+ end
57
+
58
+ def self.sanitize(result)
59
+ unless [true, false].include?(result)
60
+ raise "Invalid non-boolean response from registered self-check lambda: #{result.to_s}"
13
61
  end
62
+ result
63
+ end
64
+
65
+ def self.cache_ttl_expired
66
+ return true if @@last_check_time.nil?
67
+ Time.now > (@@last_check_time + ttl)
68
+ end
69
+
70
+ def self.ttl
71
+ return @@healthy_cache_ttl if @@healthy
72
+ @@unhealthy_cache_ttl
14
73
  end
15
74
  end
@@ -1,3 +1,3 @@
1
- module HealthReporter
2
- VERSION = '0.0.1'
1
+ class HealthReporter
2
+ VERSION = '0.0.2'
3
3
  end
data/spec/reporter.rb CHANGED
@@ -1,12 +1,199 @@
1
1
  require 'spec_helper'
2
- require 'yaml'
3
2
 
4
- describe HealthReporter::Reporter do
5
- subject { HealthReporter::Reporter }
3
+ describe HealthReporter do
4
+ subject { HealthReporter }
6
5
 
7
6
  it 'has a version number' do
8
7
  expect(HealthReporter::VERSION).not_to be nil
9
8
  end
10
9
 
10
+ before(:each) do
11
+ subject.healthy_cache_ttl = 60
12
+ subject.unhealthy_cache_ttl = 30
13
+ subject.self_test = lambda{ true }
14
+ subject.class_variable_set(:@@last_check_time, nil)
15
+ subject.class_variable_set(:@@healthy, nil)
16
+ Timecop.return
17
+ reset_lambda_runner_spy
18
+ end
19
+
20
+ context 'when configuring' do
21
+ it 'remembers the self-test lambda passed to it' do
22
+ test_lambda = lambda{ 'ab' == 'cd' }
23
+ subject.self_test = test_lambda
24
+ expect(subject.self_test).to be test_lambda
25
+ end
26
+
27
+ it 'uses the self-test lambda passed to it' do
28
+ test_lambda = spy_lambda_returning_false
29
+ subject.self_test = test_lambda
30
+ expect(subject.self_test).to be test_lambda
31
+ expect(subject.healthy?).to be false
32
+ expect(spy_lambda_was_run?).to eq true
33
+ end
34
+
35
+ it 'remembers the cache ttl when healthy' do
36
+ subject.healthy_cache_ttl = 10
37
+ expect(subject.healthy_cache_ttl).to eq 10
38
+ end
39
+
40
+ it 'remembers the cache ttl when not healthy' do
41
+ subject.unhealthy_cache_ttl = 5
42
+ expect(subject.unhealthy_cache_ttl).to eq 5
43
+ end
44
+ end
45
+
46
+ context 'when exercising self-test lambda' do
47
+ it 'allows true to be returned by self-test lambda' do
48
+ test_lambda = lambda{ true }
49
+ subject.self_test = test_lambda
50
+ expect(subject.healthy?).to be true
51
+ end
52
+
53
+ it 'allows false to be returned by self-test lambda' do
54
+ test_lambda = lambda{ false }
55
+ subject.self_test = test_lambda
56
+ expect(subject.healthy?).to be false
57
+ end
58
+
59
+ it 'raises an exception when non-boolean values are returned by self-test lambda' do
60
+ test_lambda = lambda{ "I don't feel well..." }
61
+ subject.self_test = test_lambda
62
+ expect{subject.healthy?}.to raise_error RuntimeError, "Invalid non-boolean response from registered self-check lambda: I don't feel well..."
63
+ end
64
+ end
65
+
66
+ context 'when initialized without any parameters' do
67
+ it 'sets the default self-test lambda to { true }' do
68
+ expect(subject.healthy?).to eq true
69
+ end
70
+
71
+ it 'sets the default healthy state cache ttl to 60 seconds' do
72
+ expect(subject.healthy_cache_ttl).to eq 60
73
+ end
74
+
75
+ it 'sets the default unhealthy state cache ttl to 30 seconds' do
76
+ expect(subject.unhealthy_cache_ttl).to eq 30
77
+ end
78
+ end
79
+
80
+ context 'when calling health check for first time (no cached health state)' do
81
+ it 'calls the configured self-test lambda and returns health' do
82
+ subject.self_test = spy_lambda_returning_false
83
+ expect(subject.healthy?).to be false
84
+ expect(spy_lambda_was_run?).to eq true
85
+ end
86
+ end
11
87
 
88
+ context 'when current state is healty' do
89
+ before(:each) do
90
+ subject.self_test = spy_lambda_returning_true
91
+ subject.healthy? #force the self-test
92
+ reset_lambda_runner_spy #reset the spy so that we can see if it was run or not
93
+ end
94
+
95
+ context 'when neither healty-cache-ttl nor unhealty-cache-ttl has expired' do
96
+ before(:each) do
97
+ subject.unhealthy_cache_ttl = 10
98
+ subject.healthy_cache_ttl = 10
99
+ Timecop.freeze(Time.now + 5)
100
+ end
101
+
102
+ it 'does not call the registered self-test lambda' do
103
+ subject.healthy? #request here and test if it was run in expect below
104
+ expect(spy_lambda_was_run?).to eq false
105
+ end
106
+ it 'returns the current healthy state' do
107
+ expect(subject.healthy?).to be true
108
+ end
109
+ end
110
+
111
+ context 'when healty-cache-ttl has expired' do
112
+ before do
113
+ subject.unhealthy_cache_ttl = 10
114
+ subject.healthy_cache_ttl = 3
115
+ Timecop.freeze(Time.now + 5)
116
+ end
117
+
118
+ it 'calls the registered self-test lambda' do
119
+ subject.healthy? #request here and test if it was run in expect below
120
+ expect(spy_lambda_was_run?).to eq true
121
+ end
122
+ it 'returns the current healthy state' do
123
+ expect(subject.healthy?).to be true
124
+ end
125
+ end
126
+
127
+ context 'when unhealty-cache-ttl has expired' do
128
+ before(:each) do
129
+ subject.unhealthy_cache_ttl = 3
130
+ subject.healthy_cache_ttl = 10
131
+ Timecop.freeze(Time.now + 5)
132
+ end
133
+
134
+ it 'does not call the registered self-test lambda' do
135
+ subject.healthy? #request here and test if it was run in expect below
136
+ expect(spy_lambda_was_run?).to eq false
137
+ end
138
+ it 'returns the current healthy state' do
139
+ expect(subject.healthy?).to be true
140
+ end
141
+ end
142
+ end
143
+
144
+ context 'when current state is unhealty' do
145
+ before(:each) do
146
+ subject.self_test = spy_lambda_returning_false
147
+ subject.healthy? #force the self-test
148
+ reset_lambda_runner_spy #reset the spy so that we can see if it was run or not
149
+ end
150
+
151
+ context 'when neither healty-cache-ttl nor unhealty-cache-ttl has expired' do
152
+ before(:each) do
153
+ subject.unhealthy_cache_ttl = 10
154
+ subject.healthy_cache_ttl = 10
155
+ Timecop.freeze(Time.now + 5)
156
+ end
157
+
158
+ it 'does not call the registered self-test lambda' do
159
+ subject.healthy? #request here and test if it was run in expect below
160
+ expect(spy_lambda_was_run?).to eq false
161
+ end
162
+ it 'returns the current unhealthy state' do
163
+ expect(subject.healthy?).to be false
164
+ end
165
+ end
166
+
167
+ context 'when healty-cache-ttl has expired' do
168
+ before(:each) do
169
+ subject.unhealthy_cache_ttl = 10
170
+ subject.healthy_cache_ttl = 3
171
+ Timecop.freeze(Time.now + 5)
172
+ end
173
+
174
+ it 'does not call the registered self-test lambda' do
175
+ subject.healthy? #request here and test if it was run in expect below
176
+ expect(spy_lambda_was_run?).to eq false
177
+ end
178
+ it 'returns the current unhealthy state' do
179
+ expect(subject.healthy?).to be false
180
+ end
181
+ end
182
+
183
+ context 'when unhealty-cache-ttl has expired' do
184
+ before(:each) do
185
+ subject.unhealthy_cache_ttl = 3
186
+ subject.healthy_cache_ttl = 10
187
+ Timecop.freeze(Time.now + 5)
188
+ end
189
+
190
+ it 'calls the registered self-test lambda' do
191
+ subject.healthy? #request here and test if it was run in expect below
192
+ expect(spy_lambda_was_run?).to eq true
193
+ end
194
+ it 'returns the current unhealthy state' do
195
+ expect(subject.healthy?).to be false
196
+ end
197
+ end
198
+ end
12
199
  end
data/spec/spec_helper.rb CHANGED
@@ -1,11 +1,36 @@
1
1
  require 'simplecov'
2
2
  require 'simplecov-rcov'
3
+ require 'timecop'
3
4
 
4
5
  SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
5
6
  SimpleCov.start do
6
- add_filter "/spec/"
7
+ add_filter "./spec/"
7
8
  end
8
9
 
9
10
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
10
11
 
11
12
  require 'health_reporter'
13
+
14
+
15
+ def reset_lambda_runner_spy
16
+ @test_variable_from_sender = Random.new.rand(1...100000000000)
17
+ @test_variable_from_receiver = 0
18
+ end
19
+
20
+ def spy_lambda_was_run?
21
+ @test_variable_from_receiver == @test_variable_from_sender
22
+ end
23
+
24
+ def spy_lambda_returning_false
25
+ lambda{
26
+ @test_variable_from_receiver = @test_variable_from_sender
27
+ return false
28
+ }
29
+ end
30
+
31
+ def spy_lambda_returning_true
32
+ lambda{
33
+ @test_variable_from_receiver = @test_variable_from_sender
34
+ return true
35
+ }
36
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: health-reporter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Barney de Villiers
@@ -10,6 +10,20 @@ bindir: bin
10
10
  cert_chain: []
11
11
  date: 2018-04-06 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: timecop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.8.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.8.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement