dhc 1.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 (185) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/rubocop.yml +27 -0
  3. data/.github/workflows/test.yml +27 -0
  4. data/.gitignore +37 -0
  5. data/.rubocop.yml +105 -0
  6. data/.ruby-version +1 -0
  7. data/Gemfile +5 -0
  8. data/LICENSE +674 -0
  9. data/README.md +999 -0
  10. data/Rakefile +25 -0
  11. data/dhc.gemspec +40 -0
  12. data/lib/core_ext/hash/deep_transform_values.rb +48 -0
  13. data/lib/dhc.rb +77 -0
  14. data/lib/dhc/concerns/dhc/basic_methods_concern.rb +42 -0
  15. data/lib/dhc/concerns/dhc/configuration_concern.rb +20 -0
  16. data/lib/dhc/concerns/dhc/fix_invalid_encoding_concern.rb +42 -0
  17. data/lib/dhc/concerns/dhc/formats_concern.rb +25 -0
  18. data/lib/dhc/concerns/dhc/request/user_agent_concern.rb +25 -0
  19. data/lib/dhc/config.rb +47 -0
  20. data/lib/dhc/endpoint.rb +119 -0
  21. data/lib/dhc/error.rb +82 -0
  22. data/lib/dhc/errors/client_error.rb +73 -0
  23. data/lib/dhc/errors/parser_error.rb +4 -0
  24. data/lib/dhc/errors/server_error.rb +28 -0
  25. data/lib/dhc/errors/timeout.rb +4 -0
  26. data/lib/dhc/errors/unknown_error.rb +4 -0
  27. data/lib/dhc/format.rb +18 -0
  28. data/lib/dhc/formats.rb +8 -0
  29. data/lib/dhc/formats/form.rb +45 -0
  30. data/lib/dhc/formats/json.rb +55 -0
  31. data/lib/dhc/formats/multipart.rb +45 -0
  32. data/lib/dhc/formats/plain.rb +42 -0
  33. data/lib/dhc/interceptor.rb +36 -0
  34. data/lib/dhc/interceptors.rb +26 -0
  35. data/lib/dhc/interceptors/auth.rb +94 -0
  36. data/lib/dhc/interceptors/caching.rb +148 -0
  37. data/lib/dhc/interceptors/default_timeout.rb +16 -0
  38. data/lib/dhc/interceptors/logging.rb +37 -0
  39. data/lib/dhc/interceptors/monitoring.rb +92 -0
  40. data/lib/dhc/interceptors/prometheus.rb +51 -0
  41. data/lib/dhc/interceptors/retry.rb +41 -0
  42. data/lib/dhc/interceptors/rollbar.rb +36 -0
  43. data/lib/dhc/interceptors/throttle.rb +86 -0
  44. data/lib/dhc/interceptors/zipkin.rb +110 -0
  45. data/lib/dhc/railtie.rb +9 -0
  46. data/lib/dhc/request.rb +161 -0
  47. data/lib/dhc/response.rb +60 -0
  48. data/lib/dhc/response/data.rb +28 -0
  49. data/lib/dhc/response/data/base.rb +18 -0
  50. data/lib/dhc/response/data/collection.rb +16 -0
  51. data/lib/dhc/response/data/item.rb +29 -0
  52. data/lib/dhc/rspec.rb +11 -0
  53. data/lib/dhc/test/cache_helper.rb +3 -0
  54. data/lib/dhc/version.rb +5 -0
  55. data/script/ci/build.sh +19 -0
  56. data/spec/basic_methods/delete_spec.rb +34 -0
  57. data/spec/basic_methods/get_spec.rb +49 -0
  58. data/spec/basic_methods/post_spec.rb +42 -0
  59. data/spec/basic_methods/put_spec.rb +48 -0
  60. data/spec/basic_methods/request_spec.rb +19 -0
  61. data/spec/basic_methods/request_without_rails_spec.rb +29 -0
  62. data/spec/config/endpoints_spec.rb +63 -0
  63. data/spec/config/placeholders_spec.rb +32 -0
  64. data/spec/core_ext/hash/deep_transform_values_spec.rb +24 -0
  65. data/spec/dummy/README.rdoc +28 -0
  66. data/spec/dummy/Rakefile +8 -0
  67. data/spec/dummy/app/assets/config/manifest.js +3 -0
  68. data/spec/dummy/app/assets/images/.keep +0 -0
  69. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  70. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  71. data/spec/dummy/app/controllers/application_controller.rb +7 -0
  72. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  73. data/spec/dummy/app/helpers/application_helper.rb +4 -0
  74. data/spec/dummy/app/mailers/.keep +0 -0
  75. data/spec/dummy/app/models/.keep +0 -0
  76. data/spec/dummy/app/models/concerns/.keep +0 -0
  77. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  78. data/spec/dummy/bin/bundle +5 -0
  79. data/spec/dummy/bin/rails +6 -0
  80. data/spec/dummy/bin/rake +6 -0
  81. data/spec/dummy/config.ru +6 -0
  82. data/spec/dummy/config/application.rb +16 -0
  83. data/spec/dummy/config/boot.rb +7 -0
  84. data/spec/dummy/config/environment.rb +7 -0
  85. data/spec/dummy/config/environments/development.rb +36 -0
  86. data/spec/dummy/config/environments/production.rb +77 -0
  87. data/spec/dummy/config/environments/test.rb +41 -0
  88. data/spec/dummy/config/initializers/assets.rb +10 -0
  89. data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
  90. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  91. data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  92. data/spec/dummy/config/initializers/inflections.rb +18 -0
  93. data/spec/dummy/config/initializers/mime_types.rb +6 -0
  94. data/spec/dummy/config/initializers/session_store.rb +5 -0
  95. data/spec/dummy/config/initializers/wrap_parameters.rb +11 -0
  96. data/spec/dummy/config/locales/en.yml +23 -0
  97. data/spec/dummy/config/routes.rb +58 -0
  98. data/spec/dummy/config/secrets.yml +22 -0
  99. data/spec/dummy/lib/assets/.keep +0 -0
  100. data/spec/dummy/log/.keep +0 -0
  101. data/spec/dummy/public/404.html +67 -0
  102. data/spec/dummy/public/422.html +67 -0
  103. data/spec/dummy/public/500.html +66 -0
  104. data/spec/dummy/public/favicon.ico +0 -0
  105. data/spec/endpoint/compile_spec.rb +35 -0
  106. data/spec/endpoint/match_spec.rb +41 -0
  107. data/spec/endpoint/placeholders_spec.rb +30 -0
  108. data/spec/endpoint/remove_interpolated_params_spec.rb +17 -0
  109. data/spec/endpoint/values_as_params_spec.rb +31 -0
  110. data/spec/error/dup_spec.rb +12 -0
  111. data/spec/error/find_spec.rb +57 -0
  112. data/spec/error/response_spec.rb +17 -0
  113. data/spec/error/timeout_spec.rb +14 -0
  114. data/spec/error/to_s_spec.rb +85 -0
  115. data/spec/formats/form_spec.rb +27 -0
  116. data/spec/formats/json_spec.rb +66 -0
  117. data/spec/formats/multipart_spec.rb +26 -0
  118. data/spec/formats/plain_spec.rb +29 -0
  119. data/spec/interceptors/after_request_spec.rb +20 -0
  120. data/spec/interceptors/after_response_spec.rb +39 -0
  121. data/spec/interceptors/auth/basic_auth_spec.rb +17 -0
  122. data/spec/interceptors/auth/bearer_spec.rb +19 -0
  123. data/spec/interceptors/auth/body_spec.rb +36 -0
  124. data/spec/interceptors/auth/long_basic_auth_credentials_spec.rb +17 -0
  125. data/spec/interceptors/auth/no_instance_var_for_options_spec.rb +27 -0
  126. data/spec/interceptors/auth/reauthentication_configuration_spec.rb +61 -0
  127. data/spec/interceptors/auth/reauthentication_spec.rb +44 -0
  128. data/spec/interceptors/before_request_spec.rb +21 -0
  129. data/spec/interceptors/before_response_spec.rb +20 -0
  130. data/spec/interceptors/caching/hydra_spec.rb +26 -0
  131. data/spec/interceptors/caching/main_spec.rb +73 -0
  132. data/spec/interceptors/caching/methods_spec.rb +42 -0
  133. data/spec/interceptors/caching/multilevel_cache_spec.rb +139 -0
  134. data/spec/interceptors/caching/options_spec.rb +78 -0
  135. data/spec/interceptors/caching/parameters_spec.rb +24 -0
  136. data/spec/interceptors/caching/response_status_spec.rb +29 -0
  137. data/spec/interceptors/caching/to_cache_spec.rb +16 -0
  138. data/spec/interceptors/default_interceptors_spec.rb +15 -0
  139. data/spec/interceptors/default_timeout/main_spec.rb +34 -0
  140. data/spec/interceptors/define_spec.rb +30 -0
  141. data/spec/interceptors/dup_spec.rb +19 -0
  142. data/spec/interceptors/logging/main_spec.rb +37 -0
  143. data/spec/interceptors/monitoring/caching_spec.rb +66 -0
  144. data/spec/interceptors/monitoring/main_spec.rb +97 -0
  145. data/spec/interceptors/prometheus_spec.rb +54 -0
  146. data/spec/interceptors/response_competition_spec.rb +39 -0
  147. data/spec/interceptors/retry/main_spec.rb +73 -0
  148. data/spec/interceptors/return_response_spec.rb +38 -0
  149. data/spec/interceptors/rollbar/invalid_encoding_spec.rb +43 -0
  150. data/spec/interceptors/rollbar/main_spec.rb +57 -0
  151. data/spec/interceptors/throttle/main_spec.rb +236 -0
  152. data/spec/interceptors/throttle/reset_track_spec.rb +53 -0
  153. data/spec/interceptors/zipkin/distributed_tracing_spec.rb +135 -0
  154. data/spec/rails_helper.rb +6 -0
  155. data/spec/request/body_spec.rb +39 -0
  156. data/spec/request/encoding_spec.rb +38 -0
  157. data/spec/request/error_handling_spec.rb +88 -0
  158. data/spec/request/headers_spec.rb +12 -0
  159. data/spec/request/ignore_errors_spec.rb +73 -0
  160. data/spec/request/option_dup_spec.rb +13 -0
  161. data/spec/request/parallel_requests_spec.rb +59 -0
  162. data/spec/request/params_encoding_spec.rb +26 -0
  163. data/spec/request/request_without_rails_spec.rb +15 -0
  164. data/spec/request/url_patterns_spec.rb +54 -0
  165. data/spec/request/user_agent_spec.rb +26 -0
  166. data/spec/request/user_agent_without_rails_spec.rb +27 -0
  167. data/spec/response/body_spec.rb +16 -0
  168. data/spec/response/code_spec.rb +16 -0
  169. data/spec/response/data_accessor_spec.rb +29 -0
  170. data/spec/response/data_spec.rb +85 -0
  171. data/spec/response/effective_url_spec.rb +16 -0
  172. data/spec/response/headers_spec.rb +18 -0
  173. data/spec/response/options_spec.rb +18 -0
  174. data/spec/response/success_spec.rb +13 -0
  175. data/spec/response/time_spec.rb +21 -0
  176. data/spec/spec_helper.rb +9 -0
  177. data/spec/support/fixtures/json/feedback.json +11 -0
  178. data/spec/support/fixtures/json/feedbacks.json +164 -0
  179. data/spec/support/fixtures/json/localina_content_ad.json +23 -0
  180. data/spec/support/load_json.rb +5 -0
  181. data/spec/support/reset_config.rb +8 -0
  182. data/spec/support/zipkin_mock.rb +114 -0
  183. data/spec/timeouts/no_signal_spec.rb +13 -0
  184. data/spec/timeouts/timings_spec.rb +55 -0
  185. metadata +527 -0
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC do
6
+ context 'interceptor' do
7
+ before(:each) do
8
+ class Services
9
+ def self.timing(_path, _time); end
10
+ end
11
+
12
+ class StatsTimingInterceptor < DHC::Interceptor
13
+ def after_response
14
+ uri = URI.parse(response.request.url)
15
+ path = [
16
+ 'web',
17
+ ((ActiveSupport.gem_version >= Gem::Version.new('6.0.0')) ? Rails.application.class.module_parent_name : Rails.application.class.parent_name).underscore,
18
+ Rails.env,
19
+ response.request.method,
20
+ uri.scheme,
21
+ uri.host,
22
+ response.code
23
+ ].join('.')
24
+ Services.timing(path.downcase, response.time)
25
+ end
26
+ end
27
+ DHC.configure { |c| c.interceptors = [StatsTimingInterceptor] }
28
+ end
29
+
30
+ let(:url) { 'http://depay.fi/v2/feedbacks/-Sc4_pYNpqfsudzhtivfkA' }
31
+
32
+ it 'can take action after a response was received' do
33
+ allow(Services).to receive(:timing).with('web.dummy.test.get.http.depay.fi.200', 0)
34
+ stub_request(:get, url)
35
+ DHC.get(url)
36
+ expect(Services).to have_received(:timing)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::Auth do
6
+ before(:each) do
7
+ DHC.config.interceptors = [DHC::Auth]
8
+ end
9
+
10
+ it 'adds basic auth to every request' do
11
+ options = { basic: { username: 'steve', password: 'can' } }
12
+ DHC.config.endpoint(:local, 'http://depay.fi', auth: options)
13
+ stub_request(:get, 'http://depay.fi')
14
+ .with(headers: { 'Authorization' => 'Basic c3RldmU6Y2Fu' })
15
+ DHC.get(:local)
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::Auth do
6
+ before(:each) do
7
+ DHC.config.interceptors = [DHC::Auth]
8
+ end
9
+
10
+ it 'adds the bearer token to every request' do
11
+ def bearer_token
12
+ '123456'
13
+ end
14
+ options = { bearer: -> { bearer_token } }
15
+ DHC.config.endpoint(:local, 'http://depay.fi', auth: options)
16
+ stub_request(:get, 'http://depay.fi').with(headers: { 'Authorization' => 'Bearer 123456' })
17
+ DHC.get(:local)
18
+ end
19
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::Auth do
6
+ before(:each) do
7
+ DHC.config.interceptors = [DHC::Auth]
8
+ end
9
+
10
+ it 'adds body authentication to the existing request body' do
11
+ stub_request(:post, 'http://depay.fi/')
12
+ .with(body: {
13
+ message: 'body',
14
+ userToken: 'dheur5hrk3'
15
+ }.to_json)
16
+
17
+ DHC.post('http://depay.fi', auth: { body: { userToken: 'dheur5hrk3' } }, body: {
18
+ message: 'body'
19
+ })
20
+ end
21
+
22
+ it 'adds body authentication to an empty request body' do
23
+ stub_request(:post, 'http://depay.fi/')
24
+ .with(body: {
25
+ userToken: 'dheur5hrk3'
26
+ }.to_json)
27
+
28
+ DHC.post('http://depay.fi', auth: { body: { userToken: 'dheur5hrk3' } })
29
+ end
30
+
31
+ it 'adds nothing if request method is GET' do
32
+ stub_request(:get, 'http://depay.fi/')
33
+
34
+ DHC.get('http://depay.fi', auth: { body: { userToken: 'dheur5hrk3' } })
35
+ end
36
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::Auth do
6
+ before(:each) do
7
+ DHC.config.interceptors = [DHC::Auth]
8
+ end
9
+
10
+ it 'adds basic auth in a correct way even if username and password are especially long' do
11
+ options = { basic: { username: '123456789101234', password: '12345678901234567890123456789012' } }
12
+ DHC.config.endpoint(:local, 'http://depay.fi', auth: options)
13
+ stub_request(:get, 'http://depay.fi')
14
+ .with(headers: { 'Authorization' => 'Basic MTIzNDU2Nzg5MTAxMjM0OjEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEy' })
15
+ DHC.get(:local)
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::Auth do
6
+ before(:each) do
7
+ class AuthPrepInterceptor < DHC::Interceptor
8
+
9
+ def before_request
10
+ request.options[:auth] = { bearer: 'sometoken' }
11
+ end
12
+ end
13
+
14
+ DHC.config.interceptors = [AuthPrepInterceptor, DHC::Auth]
15
+ end
16
+
17
+ after do
18
+ DHC.config.reset
19
+ end
20
+
21
+ it 'does not use instance variables internally so that other interceptors can still change auth options' do
22
+ stub_request(:get, 'http://depay.fi/')
23
+ .with(headers: { 'Authorization' => 'Bearer sometoken' })
24
+ .to_return(status: 200)
25
+ DHC.get('http://depay.fi')
26
+ end
27
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::Auth do
6
+ let(:bearer_token) { '123456' }
7
+
8
+ before(:each) do
9
+ stub_request(:get, 'http://depay.fi').with(headers: { 'Authorization' => "Bearer #{bearer_token}" })
10
+ end
11
+
12
+ context 'configuration check not happening' do
13
+ let(:options) { { bearer: bearer_token } }
14
+
15
+ before(:each) { DHC.config.interceptors = [DHC::Auth, DHC::Retry] }
16
+
17
+ it 'max_recovery_attempts is zero' do
18
+ expect_any_instance_of(described_class).not_to receive(:warn)
19
+ DHC.config.endpoint(:local, 'http://depay.fi', auth: options.merge(max_recovery_attempts: 0))
20
+ DHC.get(:local)
21
+ end
22
+
23
+ it 'max_recovery_attempts is missing' do
24
+ expect_any_instance_of(described_class).not_to receive(:warn)
25
+ DHC.config.endpoint(:local, 'http://depay.fi', auth: options)
26
+ DHC.get(:local)
27
+ end
28
+ end
29
+
30
+ context 'configuration check happening' do
31
+ let(:options) { { bearer: bearer_token, max_recovery_attempts: 1, refresh_client_token: -> { 'here comes your refresh code' } } }
32
+
33
+ it 'no warning with proper options' do
34
+ DHC.config.interceptors = [DHC::Auth, DHC::Retry]
35
+ DHC.config.endpoint(:local, 'http://depay.fi', auth: options)
36
+ expect_any_instance_of(described_class).not_to receive(:warn)
37
+ DHC.get(:local)
38
+ end
39
+
40
+ it 'warn refresh_client_token is a string' do
41
+ DHC.config.interceptors = [DHC::Auth, DHC::Retry]
42
+ DHC.config.endpoint(:local, 'http://depay.fi', auth: options.merge(refresh_client_token: bearer_token))
43
+ expect_any_instance_of(described_class).to receive(:warn).with('[WARNING] The given refresh_client_token must be a Proc for reauthentication.')
44
+ DHC.get(:local)
45
+ end
46
+
47
+ it 'warn interceptors miss DHC::Retry' do
48
+ DHC.config.interceptors = [DHC::Auth]
49
+ DHC.config.endpoint(:local, 'http://depay.fi', auth: options)
50
+ expect_any_instance_of(described_class).to receive(:warn).with('[WARNING] Your interceptors must include DHC::Retry after DHC::Auth.')
51
+ DHC.get(:local)
52
+ end
53
+
54
+ it 'warn interceptors DHC::Retry before DHC::Auth' do
55
+ DHC.config.interceptors = [DHC::Retry, DHC::Auth]
56
+ DHC.config.endpoint(:local, 'http://depay.fi', auth: options)
57
+ expect_any_instance_of(described_class).to receive(:warn).with('[WARNING] Your interceptors must include DHC::Retry after DHC::Auth.')
58
+ DHC.get(:local)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::Auth do
6
+ let(:initial_token) { '123456' }
7
+ let(:refresh_token) { 'abcdef' }
8
+ let(:options) { { bearer: initial_token, refresh_client_token: -> { refresh_token } } }
9
+ let!(:auth_failing) do
10
+ stub_request(:get, 'http://depay.fi')
11
+ .with(headers: { 'Authorization' => "Bearer #{initial_token}" })
12
+ .to_return(status: 401, body: '{}') # DHC::Unauthorized
13
+ end
14
+ let!(:auth_suceeding_after_recovery) do
15
+ stub_request(:get, 'http://depay.fi')
16
+ .with(headers: { 'Authorization' => "Bearer #{refresh_token}" })
17
+ end
18
+
19
+ before(:each) do
20
+ DHC.config.interceptors = [DHC::Auth, DHC::Retry]
21
+ end
22
+
23
+ it 'recovery is attempted' do
24
+ DHC.config.endpoint(:local, 'http://depay.fi', auth: options)
25
+ # the retried request (with updated Bearer), that should work
26
+ DHC.get(:local)
27
+ expect(auth_suceeding_after_recovery).to have_been_made.once
28
+ end
29
+
30
+ it 'recovery is not attempted again when the request has reauthenticated: true ' do
31
+ DHC.config.endpoint(:local, 'http://depay.fi', auth: options.merge(reauthenticated: true))
32
+ expect { DHC.get(:local) }.to raise_error(DHC::Unauthorized)
33
+ end
34
+
35
+ context 'token format' do
36
+ let(:initial_token) { 'BAsZ-98-ZZZ' }
37
+
38
+ it 'refreshes tokens with various formats' do
39
+ DHC.config.endpoint(:local, 'http://depay.fi', auth: options)
40
+ DHC.get(:local)
41
+ expect(auth_suceeding_after_recovery).to have_been_made.once
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC do
6
+ context 'interceptor' do
7
+ before(:each) do
8
+ class TrackingIdInterceptor < DHC::Interceptor
9
+ def before_request
10
+ request.params[:tid] = 123
11
+ end
12
+ end
13
+ DHC.configure { |c| c.interceptors = [TrackingIdInterceptor] }
14
+ end
15
+
16
+ it 'can modify requests before they are send' do
17
+ stub_request(:get, 'http://depay.fi/?tid=123')
18
+ DHC.get('http://depay.fi')
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC do
6
+ context 'interceptor' do
7
+ before(:each) do
8
+ class SomeInterceptor < DHC::Interceptor
9
+ def before_response; end
10
+ end
11
+ DHC.configure { |c| c.interceptors = [SomeInterceptor] }
12
+ end
13
+
14
+ it 'can perform some actions before a reponse is received' do
15
+ expect_any_instance_of(SomeInterceptor).to receive(:before_response)
16
+ stub_request(:get, 'http://depay.fi')
17
+ DHC.get('http://depay.fi')
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::Caching do
6
+ before(:each) do
7
+ DHC.config.interceptors = [DHC::Caching]
8
+ DHC::Caching.cache = Rails.cache
9
+ Rails.cache.clear
10
+ end
11
+
12
+ let!(:first_request) do
13
+ stub_request(:get, 'http://depay.fi/').to_return(body: 'Website')
14
+ end
15
+
16
+ let!(:second_request) do
17
+ stub_request(:get, 'http://depay.fi/weather').to_return(body: 'The weather')
18
+ end
19
+
20
+ it 'does not fetch requests served from cache when doing requests in parallel with hydra' do
21
+ DHC.request([{ url: 'http://depay.fi', cache: true }, { url: 'http://depay.fi/weather', cache: true }])
22
+ DHC.request([{ url: 'http://depay.fi', cache: true }, { url: 'http://depay.fi/weather', cache: true }])
23
+ assert_requested first_request, times: 1
24
+ assert_requested second_request, times: 1
25
+ end
26
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::Caching do
6
+ before(:each) do
7
+ DHC.config.interceptors = [DHC::Caching]
8
+ DHC::Caching.cache = Rails.cache
9
+ Rails.cache.clear
10
+ end
11
+
12
+ let(:stub) { stub_request(:get, 'http://depay.fi').to_return(status: 200, body: 'The Website') }
13
+
14
+ it 'serves a response from cache' do
15
+ stub
16
+ DHC.config.endpoint(:local, 'http://depay.fi', cache: { expires_in: 5.minutes })
17
+ expect(Rails.cache).to receive(:write)
18
+ .with(
19
+ "DHC_CACHE(v#{DHC::Caching::CACHE_VERSION}): GET http://depay.fi",
20
+ {
21
+ body: 'The Website',
22
+ code: 200,
23
+ headers: nil,
24
+ return_code: nil,
25
+ mock: :webmock
26
+ }, { expires_in: 5.minutes }
27
+ )
28
+ .and_call_original
29
+ original_response = DHC.get(:local)
30
+ cached_response = DHC.get(:local)
31
+ expect(original_response.body).to eq cached_response.body
32
+ expect(original_response.code).to eq cached_response.code
33
+ expect(original_response.headers).to eq cached_response.headers
34
+ expect(original_response.options[:return_code]).to eq cached_response.options[:return_code]
35
+ expect(original_response.mock).to eq cached_response.mock
36
+ assert_requested stub, times: 1
37
+ end
38
+
39
+ it 'does not serve from cache if option is not set' do
40
+ DHC.config.endpoint(:local, 'http://depay.fi')
41
+ expect(Rails.cache).not_to receive(:write)
42
+ expect(Rails.cache).not_to receive(:fetch)
43
+ stub
44
+ 2.times { DHC.get(:local) }
45
+ assert_requested stub, times: 2
46
+ end
47
+
48
+ it 'lets you configure the cache key that will be used' do
49
+ DHC.config.endpoint(:local, 'http://depay.fi', cache: { key: 'STATICKEY' })
50
+ expect(Rails.cache).to receive(:fetch).at_least(:once).with("DHC_CACHE(v#{DHC::Caching::CACHE_VERSION}): STATICKEY").and_call_original
51
+ expect(Rails.cache).to receive(:write).with("DHC_CACHE(v#{DHC::Caching::CACHE_VERSION}): STATICKEY", anything, anything).and_call_original
52
+ stub
53
+ DHC.get(:local)
54
+ end
55
+
56
+ it 'does not store server errors in cache' do
57
+ DHC.config.endpoint(:local, 'http://depay.fi', cache: true)
58
+ stub_request(:get, 'http://depay.fi').to_return(status: 500, body: 'ERROR')
59
+ expect { DHC.get(:local) }.to raise_error DHC::ServerError
60
+ stub
61
+ expect(Rails.cache).to receive(:write).once
62
+ DHC.get(:local)
63
+ end
64
+
65
+ it 'marks response not from cache as not served from cache and from cache as served from cache' do
66
+ stub
67
+ DHC.config.endpoint(:local, 'http://depay.fi', cache: true)
68
+ original_response = DHC.get(:local)
69
+ expect(original_response.from_cache?).to eq false
70
+ cached_response = DHC.get(:local)
71
+ expect(cached_response.from_cache?).to eq true
72
+ end
73
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::Caching do
6
+ before(:each) do
7
+ DHC.config.interceptors = [DHC::Caching]
8
+ DHC::Caching.cache = Rails.cache
9
+ Rails.cache.clear
10
+
11
+ DHC.config.endpoint(:local, 'http://depay.fi', cache: { expires_in: 5.minutes })
12
+ end
13
+
14
+ let!(:stub) { stub_request(:post, 'http://depay.fi').to_return(status: 200, body: 'The Website') }
15
+
16
+ it 'only caches GET requests by default' do
17
+ expect(Rails.cache).not_to receive(:write)
18
+ DHC.post(:local)
19
+ assert_requested stub, times: 1
20
+ end
21
+
22
+ it 'also caches other methods, when explicitly enabled' do
23
+ expect(Rails.cache).to receive(:write)
24
+ .with(
25
+ "DHC_CACHE(v#{DHC::Caching::CACHE_VERSION}): POST http://depay.fi",
26
+ {
27
+ body: 'The Website',
28
+ code: 200,
29
+ headers: nil,
30
+ return_code: nil,
31
+ mock: :webmock
32
+ }, { expires_in: 5.minutes }
33
+ )
34
+ .and_call_original
35
+ original_response = DHC.post(:local, cache: { methods: [:post] })
36
+ cached_response = DHC.post(:local, cache: { methods: [:post] })
37
+ expect(original_response.body).to eq cached_response.body
38
+ expect(original_response.code).to eq cached_response.code
39
+ expect(original_response.headers).to eq cached_response.headers
40
+ assert_requested stub, times: 1
41
+ end
42
+ end