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.
Files changed (195) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +37 -0
  3. data/.rubocop.localch.yml +325 -0
  4. data/.rubocop.yml +61 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +13 -0
  7. data/Gemfile.activesupport4 +4 -0
  8. data/Gemfile.activesupport5 +4 -0
  9. data/Gemfile.activesupport6 +4 -0
  10. data/LICENSE +674 -0
  11. data/README.md +984 -0
  12. data/Rakefile +25 -0
  13. data/cider-ci.yml +6 -0
  14. data/cider-ci/bin/bundle +51 -0
  15. data/cider-ci/bin/ruby_install +8 -0
  16. data/cider-ci/bin/ruby_version +25 -0
  17. data/cider-ci/jobs/rspec-activesupport-4.yml +28 -0
  18. data/cider-ci/jobs/rspec-activesupport-5.yml +27 -0
  19. data/cider-ci/jobs/rspec-activesupport-6.yml +28 -0
  20. data/cider-ci/jobs/rubocop.yml +18 -0
  21. data/cider-ci/task_components/bundle.yml +22 -0
  22. data/cider-ci/task_components/rspec.yml +36 -0
  23. data/cider-ci/task_components/rubocop.yml +29 -0
  24. data/cider-ci/task_components/ruby.yml +15 -0
  25. data/friday.yml +3 -0
  26. data/lhc.gemspec +39 -0
  27. data/lib/core_ext/hash/deep_transform_values.rb +48 -0
  28. data/lib/lhc.rb +136 -0
  29. data/lib/lhc/concerns/lhc/basic_methods_concern.rb +42 -0
  30. data/lib/lhc/concerns/lhc/configuration_concern.rb +20 -0
  31. data/lib/lhc/concerns/lhc/fix_invalid_encoding_concern.rb +42 -0
  32. data/lib/lhc/concerns/lhc/formats_concern.rb +25 -0
  33. data/lib/lhc/concerns/lhc/request/user_agent_concern.rb +25 -0
  34. data/lib/lhc/config.rb +47 -0
  35. data/lib/lhc/endpoint.rb +119 -0
  36. data/lib/lhc/error.rb +80 -0
  37. data/lib/lhc/errors/client_error.rb +73 -0
  38. data/lib/lhc/errors/parser_error.rb +4 -0
  39. data/lib/lhc/errors/server_error.rb +28 -0
  40. data/lib/lhc/errors/timeout.rb +4 -0
  41. data/lib/lhc/errors/unknown_error.rb +4 -0
  42. data/lib/lhc/format.rb +18 -0
  43. data/lib/lhc/formats.rb +8 -0
  44. data/lib/lhc/formats/form.rb +45 -0
  45. data/lib/lhc/formats/json.rb +55 -0
  46. data/lib/lhc/formats/multipart.rb +45 -0
  47. data/lib/lhc/formats/plain.rb +42 -0
  48. data/lib/lhc/interceptor.rb +32 -0
  49. data/lib/lhc/interceptors.rb +26 -0
  50. data/lib/lhc/interceptors/auth.rb +98 -0
  51. data/lib/lhc/interceptors/caching.rb +127 -0
  52. data/lib/lhc/interceptors/default_timeout.rb +16 -0
  53. data/lib/lhc/interceptors/logging.rb +37 -0
  54. data/lib/lhc/interceptors/monitoring.rb +63 -0
  55. data/lib/lhc/interceptors/prometheus.rb +51 -0
  56. data/lib/lhc/interceptors/retry.rb +41 -0
  57. data/lib/lhc/interceptors/rollbar.rb +36 -0
  58. data/lib/lhc/interceptors/throttle.rb +81 -0
  59. data/lib/lhc/interceptors/zipkin.rb +110 -0
  60. data/lib/lhc/railtie.rb +10 -0
  61. data/lib/lhc/request.rb +157 -0
  62. data/lib/lhc/response.rb +60 -0
  63. data/lib/lhc/response/data.rb +28 -0
  64. data/lib/lhc/response/data/base.rb +22 -0
  65. data/lib/lhc/response/data/collection.rb +16 -0
  66. data/lib/lhc/response/data/item.rb +29 -0
  67. data/lib/lhc/rspec.rb +12 -0
  68. data/lib/lhc/test/cache_helper.rb +3 -0
  69. data/lib/lhc/version.rb +5 -0
  70. data/script/ci/build.sh +19 -0
  71. data/spec/basic_methods/delete_spec.rb +34 -0
  72. data/spec/basic_methods/get_spec.rb +49 -0
  73. data/spec/basic_methods/post_spec.rb +42 -0
  74. data/spec/basic_methods/put_spec.rb +48 -0
  75. data/spec/basic_methods/request_spec.rb +19 -0
  76. data/spec/basic_methods/request_without_rails_spec.rb +29 -0
  77. data/spec/config/endpoints_spec.rb +63 -0
  78. data/spec/config/placeholders_spec.rb +32 -0
  79. data/spec/core_ext/hash/deep_transform_values_spec.rb +24 -0
  80. data/spec/dummy/README.rdoc +28 -0
  81. data/spec/dummy/Rakefile +8 -0
  82. data/spec/dummy/app/assets/config/manifest.js +3 -0
  83. data/spec/dummy/app/assets/images/.keep +0 -0
  84. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  85. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  86. data/spec/dummy/app/controllers/application_controller.rb +7 -0
  87. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  88. data/spec/dummy/app/helpers/application_helper.rb +4 -0
  89. data/spec/dummy/app/mailers/.keep +0 -0
  90. data/spec/dummy/app/models/.keep +0 -0
  91. data/spec/dummy/app/models/concerns/.keep +0 -0
  92. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  93. data/spec/dummy/bin/bundle +5 -0
  94. data/spec/dummy/bin/rails +6 -0
  95. data/spec/dummy/bin/rake +6 -0
  96. data/spec/dummy/config.ru +6 -0
  97. data/spec/dummy/config/application.rb +16 -0
  98. data/spec/dummy/config/boot.rb +7 -0
  99. data/spec/dummy/config/environment.rb +7 -0
  100. data/spec/dummy/config/environments/development.rb +36 -0
  101. data/spec/dummy/config/environments/production.rb +77 -0
  102. data/spec/dummy/config/environments/test.rb +41 -0
  103. data/spec/dummy/config/initializers/assets.rb +10 -0
  104. data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
  105. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  106. data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  107. data/spec/dummy/config/initializers/inflections.rb +18 -0
  108. data/spec/dummy/config/initializers/mime_types.rb +6 -0
  109. data/spec/dummy/config/initializers/session_store.rb +5 -0
  110. data/spec/dummy/config/initializers/wrap_parameters.rb +11 -0
  111. data/spec/dummy/config/locales/en.yml +23 -0
  112. data/spec/dummy/config/routes.rb +58 -0
  113. data/spec/dummy/config/secrets.yml +22 -0
  114. data/spec/dummy/lib/assets/.keep +0 -0
  115. data/spec/dummy/log/.keep +0 -0
  116. data/spec/dummy/public/404.html +67 -0
  117. data/spec/dummy/public/422.html +67 -0
  118. data/spec/dummy/public/500.html +66 -0
  119. data/spec/dummy/public/favicon.ico +0 -0
  120. data/spec/endpoint/compile_spec.rb +35 -0
  121. data/spec/endpoint/match_spec.rb +41 -0
  122. data/spec/endpoint/placeholders_spec.rb +30 -0
  123. data/spec/endpoint/remove_interpolated_params_spec.rb +17 -0
  124. data/spec/endpoint/values_as_params_spec.rb +31 -0
  125. data/spec/error/dup_spec.rb +12 -0
  126. data/spec/error/find_spec.rb +57 -0
  127. data/spec/error/response_spec.rb +17 -0
  128. data/spec/error/timeout_spec.rb +14 -0
  129. data/spec/error/to_s_spec.rb +80 -0
  130. data/spec/formats/form_spec.rb +27 -0
  131. data/spec/formats/json_spec.rb +66 -0
  132. data/spec/formats/multipart_spec.rb +26 -0
  133. data/spec/formats/plain_spec.rb +29 -0
  134. data/spec/interceptors/after_request_spec.rb +20 -0
  135. data/spec/interceptors/after_response_spec.rb +39 -0
  136. data/spec/interceptors/auth/basic_auth_spec.rb +17 -0
  137. data/spec/interceptors/auth/bearer_spec.rb +19 -0
  138. data/spec/interceptors/auth/reauthentication_configuration_spec.rb +61 -0
  139. data/spec/interceptors/auth/reauthentication_spec.rb +44 -0
  140. data/spec/interceptors/before_request_spec.rb +21 -0
  141. data/spec/interceptors/before_response_spec.rb +20 -0
  142. data/spec/interceptors/caching/hydra_spec.rb +26 -0
  143. data/spec/interceptors/caching/main_spec.rb +73 -0
  144. data/spec/interceptors/caching/methods_spec.rb +42 -0
  145. data/spec/interceptors/caching/options_spec.rb +89 -0
  146. data/spec/interceptors/caching/parameters_spec.rb +24 -0
  147. data/spec/interceptors/caching/response_status_spec.rb +29 -0
  148. data/spec/interceptors/caching/to_cache_spec.rb +16 -0
  149. data/spec/interceptors/default_interceptors_spec.rb +15 -0
  150. data/spec/interceptors/default_timeout/main_spec.rb +34 -0
  151. data/spec/interceptors/define_spec.rb +29 -0
  152. data/spec/interceptors/dup_spec.rb +19 -0
  153. data/spec/interceptors/logging/main_spec.rb +37 -0
  154. data/spec/interceptors/monitoring/main_spec.rb +97 -0
  155. data/spec/interceptors/prometheus_spec.rb +54 -0
  156. data/spec/interceptors/response_competition_spec.rb +41 -0
  157. data/spec/interceptors/retry/main_spec.rb +73 -0
  158. data/spec/interceptors/return_response_spec.rb +38 -0
  159. data/spec/interceptors/rollbar/invalid_encoding_spec.rb +43 -0
  160. data/spec/interceptors/rollbar/main_spec.rb +57 -0
  161. data/spec/interceptors/throttle/main_spec.rb +106 -0
  162. data/spec/interceptors/throttle/reset_track_spec.rb +53 -0
  163. data/spec/interceptors/zipkin/distributed_tracing_spec.rb +135 -0
  164. data/spec/rails_helper.rb +6 -0
  165. data/spec/request/body_spec.rb +39 -0
  166. data/spec/request/encoding_spec.rb +37 -0
  167. data/spec/request/error_handling_spec.rb +88 -0
  168. data/spec/request/headers_spec.rb +12 -0
  169. data/spec/request/ignore_errors_spec.rb +73 -0
  170. data/spec/request/option_dup_spec.rb +13 -0
  171. data/spec/request/parallel_requests_spec.rb +59 -0
  172. data/spec/request/params_encoding_spec.rb +26 -0
  173. data/spec/request/request_without_rails_spec.rb +15 -0
  174. data/spec/request/url_patterns_spec.rb +54 -0
  175. data/spec/request/user_agent_spec.rb +26 -0
  176. data/spec/request/user_agent_without_rails_spec.rb +27 -0
  177. data/spec/response/body_spec.rb +16 -0
  178. data/spec/response/code_spec.rb +16 -0
  179. data/spec/response/data_accessor_spec.rb +29 -0
  180. data/spec/response/data_spec.rb +61 -0
  181. data/spec/response/effective_url_spec.rb +16 -0
  182. data/spec/response/headers_spec.rb +18 -0
  183. data/spec/response/options_spec.rb +18 -0
  184. data/spec/response/success_spec.rb +13 -0
  185. data/spec/response/time_spec.rb +21 -0
  186. data/spec/spec_helper.rb +8 -0
  187. data/spec/support/fixtures/json/feedback.json +11 -0
  188. data/spec/support/fixtures/json/feedbacks.json +164 -0
  189. data/spec/support/fixtures/json/localina_content_ad.json +23 -0
  190. data/spec/support/load_json.rb +5 -0
  191. data/spec/support/reset_config.rb +7 -0
  192. data/spec/support/zipkin_mock.rb +113 -0
  193. data/spec/timeouts/no_signal_spec.rb +13 -0
  194. data/spec/timeouts/timings_spec.rb +55 -0
  195. 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