wellness 0.2.3 → 1.0.0

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