wellness 0.2.3 → 1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3b9c9d8b20440639156bfee90743eddc5ba49188
4
- data.tar.gz: bc55f4040c571be2d5b4d15557b1dcdcfb6f66e2
3
+ metadata.gz: 92be2479007d38b17c45cdc5a0b40b83875f4489
4
+ data.tar.gz: 37d30c24be4446ccb60de012daa4368566b247ba
5
5
  SHA512:
6
- metadata.gz: d11e691c8048d2f2a61fb5d5abfc19ed38d62f2cb5d5153cb3ac87a433dd675bc069c2cd05dd37e53cbdfe47bf8b3d9e4289cea1869922b44ea6c9a4a0171a9f
7
- data.tar.gz: 68080655a8da97463ba809c5e4dd9a93a13cae97b2aff071f328a7b9fc20fabaad1dd447c49ba72615ba42bf2d958bc04716799135100120c03ee194c5255407
6
+ metadata.gz: 5fa52e4ef5da3267dce1855e53e2a2a8eedf70bff366776584bdca5627cc739c3422efd6a37a07689d940e2de2bb2f42c7bb78f73279c2a84a6734a5f3b1c399
7
+ data.tar.gz: 991e99f1e18ec611701cdec10765d7e8169b859c7e04b77bc50945bc2fbf1fb8e41aab850cbda8ac95342f613f08966edeaf01e7a85e7ff54852e2f2b62086c1
data/README.md CHANGED
@@ -15,26 +15,20 @@ external dependencies that need to be loaded, that your application may not
15
15
  necessarily have.
16
16
 
17
17
  ```rb
18
- # app/config/application.rb
19
-
20
- # Add this to the top just below the bundler requires
21
- require 'wellness/services/postgres_service'
22
- require 'wellness/services/redis_service'
23
-
24
18
  # Within the configuration block
19
+
25
20
  system = Wellness::System.new('my-uber-duber-app')
26
- pg = Wellness::Services::PostgresService.new({
27
- host: ENV['POSTGRESQL_HOST'],
28
- port: ENV['POSTGRESQL_PORT'],
21
+ system.use(Wellness::Services::PostgresService, 'database', {
22
+ host: ENV['POSTGRESQL_HOST'],
23
+ port: ENV['POSTGRESQL_PORT'],
29
24
  database: ENV['POSTGRESQL_DATABASE'],
30
- user: ENV['POSTGRESQL_USERNAME'],
25
+ user: ENV['POSTGRESQL_USERNAME'],
31
26
  password: ENV['POSTGRESQL_PASSWORD']
32
27
  })
33
- redis = Wellness::Services::RedisService.new({
28
+ system.use(Wellness::Services::RedisService, 'redis', {
34
29
  host: ENV['REDIS_HOST']
35
30
  })
36
- system.add_service('database', pg)
37
- system.add_service('redis', redis)
31
+
38
32
  config.middleware.insert_before('::ActiveRecord::QueryCache', 'Wellness::Middleware', system)
39
33
  ```
40
34
 
