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