lhc 12.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 +7 -0
- data/.gitignore +37 -0
- data/.rubocop.localch.yml +325 -0
- data/.rubocop.yml +61 -0
- data/.ruby-version +1 -0
- data/Gemfile +13 -0
- data/Gemfile.activesupport4 +4 -0
- data/Gemfile.activesupport5 +4 -0
- data/Gemfile.activesupport6 +4 -0
- data/LICENSE +674 -0
- data/README.md +984 -0
- data/Rakefile +25 -0
- data/cider-ci.yml +6 -0
- data/cider-ci/bin/bundle +51 -0
- data/cider-ci/bin/ruby_install +8 -0
- data/cider-ci/bin/ruby_version +25 -0
- data/cider-ci/jobs/rspec-activesupport-4.yml +28 -0
- data/cider-ci/jobs/rspec-activesupport-5.yml +27 -0
- data/cider-ci/jobs/rspec-activesupport-6.yml +28 -0
- data/cider-ci/jobs/rubocop.yml +18 -0
- data/cider-ci/task_components/bundle.yml +22 -0
- data/cider-ci/task_components/rspec.yml +36 -0
- data/cider-ci/task_components/rubocop.yml +29 -0
- data/cider-ci/task_components/ruby.yml +15 -0
- data/friday.yml +3 -0
- data/lhc.gemspec +39 -0
- data/lib/core_ext/hash/deep_transform_values.rb +48 -0
- data/lib/lhc.rb +136 -0
- data/lib/lhc/concerns/lhc/basic_methods_concern.rb +42 -0
- data/lib/lhc/concerns/lhc/configuration_concern.rb +20 -0
- data/lib/lhc/concerns/lhc/fix_invalid_encoding_concern.rb +42 -0
- data/lib/lhc/concerns/lhc/formats_concern.rb +25 -0
- data/lib/lhc/concerns/lhc/request/user_agent_concern.rb +25 -0
- data/lib/lhc/config.rb +47 -0
- data/lib/lhc/endpoint.rb +119 -0
- data/lib/lhc/error.rb +80 -0
- data/lib/lhc/errors/client_error.rb +73 -0
- data/lib/lhc/errors/parser_error.rb +4 -0
- data/lib/lhc/errors/server_error.rb +28 -0
- data/lib/lhc/errors/timeout.rb +4 -0
- data/lib/lhc/errors/unknown_error.rb +4 -0
- data/lib/lhc/format.rb +18 -0
- data/lib/lhc/formats.rb +8 -0
- data/lib/lhc/formats/form.rb +45 -0
- data/lib/lhc/formats/json.rb +55 -0
- data/lib/lhc/formats/multipart.rb +45 -0
- data/lib/lhc/formats/plain.rb +42 -0
- data/lib/lhc/interceptor.rb +32 -0
- data/lib/lhc/interceptors.rb +26 -0
- data/lib/lhc/interceptors/auth.rb +98 -0
- data/lib/lhc/interceptors/caching.rb +127 -0
- data/lib/lhc/interceptors/default_timeout.rb +16 -0
- data/lib/lhc/interceptors/logging.rb +37 -0
- data/lib/lhc/interceptors/monitoring.rb +63 -0
- data/lib/lhc/interceptors/prometheus.rb +51 -0
- data/lib/lhc/interceptors/retry.rb +41 -0
- data/lib/lhc/interceptors/rollbar.rb +36 -0
- data/lib/lhc/interceptors/throttle.rb +81 -0
- data/lib/lhc/interceptors/zipkin.rb +110 -0
- data/lib/lhc/railtie.rb +10 -0
- data/lib/lhc/request.rb +157 -0
- data/lib/lhc/response.rb +60 -0
- data/lib/lhc/response/data.rb +28 -0
- data/lib/lhc/response/data/base.rb +22 -0
- data/lib/lhc/response/data/collection.rb +16 -0
- data/lib/lhc/response/data/item.rb +29 -0
- data/lib/lhc/rspec.rb +12 -0
- data/lib/lhc/test/cache_helper.rb +3 -0
- data/lib/lhc/version.rb +5 -0
- data/script/ci/build.sh +19 -0
- data/spec/basic_methods/delete_spec.rb +34 -0
- data/spec/basic_methods/get_spec.rb +49 -0
- data/spec/basic_methods/post_spec.rb +42 -0
- data/spec/basic_methods/put_spec.rb +48 -0
- data/spec/basic_methods/request_spec.rb +19 -0
- data/spec/basic_methods/request_without_rails_spec.rb +29 -0
- data/spec/config/endpoints_spec.rb +63 -0
- data/spec/config/placeholders_spec.rb +32 -0
- data/spec/core_ext/hash/deep_transform_values_spec.rb +24 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +8 -0
- data/spec/dummy/app/assets/config/manifest.js +3 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +7 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +4 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +5 -0
- data/spec/dummy/bin/rails +6 -0
- data/spec/dummy/bin/rake +6 -0
- data/spec/dummy/config.ru +6 -0
- data/spec/dummy/config/application.rb +16 -0
- data/spec/dummy/config/boot.rb +7 -0
- data/spec/dummy/config/environment.rb +7 -0
- data/spec/dummy/config/environments/development.rb +36 -0
- data/spec/dummy/config/environments/production.rb +77 -0
- data/spec/dummy/config/environments/test.rb +41 -0
- data/spec/dummy/config/initializers/assets.rb +10 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
- data/spec/dummy/config/initializers/inflections.rb +18 -0
- data/spec/dummy/config/initializers/mime_types.rb +6 -0
- data/spec/dummy/config/initializers/session_store.rb +5 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +11 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/endpoint/compile_spec.rb +35 -0
- data/spec/endpoint/match_spec.rb +41 -0
- data/spec/endpoint/placeholders_spec.rb +30 -0
- data/spec/endpoint/remove_interpolated_params_spec.rb +17 -0
- data/spec/endpoint/values_as_params_spec.rb +31 -0
- data/spec/error/dup_spec.rb +12 -0
- data/spec/error/find_spec.rb +57 -0
- data/spec/error/response_spec.rb +17 -0
- data/spec/error/timeout_spec.rb +14 -0
- data/spec/error/to_s_spec.rb +80 -0
- data/spec/formats/form_spec.rb +27 -0
- data/spec/formats/json_spec.rb +66 -0
- data/spec/formats/multipart_spec.rb +26 -0
- data/spec/formats/plain_spec.rb +29 -0
- data/spec/interceptors/after_request_spec.rb +20 -0
- data/spec/interceptors/after_response_spec.rb +39 -0
- data/spec/interceptors/auth/basic_auth_spec.rb +17 -0
- data/spec/interceptors/auth/bearer_spec.rb +19 -0
- data/spec/interceptors/auth/reauthentication_configuration_spec.rb +61 -0
- data/spec/interceptors/auth/reauthentication_spec.rb +44 -0
- data/spec/interceptors/before_request_spec.rb +21 -0
- data/spec/interceptors/before_response_spec.rb +20 -0
- data/spec/interceptors/caching/hydra_spec.rb +26 -0
- data/spec/interceptors/caching/main_spec.rb +73 -0
- data/spec/interceptors/caching/methods_spec.rb +42 -0
- data/spec/interceptors/caching/options_spec.rb +89 -0
- data/spec/interceptors/caching/parameters_spec.rb +24 -0
- data/spec/interceptors/caching/response_status_spec.rb +29 -0
- data/spec/interceptors/caching/to_cache_spec.rb +16 -0
- data/spec/interceptors/default_interceptors_spec.rb +15 -0
- data/spec/interceptors/default_timeout/main_spec.rb +34 -0
- data/spec/interceptors/define_spec.rb +29 -0
- data/spec/interceptors/dup_spec.rb +19 -0
- data/spec/interceptors/logging/main_spec.rb +37 -0
- data/spec/interceptors/monitoring/main_spec.rb +97 -0
- data/spec/interceptors/prometheus_spec.rb +54 -0
- data/spec/interceptors/response_competition_spec.rb +41 -0
- data/spec/interceptors/retry/main_spec.rb +73 -0
- data/spec/interceptors/return_response_spec.rb +38 -0
- data/spec/interceptors/rollbar/invalid_encoding_spec.rb +43 -0
- data/spec/interceptors/rollbar/main_spec.rb +57 -0
- data/spec/interceptors/throttle/main_spec.rb +106 -0
- data/spec/interceptors/throttle/reset_track_spec.rb +53 -0
- data/spec/interceptors/zipkin/distributed_tracing_spec.rb +135 -0
- data/spec/rails_helper.rb +6 -0
- data/spec/request/body_spec.rb +39 -0
- data/spec/request/encoding_spec.rb +37 -0
- data/spec/request/error_handling_spec.rb +88 -0
- data/spec/request/headers_spec.rb +12 -0
- data/spec/request/ignore_errors_spec.rb +73 -0
- data/spec/request/option_dup_spec.rb +13 -0
- data/spec/request/parallel_requests_spec.rb +59 -0
- data/spec/request/params_encoding_spec.rb +26 -0
- data/spec/request/request_without_rails_spec.rb +15 -0
- data/spec/request/url_patterns_spec.rb +54 -0
- data/spec/request/user_agent_spec.rb +26 -0
- data/spec/request/user_agent_without_rails_spec.rb +27 -0
- data/spec/response/body_spec.rb +16 -0
- data/spec/response/code_spec.rb +16 -0
- data/spec/response/data_accessor_spec.rb +29 -0
- data/spec/response/data_spec.rb +61 -0
- data/spec/response/effective_url_spec.rb +16 -0
- data/spec/response/headers_spec.rb +18 -0
- data/spec/response/options_spec.rb +18 -0
- data/spec/response/success_spec.rb +13 -0
- data/spec/response/time_spec.rb +21 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/fixtures/json/feedback.json +11 -0
- data/spec/support/fixtures/json/feedbacks.json +164 -0
- data/spec/support/fixtures/json/localina_content_ad.json +23 -0
- data/spec/support/load_json.rb +5 -0
- data/spec/support/reset_config.rb +7 -0
- data/spec/support/zipkin_mock.rb +113 -0
- data/spec/timeouts/no_signal_spec.rb +13 -0
- data/spec/timeouts/timings_spec.rb +55 -0
- metadata +534 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails_helper'
|
|
4
|
+
|
|
5
|
+
describe LHC do
|
|
6
|
+
context 'interceptor response competition' do
|
|
7
|
+
before(:each) do
|
|
8
|
+
# rubocop:disable Style/ClassVars
|
|
9
|
+
class LocalCacheInterceptor < LHC::Interceptor
|
|
10
|
+
@@cached = false
|
|
11
|
+
cattr_accessor :cached
|
|
12
|
+
|
|
13
|
+
def before_request
|
|
14
|
+
if @@cached
|
|
15
|
+
return LHC::Response.new(Typhoeus::Response.new(response_body: 'Im served from local cache'), nil)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
# rubocop:enable Style/ClassVars
|
|
20
|
+
|
|
21
|
+
class RemoteCacheInterceptor < LHC::Interceptor
|
|
22
|
+
|
|
23
|
+
def before_request
|
|
24
|
+
if request.response.nil?
|
|
25
|
+
return LHC::Response.new(Typhoeus::Response.new(response_body: 'Im served from remote cache'), nil)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
LHC.configure { |c| c.interceptors = [LocalCacheInterceptor, RemoteCacheInterceptor] }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'can handle multiple interceptors that compete for returning the response' do
|
|
34
|
+
response = LHC.get('http://local.ch')
|
|
35
|
+
expect(response.body).to eq 'Im served from remote cache'
|
|
36
|
+
LocalCacheInterceptor.cached = true
|
|
37
|
+
response = LHC.get('http://local.ch')
|
|
38
|
+
expect(response.body).to eq 'Im served from local cache'
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails_helper'
|
|
4
|
+
|
|
5
|
+
describe LHC::Rollbar do
|
|
6
|
+
before(:each) do
|
|
7
|
+
LHC.config.interceptors = [LHC::Retry]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
let(:status) { 500 }
|
|
11
|
+
|
|
12
|
+
let(:request_stub) do
|
|
13
|
+
@retry_count = 0
|
|
14
|
+
stub_request(:get, 'http://local.ch').to_return do |_|
|
|
15
|
+
if @retry_count == max_retry_count
|
|
16
|
+
{ status: 200 }
|
|
17
|
+
else
|
|
18
|
+
@retry_count += 1
|
|
19
|
+
{ status: status }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
let(:max_retry_count) { 3 }
|
|
25
|
+
|
|
26
|
+
it 'retries a request up to 3 times (default)' do
|
|
27
|
+
request_stub
|
|
28
|
+
response = LHC.get('http://local.ch', retry: true)
|
|
29
|
+
expect(response.success?).to eq true
|
|
30
|
+
expect(response.code).to eq 200
|
|
31
|
+
expect(request_stub).to have_been_requested.times(4)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context 'retry only once' do
|
|
35
|
+
let(:retry_options) { { max: 1 } }
|
|
36
|
+
let(:max_retry_count) { 1 }
|
|
37
|
+
|
|
38
|
+
it 'retries only once' do
|
|
39
|
+
request_stub
|
|
40
|
+
response = LHC.get('http://local.ch', retry: { max: 1 })
|
|
41
|
+
expect(response.success?).to eq true
|
|
42
|
+
expect(response.code).to eq 200
|
|
43
|
+
expect(request_stub).to have_been_requested.times(2)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context 'retry all' do
|
|
48
|
+
let(:max_retry_count) { 2 }
|
|
49
|
+
|
|
50
|
+
before do
|
|
51
|
+
expect(LHC::Retry).to receive(:max).at_least(:once).and_return(2)
|
|
52
|
+
expect(LHC::Retry).to receive(:all).at_least(:once).and_return(true)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'retries if configured globally' do
|
|
56
|
+
request_stub
|
|
57
|
+
response = LHC.get('http://local.ch')
|
|
58
|
+
expect(response.success?).to eq true
|
|
59
|
+
expect(response.code).to eq 200
|
|
60
|
+
expect(request_stub).to have_been_requested.times(3)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context 'ignore error' do
|
|
65
|
+
let(:status) { 404 }
|
|
66
|
+
|
|
67
|
+
it 'does not retry if the error is explicitly ignored' do
|
|
68
|
+
request_stub
|
|
69
|
+
LHC.get('http://local.ch', retry: { max: 1 }, ignore: [LHC::NotFound])
|
|
70
|
+
expect(request_stub).to have_been_requested.times(1)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails_helper'
|
|
4
|
+
|
|
5
|
+
describe LHC do
|
|
6
|
+
context 'interceptor' do
|
|
7
|
+
before(:each) do
|
|
8
|
+
class CacheInterceptor < LHC::Interceptor
|
|
9
|
+
|
|
10
|
+
def before_request
|
|
11
|
+
LHC::Response.new(Typhoeus::Response.new(response_body: 'Im served from cache'), nil)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
LHC.configure { |c| c.interceptors = [CacheInterceptor] }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'can return a response rather then doing a real request' do
|
|
18
|
+
response = LHC.get('http://local.ch')
|
|
19
|
+
expect(response.body).to eq 'Im served from cache'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context 'misusage' do
|
|
23
|
+
before(:each) do
|
|
24
|
+
class AnotherInterceptor < LHC::Interceptor
|
|
25
|
+
def before_request
|
|
26
|
+
LHC::Response.new(Typhoeus::Response.new({}), nil)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'raises an exception when two interceptors try to return a response' do
|
|
32
|
+
expect(lambda {
|
|
33
|
+
LHC.get('http://local.ch', interceptors: [CacheInterceptor, AnotherInterceptor])
|
|
34
|
+
}).to raise_error 'Response already set from another interceptor'
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails_helper'
|
|
4
|
+
|
|
5
|
+
describe LHC::Rollbar do
|
|
6
|
+
context 'invalid encoding in rollbar payload' do
|
|
7
|
+
before(:each) do
|
|
8
|
+
LHC.config.interceptors = [LHC::Rollbar]
|
|
9
|
+
stub_request(:get, 'http://local.ch').to_return(status: 400)
|
|
10
|
+
|
|
11
|
+
allow(described_class).to receive(:fix_invalid_encoding).and_call_original
|
|
12
|
+
# a stub that will throw a error on first call and suceed on subsequent calls
|
|
13
|
+
call_counter = 0
|
|
14
|
+
class Rollbar; end
|
|
15
|
+
::Rollbar.stub(:warning) do
|
|
16
|
+
call_counter += 1
|
|
17
|
+
raise Encoding::UndefinedConversionError if call_counter == 1
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# the response for the caller is still LHC::BadRequest
|
|
21
|
+
expect(-> { LHC.get('http://local.ch', rollbar: { additional: invalid }) }).to raise_error LHC::BadRequest
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
let(:invalid) { (+"in\xc3lid").force_encoding('ASCII-8BIT') }
|
|
25
|
+
let(:valid) { described_class.fix_invalid_encoding(invalid) }
|
|
26
|
+
|
|
27
|
+
it 'calls fix_invalid_encoding incase a Encoding::UndefinedConversionError was encountered' do
|
|
28
|
+
expect(described_class).to have_received(:fix_invalid_encoding).with(invalid)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'calls Rollbar.warn with the fixed data' do
|
|
32
|
+
expect(::Rollbar).to have_received(:warning)
|
|
33
|
+
.with(
|
|
34
|
+
'Status: 400 URL: http://local.ch',
|
|
35
|
+
hash_including(
|
|
36
|
+
response: anything,
|
|
37
|
+
request: anything,
|
|
38
|
+
additional: valid
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails_helper'
|
|
4
|
+
|
|
5
|
+
describe LHC::Rollbar do
|
|
6
|
+
before(:each) do
|
|
7
|
+
LHC.config.interceptors = [LHC::Rollbar]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
context 'Rollbar is undefined' do
|
|
11
|
+
before(:each) do
|
|
12
|
+
Object.send(:remove_const, 'Rollbar') if Object.const_defined?('Rollbar')
|
|
13
|
+
end
|
|
14
|
+
it 'does not report' do
|
|
15
|
+
stub_request(:get, 'http://local.ch').to_return(status: 400)
|
|
16
|
+
expect(-> { LHC.get('http://local.ch') })
|
|
17
|
+
.to raise_error LHC::BadRequest
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context 'Rollbar is defined' do
|
|
22
|
+
before(:each) do
|
|
23
|
+
class Rollbar; end
|
|
24
|
+
::Rollbar.stub(:warning)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'does report errors to rollbar' do
|
|
28
|
+
stub_request(:get, 'http://local.ch').to_return(status: 400)
|
|
29
|
+
expect(-> { LHC.get('http://local.ch') })
|
|
30
|
+
.to raise_error LHC::BadRequest
|
|
31
|
+
expect(::Rollbar).to have_received(:warning)
|
|
32
|
+
.with(
|
|
33
|
+
'Status: 400 URL: http://local.ch',
|
|
34
|
+
response: hash_including(body: anything, code: anything, headers: anything, time: anything, timeout?: anything),
|
|
35
|
+
request: hash_including(url: anything, method: anything, headers: anything, params: anything)
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context 'additional params' do
|
|
40
|
+
it 'does report errors to rollbar with additional data' do
|
|
41
|
+
stub_request(:get, 'http://local.ch')
|
|
42
|
+
.to_return(status: 400)
|
|
43
|
+
expect(-> { LHC.get('http://local.ch', rollbar: { additional: 'data' }) })
|
|
44
|
+
.to raise_error LHC::BadRequest
|
|
45
|
+
expect(::Rollbar).to have_received(:warning)
|
|
46
|
+
.with(
|
|
47
|
+
'Status: 400 URL: http://local.ch',
|
|
48
|
+
hash_including(
|
|
49
|
+
response: anything,
|
|
50
|
+
request: anything,
|
|
51
|
+
additional: 'data'
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails_helper'
|
|
4
|
+
|
|
5
|
+
describe LHC::Throttle do
|
|
6
|
+
let(:provider) { 'local.ch' }
|
|
7
|
+
let(:limit) { 10000 }
|
|
8
|
+
let(:remaining) { 1900 }
|
|
9
|
+
let(:options) do
|
|
10
|
+
{
|
|
11
|
+
throttle: {
|
|
12
|
+
provider: provider,
|
|
13
|
+
track: true,
|
|
14
|
+
limit: limit_options,
|
|
15
|
+
remaining: { header: 'Rate-Limit-Remaining' },
|
|
16
|
+
expires: { header: 'Rate-Limit-Reset' },
|
|
17
|
+
break: break_option
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
let(:limit_options) { { header: 'Rate-Limit-Limit' } }
|
|
22
|
+
let(:break_option) { false }
|
|
23
|
+
let(:expires_in) { (Time.zone.now + 1.hour).to_i }
|
|
24
|
+
|
|
25
|
+
before(:each) do
|
|
26
|
+
LHC::Throttle.track = nil
|
|
27
|
+
LHC.config.interceptors = [LHC::Throttle]
|
|
28
|
+
|
|
29
|
+
stub_request(:get, 'http://local.ch')
|
|
30
|
+
.to_return(
|
|
31
|
+
headers: {
|
|
32
|
+
'Rate-Limit-Limit' => limit,
|
|
33
|
+
'Rate-Limit-Remaining' => remaining,
|
|
34
|
+
'Rate-Limit-Reset' => expires_in
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'tracks the request limits based on response data' do
|
|
40
|
+
LHC.get('http://local.ch', options)
|
|
41
|
+
expect(LHC::Throttle.track[provider][:limit]).to eq limit
|
|
42
|
+
expect(LHC::Throttle.track[provider][:remaining]).to eq remaining
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context 'fix predefined integer for limit' do
|
|
46
|
+
let(:limit_options) { 1000 }
|
|
47
|
+
|
|
48
|
+
it 'tracks the limit based on initialy provided data' do
|
|
49
|
+
LHC.get('http://local.ch', options)
|
|
50
|
+
expect(LHC::Throttle.track[provider][:limit]).to eq limit_options
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context 'breaks' do
|
|
55
|
+
let(:break_option) { '80%' }
|
|
56
|
+
|
|
57
|
+
it 'hit the breaks if throttling quota is reached' do
|
|
58
|
+
LHC.get('http://local.ch', options)
|
|
59
|
+
expect(-> {
|
|
60
|
+
LHC.get('http://local.ch', options)
|
|
61
|
+
}).to raise_error(LHC::Throttle::OutOfQuota, 'Reached predefined quota for local.ch')
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context 'still within quota' do
|
|
65
|
+
let(:break_option) { '90%' }
|
|
66
|
+
|
|
67
|
+
it 'does not hit the breaks' do
|
|
68
|
+
LHC.get('http://local.ch', options)
|
|
69
|
+
LHC.get('http://local.ch', options)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
context 'no response headers' do
|
|
75
|
+
before do
|
|
76
|
+
stub_request(:get, 'http://local.ch')
|
|
77
|
+
.to_return(status: 200)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'does not raise an exception' do
|
|
81
|
+
LHC.get('http://local.ch', options)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
context 'no remaining tracked, but break enabled' do
|
|
85
|
+
let(:break_option) { '90%' }
|
|
86
|
+
|
|
87
|
+
it 'does not fail if a remaining was not tracked yet' do
|
|
88
|
+
LHC.get('http://local.ch', options)
|
|
89
|
+
LHC.get('http://local.ch', options)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
context 'expires' do
|
|
95
|
+
let(:break_option) { '80%' }
|
|
96
|
+
|
|
97
|
+
it 'attempts another request if the quota expired' do
|
|
98
|
+
LHC.get('http://local.ch', options)
|
|
99
|
+
expect(-> {
|
|
100
|
+
LHC.get('http://local.ch', options)
|
|
101
|
+
}).to raise_error(LHC::Throttle::OutOfQuota, 'Reached predefined quota for local.ch')
|
|
102
|
+
Timecop.travel(Time.zone.now + 2.hours)
|
|
103
|
+
LHC.get('http://local.ch', options)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails_helper'
|
|
4
|
+
|
|
5
|
+
describe LHC::Throttle do
|
|
6
|
+
let(:provider) { 'local.ch' }
|
|
7
|
+
let(:limit) { 10000 }
|
|
8
|
+
let(:remaining) { 1900 }
|
|
9
|
+
let(:options) do
|
|
10
|
+
{
|
|
11
|
+
throttle: {
|
|
12
|
+
provider: provider,
|
|
13
|
+
track: true,
|
|
14
|
+
limit: limit_options,
|
|
15
|
+
remaining: { header: 'Rate-Limit-Remaining' },
|
|
16
|
+
expires: { header: 'Rate-Limit-Reset' },
|
|
17
|
+
break: '80%'
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
let(:limit_options) { { header: 'Rate-Limit-Limit' } }
|
|
22
|
+
let(:break_option) { false }
|
|
23
|
+
let(:expires_in) { (Time.zone.now + 1.hour).to_i }
|
|
24
|
+
|
|
25
|
+
before(:each) do
|
|
26
|
+
LHC::Throttle.track = nil
|
|
27
|
+
LHC.config.interceptors = [LHC::Throttle]
|
|
28
|
+
|
|
29
|
+
stub_request(:get, 'http://local.ch')
|
|
30
|
+
.to_return(
|
|
31
|
+
headers: {
|
|
32
|
+
'Rate-Limit-Limit' => limit,
|
|
33
|
+
'Rate-Limit-Remaining' => remaining,
|
|
34
|
+
'Rate-Limit-Reset' => expires_in
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# If LHC::Trottle.track would be kept accross multiple tests,
|
|
40
|
+
# at least 2/3 of the following would fail
|
|
41
|
+
|
|
42
|
+
it 'resets track accross multiple tests 1/3' do
|
|
43
|
+
LHC.get('http://local.ch', options)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'resets track accross multiple tests 2/3' do
|
|
47
|
+
LHC.get('http://local.ch', options)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'resets track accross multiple tests 3/3' do
|
|
51
|
+
LHC.get('http://local.ch', options)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails_helper'
|
|
4
|
+
|
|
5
|
+
describe LHC::Zipkin do
|
|
6
|
+
before(:each) do
|
|
7
|
+
LHC.config.interceptors = [described_class]
|
|
8
|
+
LHC.config.endpoint(:local, 'http://local.ch')
|
|
9
|
+
stub_request(:get, 'http://local.ch').to_return(status: 200, body: 'The Website')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
context 'with zipkin tracer integration' do
|
|
13
|
+
before(:all) do
|
|
14
|
+
::ZipkinTracer::TraceContainer.setup_mock(
|
|
15
|
+
trace_id: 'trace_id',
|
|
16
|
+
parent_id: 'parent_id',
|
|
17
|
+
span_id: 'span_id',
|
|
18
|
+
sampled: true,
|
|
19
|
+
flags: 'flags'
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'adds the proper X-B3 headers and trace the request' do
|
|
24
|
+
expect(::ZipkinTracer::TraceContainer).to receive(:with_trace_id).and_call_original
|
|
25
|
+
headers = LHC.get(:local).request.headers
|
|
26
|
+
expect(headers['X-B3-TraceId']).to eq('trace_id')
|
|
27
|
+
expect(headers['X-B3-ParentSpanId']).to eq('parent_id')
|
|
28
|
+
expect(headers['X-B3-SpanId']).to eq('span_id')
|
|
29
|
+
expect(headers['X-B3-Sampled']).to eq('true')
|
|
30
|
+
expect(headers['X-B3-Flags']).to eq('flags')
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe 'creating new spans' do
|
|
35
|
+
context 'sampled? is false' do
|
|
36
|
+
before(:all) do
|
|
37
|
+
::ZipkinTracer::TraceContainer.setup_mock(
|
|
38
|
+
trace_id: 'trace_id',
|
|
39
|
+
parent_id: 'parent_id',
|
|
40
|
+
span_id: 'span_id',
|
|
41
|
+
sampled: false,
|
|
42
|
+
flags: 'flags'
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'adds the proper X-B3 headers' do
|
|
47
|
+
headers = LHC.get(:local).request.headers
|
|
48
|
+
expect(headers['X-B3-TraceId']).to eq('trace_id')
|
|
49
|
+
expect(headers['X-B3-ParentSpanId']).to eq('parent_id')
|
|
50
|
+
expect(headers['X-B3-SpanId']).to eq('span_id')
|
|
51
|
+
expect(headers['X-B3-Sampled']).to eq('false')
|
|
52
|
+
expect(headers['X-B3-Flags']).to eq('flags')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'does not register a new span' do
|
|
56
|
+
# ensure no span was registered, by checking no call on span
|
|
57
|
+
expect_any_instance_of(described_class).not_to receive(:span).and_call_original
|
|
58
|
+
LHC.get(:local)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context 'sampled? is true' do
|
|
63
|
+
before(:all) do
|
|
64
|
+
::ZipkinTracer::TraceContainer.setup_mock(
|
|
65
|
+
trace_id: 'trace_id',
|
|
66
|
+
parent_id: 'parent_id',
|
|
67
|
+
span_id: 'span_id',
|
|
68
|
+
sampled: true,
|
|
69
|
+
flags: 'flags'
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'adds the proper X-B3 headers' do
|
|
74
|
+
headers = LHC.get(:local).request.headers
|
|
75
|
+
expect(headers['X-B3-TraceId']).to eq('trace_id')
|
|
76
|
+
expect(headers['X-B3-ParentSpanId']).to eq('parent_id')
|
|
77
|
+
expect(headers['X-B3-SpanId']).to eq('span_id')
|
|
78
|
+
expect(headers['X-B3-Sampled']).to eq('true')
|
|
79
|
+
expect(headers['X-B3-Flags']).to eq('flags')
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'does register a new span' do
|
|
83
|
+
# ensure a span was registered, by checking call on span
|
|
84
|
+
expect_any_instance_of(described_class).to receive(:span).at_least(:once).and_call_original
|
|
85
|
+
LHC.get(:local)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
%w(ZipkinTracer Trace).each do |klass|
|
|
91
|
+
context "without #{klass}" do
|
|
92
|
+
before(:all) do
|
|
93
|
+
TemporaryClass = Object.send(:const_get, klass)
|
|
94
|
+
Object.send(:remove_const, klass)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
after(:all) do
|
|
98
|
+
Object.send(:const_set, klass, TemporaryClass)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'does not trace the request' do
|
|
102
|
+
headers = nil
|
|
103
|
+
expect { headers = LHC.get(:local).request.headers }.not_to raise_error
|
|
104
|
+
expect(headers['X-B3-TraceId']).to be_nil
|
|
105
|
+
expect(headers['X-B3-ParentSpanId']).to be_nil
|
|
106
|
+
expect(headers['X-B3-SpanId']).to be_nil
|
|
107
|
+
expect(headers['X-B3-Sampled']).to be_nil
|
|
108
|
+
expect(headers['X-B3-Flags']).to be_nil
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
%w(Annotation BinaryAnnotation Endpoint).each do |klass|
|
|
114
|
+
context "without Trace::#{klass}" do
|
|
115
|
+
before(:all) do
|
|
116
|
+
TemporaryClass = Trace.send(:const_get, klass)
|
|
117
|
+
Trace.send(:remove_const, klass)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
after(:all) do
|
|
121
|
+
Trace.send(:const_set, klass, TemporaryClass)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it 'does not trace the request' do
|
|
125
|
+
headers = nil
|
|
126
|
+
expect { headers = LHC.get(:local).request.headers }.not_to raise_error
|
|
127
|
+
expect(headers['X-B3-TraceId']).to be_nil
|
|
128
|
+
expect(headers['X-B3-ParentSpanId']).to be_nil
|
|
129
|
+
expect(headers['X-B3-SpanId']).to be_nil
|
|
130
|
+
expect(headers['X-B3-Sampled']).to be_nil
|
|
131
|
+
expect(headers['X-B3-Flags']).to be_nil
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|