@@ -42,22 +36,18 @@ config.middleware.insert_before('::ActiveRecord::QueryCache', 'Wellness::Middlew
42
36
 
43
37
  ```ruby
44
38
  require 'wellness'
45
- require 'wellness/services/postgres_service'
46
- require 'wellness/services/redis_service'
47
39
 
48
40
  system = Wellness::System.new('my-uber-duber-app')
49
- pg = Wellness::Services::PostgresService.new({
50
- host: ENV['POSTGRESQL_HOST'],
51
- port: ENV['POSTGRESQL_PORT'],
41
+ system.use(Wellness::Services::PostgresService, 'database', {
42
+ host: ENV['POSTGRESQL_HOST'],
43
+ port: ENV['POSTGRESQL_PORT'],
52
44
  database: ENV['POSTGRESQL_DATABASE'],
53
- user: ENV['POSTGRESQL_USERNAME'],
45
+ user: ENV['POSTGRESQL_USERNAME'],
54
46
  password: ENV['POSTGRESQL_PASSWORD']
55
47
  })
56
- redis = Wellness::Services::RedisService.new({
48
+ system.use(Wellness::Services::RedisService, 'redis', {
57
49
  host: ENV['REDIS_HOST']
58
50
  })
59
- system.add_service('database', pg)
60
- system.add_service('redis', redis)
61
51
 
62
52
  use(Wellness::Middleware, system)
63
53
  ```
@@ -115,12 +105,10 @@ run time. It can lead to unintended consequences, and weird bugs.
115
105
  class MyCustomService < Wellness::Services::Base
116
106
  def check
117
107
  if params[:foo]
118
- passed_check
119
108
  {
120
109
  'status': 'HEALTHY'
121
110
  }
122
111
  else
123
- failed_check
124
112
  {
125
113
  'status': 'UNHEALTHY'
126
114
  }
@@ -130,8 +118,29 @@ end
130
118
 
131
119
  # Initialize the wellness system
132
120
  system = Wellness::System.new('my-app')
133
- service = MyCustomService.new({foo: 'bar'})
134
- system.add_service('some service', service)
121
+ system.use(MyCustomService, 'some service', {foo: 'bar'})
122
+
123
+ # Load it into your rack
124
+ use(Wellness::Middleware, system)
125
+ ```
126
+
127
+ ## Custom Details
128
+
129
+ ```ruby
130
+ # Your custom detail component
131
+ class MyDetail < Wellness::Detail
132
+ def check
133
+ {
134
+ 'foo' => 12,
135
+ 'bar' => 31,
136
+ 'qux' => options[:qux]
137
+ }
138
+ end
139
+ end
140
+
141
+ # Initialize the wellness system
142
+ system = Wellness::System.new('my-app')
143
+ system.use(MyDetail, 'something', { qux: 9000 })
135
144
 
136
145
  # Load it into your rack
137
146
  use(Wellness::Middleware, system)
@@ -0,0 +1,24 @@
1
+ module Wellness
2
+ # The parent class of all details that need to run.
3
+ #
4
+ # @author Matthew A. Johnston (warmwaffles)
5
+ class Detail
6
+ attr_reader :name, :options, :result
7
+
8
+ def initialize(name, options={})
9
+ @name = name
10
+ @options = options
11
+ @result = {}
12
+ end
13
+
14
+ def call
15
+ @result = self.check
16
+ self
17
+ end
18
+
19
+ # @return [Hash]
20
+ def check
21
+ {}
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ module Wellness
2
+ # A simple class that builds the provided class with the provided arguments
3
+ # @author Matthew A. Johnston (warmwaffles)
4
+ class Factory
5
+ def initialize(klass, *args)
6
+ @klass = klass
7
+ @args = args
8
+ end
9
+
10
+ def build
11
+ @klass.new(*@args)
12
+ end
13
+ end
14
+ end
@@ -6,38 +6,25 @@ module Wellness
6
6
  def initialize(app, system, options={})
7
7
  @app = app
8
8
  @system = system
9
-
10
- # Optional arguments
11
- @health_status_path = options[:status_path] || '/health/status'
12
- @health_details_path = options[:details_path] || '/health/details'
9
+ @options = options
13
10
  end
14
11
 
15
- def call(env)
16
- case env['PATH_INFO']
17
- when @health_status_path
18
- health_status_check
19
- when @health_details_path
20
- health_details_check
21
- else
22
- @app.call(env)
23
- end
12
+ def health_status_path
13
+ @options[:status_path] || '/health/status'
24
14
  end
25
15
 
26
- private
27
-
28
- def health_status_check
29
- if @system.check
30
- [200, { 'Content-Type' => 'application/json' }, [{ status: 'HEALTHY' }.to_json]]
31
- else
32
- [500, { 'Content-Type' => 'application/json' }, [{ status: 'UNHEALTHY' }.to_json]]
33
- end
16
+ def health_details_path
17
+ @options[:details_path] || '/health/details'
34
18
  end
35
19
 
36
- def health_details_check
37
- if @system.check
38
- [200, { 'Content-Type' => 'application/json' }, [@system.to_json]]
20
+ def call(env)
21
+ case env['PATH_INFO']
22
+ when health_status_path
23
+ @system.simple_check
24
+ when health_details_path
25
+ @system.detailed_check
39
26
  else
40
- [500, { 'Content-Type' => 'application/json' }, [@system.to_json]]
27
+ @app.call(env)
41
28
  end
42
29
  end
43
30
  end
@@ -0,0 +1,59 @@
1
+ module Wellness
2
+ # A simple presenter for the services and details of the Wellness System.
3
+ #
4
+ # @author Matthew A. Johnston (warmwaffles)
5
+ class Report
6
+ # @param services [Hash]
7
+ # @param details [Hash]
8
+ def initialize(services, details)
9
+ @services = services
10
+ @details = details
11
+ end
12
+
13
+ # @return [Hash]
14
+ def detailed
15
+ {
16
+ status: status,
17
+ services: services,
18
+ details: details
19
+ }
20
+ end
21
+
22
+ # @return [Hash]
23
+ def simple
24
+ {
25
+ status: status
26
+ }
27
+ end
28
+
29
+ # Returns the {#detailed} hash in json form
30
+ #
31
+ # @return [String]
32
+ def to_json(*)
33
+ detailed.to_json
34
+ end
35
+
36
+ # @return [String]
37
+ def status
38
+ healthy? ? 'HEALTHY' : 'UNHEALTHY'
39
+ end
40
+
41
+ # @return [Integer]
42
+ def status_code
43
+ healthy? ? 200 : 500
44
+ end
45
+
46
+ # @return [TrueClass,FalseClass]
47
+ def healthy?
48
+ @services.all?(&:healthy?)
49
+ end
50
+
51
+ def services
52
+ Hash[@services.map { |s| [s.name, s.result] }]
53
+ end
54
+
55
+ def details
56
+ Hash[@details.map { |d| [d.name, d.result] }]
57
+ end
58
+ end
59
+ end
@@ -2,7 +2,7 @@ module Wellness
2
2
  module Services
3
3
  # @author Matthew A. Johnston
4
4
  class Base
5
- attr_reader :params
5
+ attr_reader :params, :result, :name
6
6
 
7
7
  # Load dependencies when the class is loaded. This makes putting requires
8
8
  # at the top of the file unnecessary. It plays nicely with the
@@ -12,51 +12,37 @@ module Wellness
12
12
  end
13
13
 
14
14
  # @param params [Hash]
15
- def initialize(params={})
15
+ def initialize(name, params={})
16
+ @name = name
16
17
  @params = params
17
- @health = false
18
- @mutex = Mutex.new
18
+ @result = {}
19
19
  end
20
20
 
21
- # Flags the check as failed
22
- # @return [FalseClass]
23
- def failed_check
24
- @mutex.synchronize do
25
- @health = false
26
- end
21
+ # Returns true if the service is healthy, otherwise false
22
+ # @return [TrueClass,FalseClass]
23
+ def healthy?
24
+ @result.fetch(:status, 'UNHEALTHY') == 'HEALTHY'
27
25
  end
28
26
 
29
- # Flags the check as passed
30
- # @return [TrueClass]
31
27
  def passed_check
32
- @mutex.synchronize do
33
- @health = true
34
- end
28
+ warn('#passed_check has been deprecated')
35
29
  end
36
30
 
37
- # Returns true if the service is healthy, otherwise false
38
- # @return [TrueClass,FalseClass]
39
- def healthy?
40
- @mutex.synchronize do
41
- !!@health
42
- end
31
+ def failed_check
32
+ warn('#failed_check has been deprecated')
43
33
  end
44
34
 
45
- # @return [Hash]
35
+ # @return [Wellness::Services::Base]
46
36
  def call
47
- @last_check = self.check
37
+ @result = self.check
38
+ self
48
39
  end
49
40
 
50
41
  # @return [Hash]
51
42
  def check
52
- {}
53
- end
54
-
55
- # @return [Hash]
56
- def last_check
57
- @mutex.synchronize do
58
- @last_check ||= {}
59
- end
43
+ {
44
+ status: 'UNHEALTHY'
45
+ }
60
46
  end
61
47
  end
62
48
  end
@@ -6,8 +6,9 @@ module Wellness
6
6
  require('pg')
7
7
  end
8
8
 
9
+ # @return [Hash]
9
10
  def check
10
- case ping
11
+ case PG::Connection.ping(connection_options)
11
12
  when PG::Constants::PQPING_NO_ATTEMPT
12
13
  ping_failed('no attempt made to ping')
13
14
  when PG::Constants::PQPING_NO_RESPONSE
@@ -21,10 +22,6 @@ module Wellness
21
22
 
22
23
  private
23
24
 
24
- def ping
25
- PG::Connection.ping(connection_options)
26
- end
27
-
28
25
  # @return [Hash]
29
26
  def connection_options
30
27
  {
@@ -39,7 +36,6 @@ module Wellness
39
36
  # @param message [String] the reason it failed
40
37
  # @return [Hash]
41
38
  def ping_failed(message)
42
- failed_check
43
39
  {
44
40
  status: 'UNHEALTHY',
45
41
  details: {
@@ -50,7 +46,6 @@ module Wellness
50
46
 
51
47
  # @return [Hash]
52
48
  def ping_successful
53
- passed_check
54
49
  {
55
50
  status: 'HEALTHY',
56
51
  details: {
@@ -22,17 +22,29 @@ module Wellness
22
22
  require('redis')
23
23
  end
24
24
 
25
+ # @return [Hash]
25
26
  def check
26
27
  client = build_client
27
28
  details = client.info.select { |k, _| KEYS.include?(k) }
29
+ passed(details)
30
+ rescue Redis::BaseError => error
31
+ failed(error)
32
+ rescue Exception => error
33
+ failed(error)
34
+ end
28
35
 
29
- passed_check
36
+ def build_client
37
+ Redis.new(self.params)
38
+ end
39
+
40
+ def passed(details)
30
41
  {
31
42
  status: 'HEALTHY',
32
43
  details: details
33
44
  }
34
- rescue Redis::BaseError => error
35
- failed_check
45
+ end
46
+
47
+ def failed(error)
36
48
  {
37
49
  status: 'UNHEALTHY',
38
50
  details: {
@@ -40,10 +52,6 @@ module Wellness
40
52
  }
41
53
  }
42
54
  end
43
-
44
- def build_client
45
- Redis.new(self.params)
46
- end
47
55
  end
48
56
  end
49
57
  end
@@ -9,14 +9,14 @@ module Wellness
9
9
  require 'sidekiq'
10
10
  end
11
11
 
12
+ # @return [Hash]
12
13
  def check
13
14
  sidekiq_stats = Sidekiq::Stats.new
14
15
  queue = Sidekiq::Queue.new
15
16
  redis = Redis.new(self.params.fetch(:redis))
16
17
  redis_stats = redis.info.select { |k, _| KEYS.include?(k) }
17
- workers_size = redis.scard("workers").to_i
18
+ workers_size = redis.scard('workers').to_i
18
19
 
19
- passed_check
20
20
  {
21
21
  status: 'HEALTHY',
22
22
  details: {
@@ -31,7 +31,6 @@ module Wellness
31
31
  }
32
32
  }
33
33
  rescue => error
34
- failed_check
35
34
  {
36
35
  status: 'UNHEALTHY',
37
36
  details: {
@@ -1,61 +1,49 @@
1
1
  module Wellness
2
- # @author Matthew A. Johnston
2
+ # @author Matthew A. Johnston (warmwaffles)
3
3
  class System
4
- attr_reader :name, :services
5
-
6
- attr_accessor :details
4
+ attr_reader :name
7
5
 
8
6
  # @param name [String] the name of the system
9
7
  def initialize(name)
10
8
  @name = name
11
- @services = Hash.new
12
- @details = Hash.new
13
- end
14
-
15
- # Add a service to this system
16
- #
17
- # @param name [String, Symbol] the name of the service
18
- # @param service [Wellness::Service] the service you wish to add
19
- def add_service(name, service)
20
- @services[name] = service
9
+ @services = []
10
+ @details = []
11
+ @mutex = Mutex.new
21
12
  end
22
13
 
23
- # Remove a service from this system
24
- #
25
- # @param name [String, Symbol]
26
- # @return [Wellness::Service] the service removed, else nil
27
- def remove_service(name)
28
- @services.delete(name)
14
+ def use(klass, *args)
15
+ @mutex.synchronize do
16
+ factory = Factory.new(klass, *args)
17
+ if klass <= Wellness::Services::Base
18
+ @services << factory
19
+ else
20
+ @details << factory
21
+ end
22
+ end
29
23
  end
30
24
 
31
- # Checks all of the services
32
- # @return
33
- def check
34
- @services.values.each { |service| service.call }
35
- healthy?
25
+ def detailed_check
26
+ report = build_report
27
+ [report.status_code, { 'Content-Type' => 'application/json' }, [report.detailed.to_json]]
36
28
  end
37
29
 
38
- # Returns true if the system is healthy, false otherwise
39
- #
40
- # @return [Boolean]
41
- def healthy?
42
- @services.values.all? { |service| service.healthy? }
30
+ def simple_check
31
+ report = build_report
32
+ [report.status_code, { 'Content-Type' => 'application/json' }, [report.simple.to_json]]
43
33
  end
44
34
 
45
- def to_json(*)
46
- dependencies = Hash.new
47
-
48
- @services.each do |name, service|
49
- dependencies[name] = service.last_check
35
+ def build_report
36
+ @mutex.synchronize do
37
+ Wellness::Report.new(services.map(&:call), details.map(&:call))
50
38
  end
39
+ end
51
40
 
52
- data = {
53
- status: (healthy? ? 'HEALTHY' : 'UNHEALTHY'),
54
- details: @details,
55
- dependencies: dependencies
56
- }
41
+ def services
42
+ @services.map(&:build)
43
+ end
57
44
 
58
- data.to_json
45
+ def details
46
+ @details.map(&:build)
59
47
  end
60
48
  end
61
49
  end
data/lib/wellness.rb CHANGED
@@ -1,9 +1,12 @@
1
1
  require 'json'
2
2
 
3
3
  module Wellness
4
- VERSION = '0.2.3'
4
+ VERSION = '1.0.0'
5
5
 
6
6
  autoload :Services, 'wellness/services'
7
7
  autoload :Middleware, 'wellness/middleware'
8
8
  autoload :System, 'wellness/system'
9
+ autoload :Detail, 'wellness/detail'
10
+ autoload :Report, 'wellness/report'
11
+ autoload :Factory, 'wellness/factory'
9
12
  end
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,10 @@
1
1
  require 'simplecov'
2
2
  SimpleCov.start do
3
+ add_filter('/spec')
3
4
  add_group('Services', 'wellness/services')
4
5
  end
5
6
 
7
+ Dir[Dir.pwd.concat('/spec/support/**/*.rb')].each { |f| require f }
8
+
6
9
  require 'rspec'
7
10
  require 'wellness'
@@ -0,0 +1,17 @@
1
+ class UnhealthyService < Wellness::Services::Base
2
+ def check
3
+ { status: 'UNHEALTHY' }
4
+ end
5
+ end
6
+
7
+ class HealthyService < Wellness::Services::Base
8
+ def check
9
+ { status: 'HEALTHY' }
10
+ end
11
+ end
12
+
13
+ class MockedDetail < Wellness::Detail
14
+ def check
15
+ { 'data' => 'here' }
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Wellness::Detail do
4
+ let(:detail) { MockedDetail.new('test_detail') }
5
+
6
+ describe '#name' do
7
+ subject { detail.name }
8
+ it 'returns "test_detail"' do
9
+ expect(subject).to eq('test_detail')
10
+ end
11
+ end
12
+
13
+ describe '#call' do
14
+ subject { detail.call }
15
+ it 'sets the result' do
16
+ expect { subject }.to change(detail, :result)
17
+ end
18
+ end
19
+
20
+ describe '#check' do
21
+ subject { detail.check }
22
+ it 'returns a hash' do
23
+ expect(subject).to be_a(Hash)
24
+ end
25
+ end
26
+ end
@@ -1,138 +1,66 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Wellness::Middleware do
4
- let(:app) { double('Application', call: true) }
5
- let(:system) { Wellness::System.new('testing') }
6
- let(:middleware) { Wellness::Middleware.new(app, system) }
7
-
8
- class PassingMockService < Wellness::Services::Base
9
- def check
10
- passed_check
11
- {
12
- 'status' => 'HEALTHY',
13
- 'details' => {}
14
- }
4
+ let(:app) { double('Application') }
5
+ let(:system) { Wellness::System.new('test_system') }
6
+
7
+ describe '#health_status_path' do
8
+ subject { middleware.health_status_path }
9
+ context 'when the status_path is not passed' do
10
+ let(:middleware) { described_class.new(app, system) }
11
+ it 'returns "/health/status"' do
12
+ expect(subject).to eq('/health/status')
13
+ end
14
+ end
15
+ context 'when the status_path is passed' do
16
+ let(:options) { {status_path: '/path/to/status' } }
17
+ let(:middleware) { described_class.new(app, system, options) }
18
+ it 'returns "/path/to/status"' do
19
+ expect(subject).to eq('/path/to/status')
20
+ end
15
21
  end
16
22
  end
17
23
 
18
- class FailingMockService < Wellness::Services::Base
19
- def check
20
- failed_check
21
- {
22
- 'status' => 'UNHEALTHY',
23
- 'details' => {}
24
- }
24
+ describe '#health_details_path' do
25
+ context 'when the details_path is not passed' do
26
+ let(:middleware) { described_class.new(app, system) }
27
+ subject { middleware.health_details_path }
28
+ it 'returns "/health/details"' do
29
+ expect(subject).to eq('/health/details')
30
+ end
31
+ end
32
+ context 'when the details_path is passed' do
33
+ let(:options) { {details_path: '/path/to/status' } }
34
+ let(:middleware) { described_class.new(app, system, options) }
35
+ subject { middleware.health_details_path }
36
+ it 'returns "/path/to/status"' do
37
+ expect(subject).to eq('/path/to/status')
38
+ end
25
39
  end
26
40
  end
27
41
 
28
42
  describe '#call' do
29
- context 'when the PATH_INFO is the health status path' do
43
+ let(:middleware) { described_class.new(app, system) }
44
+ context 'when the PATH_INFO matches the health_status_path' do
30
45
  let(:env) { { 'PATH_INFO' => '/health/status' } }
31
- subject { middleware.call(env) }
32
-
33
- context 'when the system check is unhealthy' do
34
- before { system.stub(check: false) }
35
-
36
- it 'returns a 500 status' do
37
- expect(subject[0]).to eq(500)
38
- end
39
-
40
- it 'returns a json content type' do
41
- expect(subject[1]).to eq({ 'Content-Type' => 'application/json' })
42
- end
43
-
44
- it 'returns UNHEALTHY' do
45
- data = JSON.parse(subject[2].first)
46
- expect(data).to eq({ 'status' => 'UNHEALTHY' })
47
- end
48
- end
49
-
50
- context 'when the system check is healthy' do
51
- before { system.stub(check: true) }
52
-
53
- it 'returns a 200 status' do
54
- expect(subject[0]).to eq(200)
55
- end
56
-
57
- it 'returns a json content type' do
58
- expect(subject[1]).to eq({ 'Content-Type' => 'application/json' })
59
- end
60
-
61
- it 'returns HEALTHY' do
62
- data = JSON.parse(subject[2].first)
63
- expect(data).to eq({ 'status' => 'HEALTHY' })
64
- end
46
+ before { system.stub(simple_check: true) }
47
+ it 'calls #simple_check on the system' do
48
+ middleware.call(env)
49
+ expect(system).to have_received(:simple_check).once
65
50
  end
66
51
  end
67
-
68
- context 'when the PATH_INFO is the health details path' do
52
+ context 'when the PATH_INFO matches the health_details_path' do
69
53
  let(:env) { { 'PATH_INFO' => '/health/details' } }
70
- subject { middleware.call(env) }
71
-
72
- context 'when the system check is unhealthy' do
73
- before do
74
- system.add_service('mock service', FailingMockService.new)
75
- end
76
-
77
- it 'returns a 500 status' do
78
- expect(subject[0]).to eq(500)
79
- end
80
-
81
- it 'returns a json content type' do
82
- expect(subject[1]).to eq({ 'Content-Type' => 'application/json' })
83
- end
84
-
85
- it 'returns UNHEALTHY' do
86
- expected = {
87
- 'status' => 'UNHEALTHY',
88
- 'details' => {},
89
- 'dependencies' => {
90
- 'mock service' => {
91
- 'status' => 'UNHEALTHY',
92
- 'details' => {}
93
- }
94
- }
95
- }
96
-
97
- data = JSON.parse(subject[2].first)
98
- expect(data).to eq(expected)
99
- end
100
- end
101
- context 'when the system check is healthy' do
102
- before do
103
- system.add_service('mock service', PassingMockService.new)
104
- end
105
-
106
- it 'returns a 200 status' do
107
- expect(subject[0]).to eq(200)
108
- end
109
-
110
- it 'returns a json content type' do
111
- expect(subject[1]).to eq({ 'Content-Type' => 'application/json' })
112
- end
113
-
114
- it 'returns UNHEALTHY' do
115
- expected = {
116
- 'status' => 'HEALTHY',
117
- 'details' => {},
118
- 'dependencies' => {
119
- 'mock service' => {
120
- 'status' => 'HEALTHY',
121
- 'details' => {}
122
- }
123
- }
124
- }
125
-
126
- data = JSON.parse(subject[2].first)
127
- expect(data).to eq(expected)
128
- end
54
+ before { system.stub(detailed_check: true) }
55
+ it 'calls #detailed_check on the system' do
56
+ middleware.call(env)
57
+ expect(system).to have_received(:detailed_check).once
129
58
  end
130
59
  end
131
-
132
- context 'when the PATH_INFO is /who/cares' do
133
- let(:env) { { 'PATH_INFO' => '/who/cares' } }
134
-
135
- it 'passes the request to the next rack' do
60
+ context 'when the PATH_INFO doesn\'t match any of the paths' do
61
+ let(:env) { { 'PATH_INFO' => '/different/path' } }
62
+ before { app.stub(call: true) }
63
+ it 'calls #detailed_check on the system' do
136
64
  middleware.call(env)
137
65
  expect(app).to have_received(:call).with(env)
138
66
  end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ describe Wellness::Report do
4
+ let(:report) { described_class.new({}, {}) }
5
+
6
+ describe '#to_json' do
7
+ subject { report.to_json }
8
+ it 'returns a json string' do
9
+ expect(JSON.parse(subject)).to be_a(Hash)
10
+ end
11
+ end
12
+
13
+ describe '#detailed' do
14
+ subject { report.detailed }
15
+ it 'returns a hash' do
16
+ expect(subject).to be_a(Hash)
17
+ end
18
+ end
19
+
20
+ describe '#simple' do
21
+ subject { report.simple }
22
+ it 'returns a hash' do
23
+ expect(subject).to be_a(Hash)
24
+ end
25
+ end
26
+
27
+ describe '#status' do
28
+ subject { report.status }
29
+ context 'when the report is healthy' do
30
+ before { report.stub(:healthy? => true) }
31
+ it 'returns HEALTHY' do
32
+ expect(subject).to eq('HEALTHY')
33
+ end
34
+ end
35
+ context 'when the report is not healthy' do
36
+ before { report.stub(:healthy? => false) }
37
+ it 'returns UNHEALTHY' do
38
+ expect(subject).to eq('UNHEALTHY')
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '#status_code' do
44
+ subject { report.status_code }
45
+ context 'when the report is healthy' do
46
+ before { report.stub(:healthy? => true) }
47
+ it 'returns 200' do
48
+ expect(subject).to eq(200)
49
+ end
50
+ end
51
+ context 'when the report is not healthy' do
52
+ before { report.stub(:healthy? => false) }
53
+ it 'returns 500' do
54
+ expect(subject).to eq(500)
55
+ end
56
+ end
57
+ end
58
+
59
+ describe '#healthy?' do
60
+ subject { report.healthy? }
61
+ context 'when all of the report are healthy' do
62
+ let(:services) { [HealthyService.new('service-a'), HealthyService.new('service-b')] }
63
+ let(:report) { described_class.new(services, []) }
64
+ before { services.collect(&:call) }
65
+ it 'returns true' do
66
+ expect(subject).to eq(true)
67
+ end
68
+ end
69
+ context 'when one of the reports are not healthy' do
70
+ let(:services) { [HealthyService.new('service-a'), UnhealthyService.new('service-b')] }
71
+ let(:report) { described_class.new(services, []) }
72
+ before { services.collect(&:call) }
73
+ it 'returns false' do
74
+ expect(subject).to eq(false)
75
+ end
76
+ end
77
+ context 'when all of the reports are not healthy' do
78
+ let(:services) { [UnhealthyService.new('service-a'), UnhealthyService.new('service-b')] }
79
+ let(:report) { described_class.new(services, []) }
80
+ before { services.collect(&:call) }
81
+ it 'returns false' do
82
+ expect(subject).to eq(false)
83
+ end
84
+ end
85
+ end
86
+ end
87
+
@@ -1,40 +1,51 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Wellness::Services::Base do
4
- let(:params) { { test: 'data' } }
4
+ let(:params) { { foo: 'bar' } }
5
5
  let(:service) { described_class.new(params) }
6
- describe '#failed_check' do
7
- before { service.passed_check }
8
- subject { service.failed_check }
9
- it 'returns false' do
10
- expect(subject).to eq(false)
6
+
7
+ describe '#healthy?' do
8
+ subject { service.healthy? }
9
+ context 'when the status key is missing' do
10
+ before { service.result.delete(:status) }
11
+ it 'returns false' do
12
+ expect(subject).to eq(false)
13
+ end
14
+ end
15
+ context 'when the status is UNHEALTHY' do
16
+ before { service.result[:status] = 'UNHEALTHY' }
17
+ it 'returns false' do
18
+ expect(subject).to eq(false)
19
+ end
11
20
  end
12
- it 'flags the service as unhealthy' do
13
- expect { subject }.to change(service, :healthy?).from(true).to(false)
21
+ context 'when the status is HEALTHY' do
22
+ before { service.result[:status] = 'HEALTHY' }
23
+ it 'returns true' do
24
+ expect(subject).to eq(true)
25
+ end
14
26
  end
15
27
  end
16
28
 
17
- describe '#passed_check' do
18
- subject { service.passed_check }
19
- it 'returns true' do
20
- expect(subject).to eq(true)
21
- end
22
- it 'flags the service as healthy' do
23
- expect { subject }.to change(service, :healthy?).from(false).to(true)
29
+ describe '#call' do
30
+ let(:result) { {status: 'HEALTHY'} }
31
+ subject { service.call }
32
+
33
+ before { service.stub(check: result) }
34
+
35
+ it 'receives the #call method' do
36
+ subject
37
+ expect(service).to have_received(:check).once
24
38
  end
25
- end
26
39
 
27
- describe '#params' do
28
- subject { service.params }
29
- it 'returns the params that it was constructed with' do
30
- expect(subject).to eq(params)
40
+ it 'sets the result' do
41
+ expect { subject }.to change { service.result }.to(result)
31
42
  end
32
43
  end
33
44
 
34
45
  describe '#check' do
35
46
  subject { service.check }
36
- it 'returns an empty hash' do
37
- expect(subject).to eq({})
47
+ it 'returns a status of UNHEALTHY' do
48
+ expect(subject).to include(status: 'UNHEALTHY')
38
49
  end
39
50
  end
40
51
  end
@@ -27,21 +27,5 @@ describe Wellness::Services::RedisService do
27
27
  'keyspace_misses' => '0'
28
28
  }
29
29
  }
30
- describe '#check' do
31
- subject { service.check }
32
- context 'when redis can\'t connect' do
33
- it 'fails the health check' do
34
- expect(subject).to include(status: 'UNHEALTHY')
35
- end
36
- end
37
- context 'when redis returns the its details' do
38
- let(:client) { double('Redis', info: redis_info) }
39
- before do
40
- service.stub(build_client: client)
41
- end
42
- it 'passes the health check' do
43
- expect(subject).to include(status: 'HEALTHY')
44
- end
45
- end
46
- end
30
+ pending
47
31
  end
@@ -1,100 +1,78 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Wellness::System do
4
- let(:name) { 'testing-app' }
5
- let(:system) { described_class.new(name) }
4
+ let(:system) { described_class.new('test_system') }
6
5
 
7
- class PassingMockService < Wellness::Services::Base
8
- def check
9
- passed_check
10
- {
11
- 'status' => 'HEALTHY',
12
- 'details' => {}
13
- }
14
- end
15
- end
16
-
17
- class FailingMockService < Wellness::Services::Base
18
- def check
19
- failed_check
20
- {
21
- 'status' => 'UNHEALTHY',
22
- 'details' => {}
23
- }
6
+ describe '#use' do
7
+ context 'when a Wellness::Services::Base is provided' do
8
+ subject { system.use(HealthyService, 'test_service') }
9
+ it 'adds the service to the list of services' do
10
+ expect { subject }.to change { system.services.count }.by(1)
11
+ end
24
12
  end
25
- end
26
-
27
- describe '#name' do
28
- subject { system.name }
29
- it 'equals "testing-app"' do
30
- expect(subject).to eq(name)
13
+ context 'when a Wellness::Detail is provided' do
14
+ subject { system.use(MockedDetail, 'test_detail') }
15
+ it 'adds the detail to the list of details' do
16
+ expect { subject }.to change { system.details.count }.by(1)
17
+ end
31
18
  end
32
19
  end
33
20
 
34
- describe '#add_service' do
35
- let(:service) { double('Service') }
36
- subject { system.add_service('foo', service) }
37
-
38
- it 'adds the service to the system' do
39
- expect { subject }.to change(system.services, :length).to(1)
21
+ describe '#build_report' do
22
+ subject { system.build_report }
23
+ it 'returns a Wellness::Report' do
24
+ expect(subject).to be_a(Wellness::Report)
40
25
  end
41
26
  end
42
27
 
43
- describe '#remove_service' do
44
- let(:service) { double('Service') }
45
- subject { system.remove_service('foo') }
46
- before { system.add_service('foo', service) }
47
- it 'removes the service from the system' do
48
- expect { subject }.to change(system.services, :length).to(0)
49
- end
50
- end
28
+ describe '#detailed_check' do
29
+ let(:headers) { { 'Content-Type' => 'application/json' } }
30
+ subject { system.detailed_check }
51
31
 
52
- describe '#check' do
53
- subject { system.check }
32
+ context 'when the check is HEALTHY' do
33
+ before { system.use(HealthyService, 'test_service') }
54
34
 
55
- context 'when no services are registered' do
56
- it 'returns true' do
57
- expect(subject).to be_true
35
+ it 'responds with a 200' do
36
+ expect(subject[0]).to eq(200)
58
37
  end
59
- end
60
-
61
- context 'when a passing service is registered' do
62
- before { system.add_service('passing', PassingMockService.new) }
63
-
64
- it 'returns true' do
65
- expect(subject).to be_true
38
+ it 'sets the "Content-Type" to "application/json"' do
39
+ expect(subject[1]).to eq(headers)
66
40
  end
67
41
  end
42
+ context 'when the check is UNHEALTHY' do
43
+ before { system.use(UnhealthyService, 'test_service') }
68
44
 
69
- context 'when a failing service is registered' do
70
- before { system.add_service('failing', FailingMockService.new) }
71
-
72
- it 'returns false' do
73
- expect(subject).to be_false
45
+ it 'responds with a 500' do
46
+ expect(subject[0]).to eq(500)
47
+ end
48
+ it 'sets the "Content-Type" to "application/json"' do
49
+ expect(subject[1]).to eq(headers)
74
50
  end
75
51
  end
52
+ end
76
53
 
77
- context 'when one service is failing and the other is passing' do
78
- before do
79
- system.add_service('passing', PassingMockService.new)
80
- system.add_service('failing', FailingMockService.new)
81
- end
54
+ describe '#simple_check' do
55
+ let(:headers) { { 'Content-Type' => 'application/json' } }
56
+ subject { system.simple_check }
57
+ context 'when the check is HEALTHY' do
58
+ before { system.use(HealthyService, 'test_service') }
82
59
 
83
- it 'returns false' do
84
- expect(subject).to be_false
60
+ it 'responds with a 200' do
61
+ expect(subject[0]).to eq(200)
62
+ end
63
+ it 'sets the "Content-Type" to "application/json"' do
64
+ expect(subject[1]).to eq(headers)
85
65
  end
86
66
  end
67
+ context 'when the check is UNHEALTHY' do
68
+ before { system.use(UnhealthyService, 'test_service') }
87
69
 
88
- context 'when all services are passing' do
89
- before do
90
- system.add_service('passing one', PassingMockService.new)
91
- system.add_service('passing two', PassingMockService.new)
70
+ it 'responds with a 500' do
71
+ expect(subject[0]).to eq(500)
92
72
  end
93
-
94
- it 'returns true' do
95
- expect(subject).to be_true
73
+ it 'sets the "Content-Type" to "application/json"' do
74
+ expect(subject[1]).to eq(headers)
96
75
  end
97
76
  end
98
77
  end
99
-
100
78
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wellness
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Johnston
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-30 00:00:00.000000000 Z
11
+ date: 2014-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -126,7 +126,10 @@ files:
126
126
  - README.md
127
127
  - Rakefile
128
128
  - lib/wellness.rb
129
+ - lib/wellness/detail.rb
130
+ - lib/wellness/factory.rb
129
131
  - lib/wellness/middleware.rb
132
+ - lib/wellness/report.rb
130
133
  - lib/wellness/services.rb
131
134
  - lib/wellness/services/base.rb
132
135
  - lib/wellness/services/postgres_service.rb
@@ -134,7 +137,10 @@ files:
134
137
  - lib/wellness/services/sidekiq_service.rb
135
138
  - lib/wellness/system.rb
136
139
  - spec/spec_helper.rb
140
+ - spec/support/services_support.rb
141
+ - spec/wellness/detail_spec.rb
137
142
  - spec/wellness/middleware_spec.rb
143
+ - spec/wellness/report_spec.rb
138
144
  - spec/wellness/services/base_spec.rb
139
145
  - spec/wellness/services/postgres_service_spec.rb
140
146
  - spec/wellness/services/redis_service_spec.rb
@@ -167,7 +173,10 @@ specification_version: 4
167
173
  summary: A rack middleware health check
168
174
  test_files:
169
175
  - spec/spec_helper.rb
176
+ - spec/support/services_support.rb
177
+ - spec/wellness/detail_spec.rb
170
178
  - spec/wellness/middleware_spec.rb
179
+ - spec/wellness/report_spec.rb
171
180
  - spec/wellness/services/base_spec.rb
172
181
  - spec/wellness/services/postgres_service_spec.rb
173
182
  - spec/wellness/services/redis_service_spec.rb