spec/reporter.rb in health-reporter-0.2.1 vs spec/reporter.rb in health-reporter-0.3.0

- old
+ new

@@ -16,11 +16,11 @@ subject.clear_dependencies Timecop.return reset_lambda_runner_spy end - context 'when configuring' do + describe '#self_test' do it 'remembers the self-test lambda passed to it' do test_lambda = lambda{ 'ab' == 'cd' } subject.self_test = test_lambda expect(subject.self_test).to be test_lambda end @@ -30,33 +30,39 @@ subject.self_test = test_lambda expect(subject.self_test).to be test_lambda expect(subject.healthy?).to be false expect(spy_lambda_was_run?).to eq true end + end + describe '#healthy_cache_ttl' do it 'remembers the cache ttl when healthy' do subject.healthy_cache_ttl = 10 expect(subject.healthy_cache_ttl).to eq 10 end + end + describe '#unhealthy_cache_ttl' do it 'remembers the cache ttl when not healthy' do subject.unhealthy_cache_ttl = 5 expect(subject.unhealthy_cache_ttl).to eq 5 end + end - it 'remembers when you add a dependencies' do + describe '#register_dependency' do + it 'remembers when you add a dependency' do subject.register_dependency(url: 'https://hardware-store/status', code: 123, timeout: 1) expect(subject.dependencies).to eq({ 'https://hardware-store/status' => { :code => 123, :timeout => 1 } }) end - it 'validates the urls of the dependencies during registration' do + it 'validates the urls of the dependency during registration' do expect{subject.register_dependency(url: 'no-valid-url')}.to raise_error RuntimeError, "Configured URL no-valid-url is invalid" end - it 'adds dependencies without removing the dependencies already registered' do + it 'adds dependency without removing the dependencies already registered' do subject.register_dependency(url: 'https://hardware-store/status', code: 123, timeout: 1) subject.register_dependency(url: 'https://grocery-store/status', code: 123, timeout: 1) expect(subject.dependencies).to eq({ 'https://hardware-store/status' => { :code => 123, :timeout => 1 }, 'https://grocery-store/status' => { :code => 123, :timeout => 1 } @@ -72,217 +78,391 @@ expect(subject.dependencies).to eq({ 'https://hardware-store/status' => { :code => 123, :timeout => 1 }, 'https://grocery-store/status' => { :code => 123, :timeout => 1 } }) end - - it 'clears the cache when requested' do - subject.healthy? - expect(subject.class_variable_get('@@healthy')).to be true - expect(subject.class_variable_get('@@last_check_time')).to_not be nil - subject.clear_cache - expect(subject.class_variable_get('@@healthy')).to be nil - expect(subject.class_variable_get('@@last_check_time')).to be nil - end end - context 'when exercising self-test lambda' do - it 'allows true to be returned by self-test lambda' do - test_lambda = lambda{ true } - subject.self_test = test_lambda - expect(subject.healthy?).to be true - end - it 'allows false to be returned by self-test lambda' do - test_lambda = lambda{ false } - subject.self_test = test_lambda - expect(subject.healthy?).to be false - end - - it 'raises an exception when non-boolean values are returned by self-test lambda' do - test_lambda = lambda{ "I don't feel well..." } - subject.self_test = test_lambda - expect{subject.healthy?}.to raise_error RuntimeError, "Invalid non-boolean response from registered self-check lambda: I don't feel well..." - end + def self.register_dependencies(provided_dependencies = []) + provided_dependencies.map{ |dependency| + @@dependencies[url] = dependency.delete(:url) + } end - context 'when initialized without any parameters' do - it 'sets the default self-test lambda to { true }' do - expect(subject.healthy?).to eq true - end - it 'sets the default healthy state cache ttl to 60 seconds' do - expect(subject.healthy_cache_ttl).to eq 60 + describe '#register_dependencies' do + let(:provided_test_dependencies_with_string_keys) { + [ + { + "url" => "https://cake-place/health", + "code" => 200, + "timeout" => 3 + }, + { + "url" => "https://coffee-place/status", + "code" => 200, + "timeout" => 3 + }, + { + "url" => "http://postoffice/status", + "code" => 200, + "timeout" => 3 + }, + { + "url" => "http://postoffice/status", + "code" => 200, + "timeout" => 3, + "authorization_token" => "api_token" + }, + { + "url" => "http://duplicated/status", + "code" => 200, + "timeout" => 3, + "authorization_token" => "api_token" + }, + { + "url" => "http://duplicated/status", + "code" => 200, + "timeout" => 3, + "authorization_token" => "api_token" + }, + { + "url" => "https://pizza-place/health", + "code" => 204, + "timeout" => 2, + "username" => "checker", + "password" => "checking" + } + ] + } + let(:provided_test_dependencies_with_symbol_keys) { + [ + { + url: "https://cake-place/health", + code: 200, + timeout: 3 + }, + { + url: "https://coffee-place/status", + code: 200, + timeout: 3 + }, + { + url: "http://postoffice/status", + code: 200, + timeout: 3 + }, + { + url: "http://postoffice/status", + code: 200, + timeout: 3, + authorization_token: "api_token" + }, + { + url: "http://duplicated/status", + code: 200, + timeout: 3, + authorization_token: "api_token" + }, + { + url: "http://duplicated/status", + code: 200, + timeout: 3, + authorization_token: "api_token" + }, + { + url: "https://pizza-place/health", + code: 204, + timeout: 2, + username: "checker", + password: "checking" + } + ] + } + let(:expected_dependencies) { + { + "https://cake-place/health" => { + code: 200, + timeout: 3 + }, + "https://coffee-place/status" => { + code: 200, + timeout: 3 + }, + "http://postoffice/status" => { + code: 200, + timeout: 3, + authorization_token: "api_token" + }, + "http://duplicated/status" => { + code: 200, + timeout: 3, + authorization_token: "api_token" + }, + "https://pizza-place/health" => { + code: 204, + timeout: 2, + username: "checker", + password: "checking" + } + } + } + it 'remembers when you add the dependencies with string keys' do + subject.register_dependencies(provided_test_dependencies_with_string_keys) + expect(subject.dependencies).to eq(expected_dependencies) end - - it 'sets the default unhealthy state cache ttl to 30 seconds' do - expect(subject.unhealthy_cache_ttl).to eq 30 + it 'remembers when you add the dependencies with symbol keys' do + subject.register_dependencies(provided_test_dependencies_with_symbol_keys) + expect(subject.dependencies).to eq(expected_dependencies) end + + end - context 'when calling health check for first time (no cached health state)' do - it 'calls the configured self-test lambda and returns health' do - subject.self_test = spy_lambda_returning_false - expect(subject.healthy?).to be false - expect(spy_lambda_was_run?).to eq true + describe '#clear_cache' do + it 'clears the cache when requested' do + subject.healthy? + expect(subject.class_variable_get('@@healthy')).to be true + expect(subject.class_variable_get('@@last_check_time')).to_not be nil + subject.clear_cache + expect(subject.class_variable_get('@@healthy')).to be nil + expect(subject.class_variable_get('@@last_check_time')).to be nil end end - context 'when current state is healty' do - before(:each) do - subject.self_test = spy_lambda_returning_true - subject.healthy? #force the self-test - reset_lambda_runner_spy #reset the spy so that we can see if it was run or not - end - - context 'when neither healty-cache-ttl nor unhealty-cache-ttl has expired' do - before(:each) do - subject.unhealthy_cache_ttl = 10 - subject.healthy_cache_ttl = 10 - Timecop.freeze(Time.now + 5) - end - - it 'does not call the registered self-test lambda' do - subject.healthy? #request here and test if it was run in expect below - expect(spy_lambda_was_run?).to eq false - end - it 'returns the current healthy state' do + describe '#healthy?' do + context 'when exercising self-test lambda' do + it 'allows true to be returned by self-test lambda' do + test_lambda = lambda{ true } + subject.self_test = test_lambda expect(subject.healthy?).to be true end - end - - context 'when healty-cache-ttl has expired' do - before do - subject.unhealthy_cache_ttl = 10 - subject.healthy_cache_ttl = 3 - Timecop.freeze(Time.now + 5) + + it 'allows false to be returned by self-test lambda' do + test_lambda = lambda{ false } + subject.self_test = test_lambda + expect(subject.healthy?).to be false end - - it 'calls the registered self-test lambda' do - subject.healthy? #request here and test if it was run in expect below - expect(spy_lambda_was_run?).to eq true + + it 'raises an exception when non-boolean values are returned by self-test lambda' do + test_lambda = lambda{ "I don't feel well..." } + subject.self_test = test_lambda + expect{subject.healthy?}.to raise_error RuntimeError, "Invalid non-boolean response from registered self-check lambda: I don't feel well..." end - it 'returns the current healthy state' do - expect(subject.healthy?).to be true - end end - - context 'when unhealty-cache-ttl has expired' do - before(:each) do - subject.unhealthy_cache_ttl = 3 - subject.healthy_cache_ttl = 10 - Timecop.freeze(Time.now + 5) + + context 'when initialized without any parameters' do + it 'sets the default self-test lambda to { true }' do + expect(subject.healthy?).to eq true end - - it 'does not call the registered self-test lambda' do - subject.healthy? #request here and test if it was run in expect below - expect(spy_lambda_was_run?).to eq false + + it 'sets the default healthy state cache ttl to 60 seconds' do + expect(subject.healthy_cache_ttl).to eq 60 end - it 'returns the current healthy state' do - expect(subject.healthy?).to be true + + it 'sets the default unhealthy state cache ttl to 30 seconds' do + expect(subject.unhealthy_cache_ttl).to eq 30 end end - end - - context 'when current state is unhealty' do - before(:each) do - subject.self_test = spy_lambda_returning_false - subject.healthy? #force the self-test - reset_lambda_runner_spy #reset the spy so that we can see if it was run or not - end - - context 'when neither healty-cache-ttl nor unhealty-cache-ttl has expired' do - before(:each) do - subject.unhealthy_cache_ttl = 10 - subject.healthy_cache_ttl = 10 - Timecop.freeze(Time.now + 5) - end - - it 'does not call the registered self-test lambda' do - subject.healthy? #request here and test if it was run in expect below - expect(spy_lambda_was_run?).to eq false - end - it 'returns the current unhealthy state' do + + context 'when calling health check for first time (no cached health state)' do + it 'calls the configured self-test lambda and returns health' do + subject.self_test = spy_lambda_returning_false expect(subject.healthy?).to be false + expect(spy_lambda_was_run?).to eq true end end - - context 'when healty-cache-ttl has expired' do + + context 'when current state is healty' do before(:each) do - subject.unhealthy_cache_ttl = 10 - subject.healthy_cache_ttl = 3 - Timecop.freeze(Time.now + 5) + subject.self_test = spy_lambda_returning_true + subject.healthy? #force the self-test + reset_lambda_runner_spy #reset the spy so that we can see if it was run or not end - - it 'does not call the registered self-test lambda' do - subject.healthy? #request here and test if it was run in expect below - expect(spy_lambda_was_run?).to eq false + + context 'when neither healty-cache-ttl nor unhealty-cache-ttl has expired' do + before(:each) do + subject.unhealthy_cache_ttl = 10 + subject.healthy_cache_ttl = 10 + Timecop.freeze(Time.now + 5) + end + + it 'does not call the registered self-test lambda' do + subject.healthy? #request here and test if it was run in expect below + expect(spy_lambda_was_run?).to eq false + end + it 'returns the current healthy state' do + expect(subject.healthy?).to be true + end end - it 'returns the current unhealthy state' do - expect(subject.healthy?).to be false + + context 'when healty-cache-ttl has expired' do + before do + subject.unhealthy_cache_ttl = 10 + subject.healthy_cache_ttl = 3 + Timecop.freeze(Time.now + 5) + end + + it 'calls the registered self-test lambda' do + subject.healthy? #request here and test if it was run in expect below + expect(spy_lambda_was_run?).to eq true + end + it 'returns the current healthy state' do + expect(subject.healthy?).to be true + end end + + context 'when unhealty-cache-ttl has expired' do + before(:each) do + subject.unhealthy_cache_ttl = 3 + subject.healthy_cache_ttl = 10 + Timecop.freeze(Time.now + 5) + end + + it 'does not call the registered self-test lambda' do + subject.healthy? #request here and test if it was run in expect below + expect(spy_lambda_was_run?).to eq false + end + it 'returns the current healthy state' do + expect(subject.healthy?).to be true + end + end end - - context 'when unhealty-cache-ttl has expired' do + + context 'when current state is unhealty' do before(:each) do - subject.unhealthy_cache_ttl = 3 - subject.healthy_cache_ttl = 10 - Timecop.freeze(Time.now + 5) + subject.self_test = spy_lambda_returning_false + subject.healthy? #force the self-test + reset_lambda_runner_spy #reset the spy so that we can see if it was run or not end - - it 'calls the registered self-test lambda' do - subject.healthy? #request here and test if it was run in expect below - expect(spy_lambda_was_run?).to eq true + + context 'when neither healty-cache-ttl nor unhealty-cache-ttl has expired' do + before(:each) do + subject.unhealthy_cache_ttl = 10 + subject.healthy_cache_ttl = 10 + Timecop.freeze(Time.now + 5) + end + + it 'does not call the registered self-test lambda' do + subject.healthy? #request here and test if it was run in expect below + expect(spy_lambda_was_run?).to eq false + end + it 'returns the current unhealthy state' do + expect(subject.healthy?).to be false + end end - it 'returns the current unhealthy state' do - expect(subject.healthy?).to be false + + context 'when healty-cache-ttl has expired' do + before(:each) do + subject.unhealthy_cache_ttl = 10 + subject.healthy_cache_ttl = 3 + Timecop.freeze(Time.now + 5) + end + + it 'does not call the registered self-test lambda' do + subject.healthy? #request here and test if it was run in expect below + expect(spy_lambda_was_run?).to eq false + end + it 'returns the current unhealthy state' do + expect(subject.healthy?).to be false + end end - end - end - - context 'when checking dependencies' do - context 'when there are no dependencies registered' do - it 'only performs the self-test' do - subject.self_test = spy_lambda_returning_false - expect(subject.healthy?).to be false - expect(spy_lambda_was_run?).to eq true + + context 'when unhealty-cache-ttl has expired' do + before(:each) do + subject.unhealthy_cache_ttl = 3 + subject.healthy_cache_ttl = 10 + Timecop.freeze(Time.now + 5) + end + + it 'calls the registered self-test lambda' do + subject.healthy? #request here and test if it was run in expect below + expect(spy_lambda_was_run?).to eq true + end + it 'returns the current unhealthy state' do + expect(subject.healthy?).to be false + end end end - - context 'when there are multiple dependencies registered' do - before(:each) do - subject.register_dependency(url: 'https://hardware-store/status') - subject.register_dependency(url: 'https://grocery-store/status') - subject.self_test = spy_lambda_returning_true + + context 'when checking dependencies' do + context 'when there are no dependencies registered' do + it 'only performs the self-test' do + subject.self_test = spy_lambda_returning_false + expect(subject.healthy?).to be false + expect(spy_lambda_was_run?).to eq true + end end - - it 'performs the self-test and checks all dependencies' do - stub_request(:get, "https://hardware-store/status").to_return(:status => 200, :body => "", :headers => {}) - stub_request(:get, "https://grocery-store/status").to_return(:status => 200, :body => "", :headers => {}) - expect(subject.healthy?).to be true - expect(spy_lambda_was_run?).to eq true + + context 'when there are multiple dependencies registered' do + before(:each) do + subject.register_dependency(url: 'https://hardware-store/status') + subject.register_dependency(url: 'https://grocery-store/status') + subject.self_test = spy_lambda_returning_true + end + + it 'performs the self-test and checks all dependencies' do + stub_request(:get, "https://hardware-store/status").to_return(:status => 200, :body => "", :headers => {}) + stub_request(:get, "https://grocery-store/status").to_return(:status => 200, :body => "", :headers => {}) + expect(subject.healthy?).to be true + expect(spy_lambda_was_run?).to eq true + end + + it 'indicates healthy if all of the dependencies are healthy' do + stub_request(:get, "https://hardware-store/status").to_return(:status => 200, :body => "", :headers => {}) + stub_request(:get, "https://grocery-store/status").to_return(:status => 200, :body => "", :headers => {}) + expect(subject.healthy?).to be true + expect(spy_lambda_was_run?).to eq true + end + + it 'raises a detailed exception indicating why a dependency was determined to be unhealthy state was uncached' do + stub_request(:get, "https://hardware-store/status").to_return(:status => 500, :body => "", :headers => {}) + stub_request(:get, "https://grocery-store/status").to_return(:status => 200, :body => "", :headers => {}) + expect{subject.healthy?}.to raise_error RuntimeError, "Dependency <https://hardware-store/status> failed check due to RuntimeError: Response expected to be 200 but is 500" + end + + it 'indicates cached unhealthy state if it is unhealthy because a dependency was unhealthy' do + stub_request(:get, "https://hardware-store/status").to_return(:status => 500, :body => "", :headers => {}) + stub_request(:get, "https://grocery-store/status").to_return(:status => 200, :body => "", :headers => {}) + expect{subject.healthy?}.to raise_error RuntimeError, "Dependency <https://hardware-store/status> failed check due to RuntimeError: Response expected to be 200 but is 500" + reset_lambda_runner_spy + expect(subject.healthy?).to be false + expect(spy_lambda_was_run?).to eq false + end end - it 'indicates healthy if all of the dependencies are healthy' do - stub_request(:get, "https://hardware-store/status").to_return(:status => 200, :body => "", :headers => {}) - stub_request(:get, "https://grocery-store/status").to_return(:status => 200, :body => "", :headers => {}) - expect(subject.healthy?).to be true - expect(spy_lambda_was_run?).to eq true - end + context 'when a dependency requires basic auth' do + let(:test_dependencies_with_basic_auth) { + [ + { + url: "https://pizza-place/health", + code: 204, + timeout: 2, + username: "check_user", + password: "check_password" + } + ] + } - it 'raises a detailed exception indicating why a dependency was determined to be unhealthy state was uncached' do - stub_request(:get, "https://hardware-store/status").to_return(:status => 500, :body => "", :headers => {}) - stub_request(:get, "https://grocery-store/status").to_return(:status => 200, :body => "", :headers => {}) - expect{subject.healthy?}.to raise_error RuntimeError, "Dependency <https://hardware-store/status> failed check due to RuntimeError: Response expected to be 200 but is 500" - end + before(:each) do + subject.register_dependencies(test_dependencies_with_basic_auth) + end - it 'indicates cached unhealthy state if it is unhealthy because a dependency was unhealthy' do - stub_request(:get, "https://hardware-store/status").to_return(:status => 500, :body => "", :headers => {}) - stub_request(:get, "https://grocery-store/status").to_return(:status => 200, :body => "", :headers => {}) - expect{subject.healthy?}.to raise_error RuntimeError, "Dependency <https://hardware-store/status> failed check due to RuntimeError: Response expected to be 200 but is 500" - reset_lambda_runner_spy - expect(subject.healthy?).to be false - expect(spy_lambda_was_run?).to eq false + it 'reaches out to the dependency using a basic auth and dependency have good health' do + stub_request(:get, "https://pizza-place/health"). + with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'Basic Y2hlY2tfdXNlcjpjaGVja19wYXNzd29yZA==', 'User-Agent'=>'Faraday v0.15.2'}). + to_return(:status => 204, :body => "", :headers => {}) + expect(subject.healthy?).to be true + end + + it 'reaches out to the dependency using a basic auth and dependency does not have good health' do + stub_request(:get, "https://pizza-place/health"). + with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'Basic Y2hlY2tfdXNlcjpjaGVja19wYXNzd29yZA==', 'User-Agent'=>'Faraday v0.15.2'}). + to_return(:status => 500, :body => "", :headers => {}) + expect{subject.healthy?}.to raise_error RuntimeError, "Dependency <https://pizza-place/health> failed check due to RuntimeError: Response expected to be 204 but is 500" + expect(subject.healthy?).to be false + end end end end end