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,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::Caching do
6
+ let(:redis_url) { 'redis://localhost:6379/0' }
7
+ let(:redis_cache) do
8
+ spy('ActiveSupport::Cache::RedisCacheStore')
9
+ end
10
+
11
+ before do
12
+ Rails.cache.clear
13
+ DHC.config.interceptors = [DHC::Caching]
14
+ ActiveSupport::Cache::RedisCacheStore.new(url: redis_url).clear
15
+ allow(ActiveSupport::Cache::RedisCacheStore).to receive(:new).and_return(redis_cache)
16
+ allow(redis_cache).to receive(:present?).and_return(true)
17
+ end
18
+
19
+ let!(:request_stub) do
20
+ stub_request(:get, 'http://depay.fi/')
21
+ .to_return(body: '<h1>Hi there</h1>')
22
+ end
23
+
24
+ def request
25
+ DHC.get('http://depay.fi', cache: true)
26
+ end
27
+
28
+ def response_has_been_cached_and_served_from_cache!
29
+ original_response = request
30
+ cached_response = request
31
+
32
+ expect(original_response.body).to eq cached_response.body
33
+ expect(original_response.code).to eq cached_response.code
34
+ expect(original_response.headers).to eq cached_response.headers
35
+ expect(original_response.options[:return_code]).to eq cached_response.options[:return_code]
36
+ expect(original_response.mock).to eq cached_response.mock
37
+
38
+ assert_requested request_stub, times: 1
39
+ end
40
+
41
+ context 'only local cache has been configured' do
42
+ before do
43
+ DHC::Caching.cache = Rails.cache
44
+ end
45
+
46
+ it 'serves a response from local cache without trying the central cache' do
47
+ expect(Rails.cache).to receive(:fetch).at_least(:once).and_call_original
48
+ expect(Rails.cache).to receive(:write).and_call_original
49
+ expect(-> { response_has_been_cached_and_served_from_cache! })
50
+ .to output(%{[DHC] served from local cache: "DHC_CACHE(v1): GET http://depay.fi"\n}).to_stdout
51
+ end
52
+ end
53
+
54
+ context 'local and central cache have been configured' do
55
+ before do
56
+ DHC::Caching.cache = Rails.cache
57
+ DHC::Caching.central = {
58
+ read: redis_url,
59
+ write: redis_url
60
+ }
61
+ end
62
+
63
+ context 'found in central cache' do
64
+ it 'serves it from central cache if found there' do
65
+ expect(redis_cache).to receive(:fetch).and_return(nil,
66
+ body: '<h1>Hi there</h1>', code: 200, headers: nil, return_code: nil, mock: :webmock)
67
+ expect(redis_cache).to receive(:write).and_return(true)
68
+ expect(Rails.cache).to receive(:fetch).and_call_original
69
+ expect(Rails.cache).to receive(:write).and_call_original
70
+ expect(-> { response_has_been_cached_and_served_from_cache! })
71
+ .to output(%{[DHC] served from central cache: "DHC_CACHE(v1): GET http://depay.fi"\n}).to_stdout
72
+ end
73
+ end
74
+
75
+ context 'not found in central cache' do
76
+ it 'serves it from local cache if found there' do
77
+ expect(redis_cache).to receive(:fetch).and_return(nil, nil)
78
+ expect(redis_cache).to receive(:write).and_return(true)
79
+ expect(Rails.cache).to receive(:fetch).at_least(:once).and_call_original
80
+ expect(Rails.cache).to receive(:write).and_call_original
81
+ expect(-> { response_has_been_cached_and_served_from_cache! })
82
+ .to output(%{[DHC] served from local cache: "DHC_CACHE(v1): GET http://depay.fi"\n}).to_stdout
83
+ end
84
+ end
85
+ end
86
+
87
+ context 'only central read configured' do
88
+ before do
89
+ DHC::Caching.cache = Rails.cache
90
+ DHC::Caching.central = {
91
+ read: redis_url
92
+ }
93
+ end
94
+
95
+ it 'still serves responses from cache, but does not write them back' do
96
+ expect(redis_cache).to receive(:fetch).and_return(nil, body: '<h1>Hi there</h1>', code: 200, headers: nil, return_code: nil, mock: :webmock)
97
+ expect(redis_cache).not_to receive(:write)
98
+ expect(Rails.cache).to receive(:fetch).and_call_original
99
+ expect(Rails.cache).to receive(:write).and_call_original
100
+ expect(-> { response_has_been_cached_and_served_from_cache! })
101
+ .to output(%{[DHC] served from central cache: "DHC_CACHE(v1): GET http://depay.fi"\n}).to_stdout
102
+ end
103
+ end
104
+
105
+ context 'only central write configured' do
106
+ before do
107
+ DHC::Caching.cache = Rails.cache
108
+ DHC::Caching.central = {
109
+ write: redis_url
110
+ }
111
+ end
112
+
113
+ it 'still writes responses to cache, but does not retrieve them from there' do
114
+ expect(redis_cache).not_to receive(:fetch)
115
+ expect(redis_cache).to receive(:write).and_return(true)
116
+ expect(Rails.cache).to receive(:fetch).at_least(:once).and_call_original
117
+ expect(Rails.cache).to receive(:write).and_call_original
118
+ expect(-> { response_has_been_cached_and_served_from_cache! })
119
+ .to output(%{[DHC] served from local cache: "DHC_CACHE(v1): GET http://depay.fi"\n}).to_stdout
120
+ end
121
+ end
122
+
123
+ context 'central cache configured only' do
124
+ before do
125
+ DHC::Caching.cache = nil
126
+ DHC::Caching.central = {
127
+ read: redis_url,
128
+ write: redis_url
129
+ }
130
+ end
131
+
132
+ it 'does not inquire the local cache for information neither to write them' do
133
+ expect(redis_cache).to receive(:fetch).and_return(nil, body: '<h1>Hi there</h1>', code: 200, headers: nil, return_code: nil, mock: :webmock)
134
+ expect(redis_cache).to receive(:write).and_return(true)
135
+ expect(-> { response_has_been_cached_and_served_from_cache! })
136
+ .to output(%{[DHC] served from central cache: "DHC_CACHE(v1): GET http://depay.fi"\n}).to_stdout
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ # due to the fact that options passed into DHC get dup'ed
6
+ # we need a class where we can setup method expectations
7
+ # with `expect_any_instance`
8
+ class CacheMock
9
+ def fetch(*_); end
10
+
11
+ def write(*_); end
12
+ end
13
+
14
+ describe DHC::Caching do
15
+ let(:default_cache) { DHC::Caching.cache }
16
+
17
+ before(:each) do
18
+ stub_request(:get, 'http://depay.fi').to_return(status: 200, body: 'The Website')
19
+ DHC.config.interceptors = [DHC::Caching]
20
+ default_cache.clear
21
+ end
22
+
23
+ it 'does cache' do
24
+ expect(default_cache).to receive(:fetch)
25
+ expect(default_cache).to receive(:write)
26
+ DHC.get('http://depay.fi', cache: true)
27
+ end
28
+
29
+ it 'does not cache' do
30
+ expect(default_cache).not_to receive(:fetch)
31
+ expect(default_cache).not_to receive(:write)
32
+ DHC.get('http://depay.fi')
33
+ end
34
+
35
+ context 'options - directly via DHC.get' do
36
+ it 'uses the default cache' do
37
+ expect(default_cache).to receive(:fetch)
38
+ expect(default_cache).to receive(:write)
39
+ DHC.get('http://depay.fi', cache: true)
40
+ end
41
+
42
+ it 'uses the provided cache' do
43
+ expect_any_instance_of(CacheMock).to receive(:fetch)
44
+ expect_any_instance_of(CacheMock).to receive(:write)
45
+ DHC.get('http://depay.fi', cache: { use: CacheMock.new })
46
+ end
47
+
48
+ it 'cache options are properly forwarded to the cache' do
49
+ cache_options = { expires_in: 5.minutes, race_condition_ttl: 15.seconds }
50
+ expect(default_cache).to receive(:write).with(anything, anything, cache_options)
51
+ DHC.get('http://depay.fi', cache: cache_options)
52
+ end
53
+ end
54
+
55
+ context 'options - via endpoint configuration' do
56
+ it 'uses the default cache' do
57
+ DHC.config.endpoint(:local, 'http://depay.fi', cache: true)
58
+ expect(default_cache).to receive(:fetch)
59
+ expect(default_cache).to receive(:write)
60
+ DHC.get(:local)
61
+ end
62
+
63
+ it 'uses the provided cache' do
64
+ options = { cache: { use: CacheMock.new } }
65
+ DHC.config.endpoint(:local, 'http://depay.fi', options)
66
+ expect_any_instance_of(CacheMock).to receive(:fetch)
67
+ expect_any_instance_of(CacheMock).to receive(:write)
68
+ DHC.get(:local)
69
+ end
70
+
71
+ it 'cache options are properly forwarded to the cache' do
72
+ cache_options = { expires_in: 5.minutes, race_condition_ttl: 15.seconds }
73
+ DHC.config.endpoint(:local, 'http://depay.fi', cache: cache_options)
74
+ expect(default_cache).to receive(:write).with(anything, anything, cache_options)
75
+ DHC.get(:local)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::Caching do
6
+ context 'parameters' do
7
+ before(:each) do
8
+ DHC.config.interceptors = [DHC::Caching]
9
+ Rails.cache.clear
10
+ end
11
+
12
+ it 'considers parameters when writing/reading from cache' do
13
+ DHC.config.endpoint(:local, 'http://depay.fi', cache: true)
14
+ stub_request(:get, 'http://depay.fi').to_return(status: 200, body: 'The Website')
15
+ stub_request(:get, 'http://depay.fi?location=zuerich').to_return(status: 200, body: 'The Website for Zuerich')
16
+ expect(
17
+ DHC.get(:local).body
18
+ ).to eq 'The Website'
19
+ expect(
20
+ DHC.get(:local, params: { location: 'zuerich' }).body
21
+ ).to eq 'The Website for Zuerich'
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,29 @@
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.config.endpoint(:local, 'http://depay.fi', cache: true)
9
+ Rails.cache.clear
10
+ # leverage the Typhoeus internal mock attribute in order to get Typhoeus evaluate the return_code
11
+ # lib/typhoeus/response/status.rb:48
12
+ allow_any_instance_of(Typhoeus::Response).to receive(:mock).and_return(false)
13
+ end
14
+
15
+ let!(:stub) { stub_request(:get, 'http://depay.fi').to_return(status: 200, body: 'The Website') }
16
+
17
+ it 'provides the correct response status for responses from cache' do
18
+ stub
19
+ # the real request provides the return_code
20
+ allow_any_instance_of(Typhoeus::Response).to receive(:options)
21
+ .and_return(code: 200, status_message: '', body: 'The Website', headers: nil, return_code: :ok)
22
+ response = DHC.get(:local)
23
+ expect(response.success?).to eq true
24
+ # the cached response should get it from the cache
25
+ allow_any_instance_of(Typhoeus::Response).to receive(:options).and_call_original
26
+ cached_response = DHC.get(:local)
27
+ expect(cached_response.success?).to eq true
28
+ end
29
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::Caching do
6
+ context 'to_cache' do
7
+ it 'returns a marshalable object to store in the cache' do
8
+ expect do
9
+ response = Typhoeus::Response.new(headers: { 'Accept' => 'application/json' })
10
+ Marshal.dump(
11
+ DHC::Caching.new(response).send(:to_cache, response)
12
+ )
13
+ end.not_to raise_error
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC do
6
+ context 'default interceptors' do
7
+ before(:each) do
8
+ DHC.configure {}
9
+ end
10
+
11
+ it 'alwayses return a list for default interceptors' do
12
+ expect(DHC.config.interceptors).to eq []
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::DefaultTimeout do
6
+ before(:each) do
7
+ DHC.config.interceptors = [DHC::DefaultTimeout]
8
+ DHC::DefaultTimeout.timeout = nil
9
+ DHC::DefaultTimeout.connecttimeout = nil
10
+ end
11
+
12
+ let(:stub) { stub_request(:get, 'http://depay.fi').to_return(status: 200, body: 'The Website') }
13
+
14
+ it 'applies default timeouts to all requests made' do
15
+ stub
16
+ expect_any_instance_of(Ethon::Easy).to receive(:http_request)
17
+ .with(anything, anything, hash_including(timeout: 15, connecttimeout: 2)).and_call_original
18
+ DHC.get('http://depay.fi')
19
+ end
20
+
21
+ context 'with changed default timesouts' do
22
+ before(:each) do
23
+ DHC::DefaultTimeout.timeout = 10
24
+ DHC::DefaultTimeout.connecttimeout = 3
25
+ end
26
+
27
+ it 'applies custom default timeouts to all requests made' do
28
+ stub
29
+ expect_any_instance_of(Ethon::Easy).to receive(:http_request)
30
+ .with(anything, anything, hash_including(timeout: 10, connecttimeout: 3)).and_call_original
31
+ DHC.get('http://depay.fi')
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,30 @@
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
+ end
10
+
11
+ class AnotherInterceptor < DHC::Interceptor
12
+ end
13
+ end
14
+
15
+ it 'performs interceptor when they are set globally' do
16
+ DHC.configure { |c| c.interceptors = [SomeInterceptor] }
17
+ expect_any_instance_of(SomeInterceptor).to receive(:before_request)
18
+ stub_request(:get, 'http://depay.fi')
19
+ DHC.get('http://depay.fi')
20
+ end
21
+
22
+ it 'overrides interceptors on request level' do
23
+ DHC.configure { |c| c.interceptors = [SomeInterceptor] }
24
+ expect_any_instance_of(AnotherInterceptor).to receive(:before_request)
25
+ expect_any_instance_of(SomeInterceptor).not_to receive(:before_request)
26
+ stub_request(:get, 'http://depay.fi')
27
+ DHC.get('http://depay.fi', interceptors: [AnotherInterceptor])
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,19 @@
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
+ end
10
+ end
11
+
12
+ it 'does not dup' do
13
+ options = { interceptors: [SomeInterceptor] }
14
+ expect(
15
+ options.deep_dup[:interceptors].include?(SomeInterceptor)
16
+ ).to eq true
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::Logging do
6
+ let(:logger) { spy('logger') }
7
+
8
+ before(:each) do
9
+ DHC.config.interceptors = [DHC::Logging]
10
+ DHC::Logging.logger = logger
11
+ stub_request(:get, 'http://depay.fi').to_return(status: 200)
12
+ end
13
+
14
+ it 'does log information before and after every request made with DHC' do
15
+ DHC.get('http://depay.fi')
16
+ expect(logger).to have_received(:info).once.with(
17
+ %r{Before DHC request <\d+> GET http://depay.fi at \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+\d{2}:\d{2} Params={} Headers={.*?}}
18
+ )
19
+ expect(logger).to have_received(:info).once.with(
20
+ %r{After DHC response for request <\d+> GET http://depay.fi at \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+\d{2}:\d{2} Time=0ms URL=http://depay.fi:80/}
21
+ )
22
+ end
23
+
24
+ context 'source' do
25
+ let(:source) { '/Users/Sebastian/DHC/test.rb' }
26
+
27
+ it 'does log the source if provided as option' do
28
+ DHC.get('http://depay.fi', source: source)
29
+ expect(logger).to have_received(:info).once.with(
30
+ %r{Before DHC request <\d+> GET http://depay.fi at \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+\d{2}:\d{2} Params={} Headers={.*?} \nCalled from #{source}}
31
+ )
32
+ expect(logger).to have_received(:info).once.with(
33
+ %r{After DHC response for request <\d+> GET http://depay.fi at \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+\d{2}:\d{2} Time=0ms URL=http://depay.fi:80/ \nCalled from #{source}}
34
+ )
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHC::Monitoring do
6
+ let(:stub) do
7
+ stub_request(:get, 'http://depay.fi').to_return(status: 200, body: 'The Website')
8
+ end
9
+
10
+ module Statsd
11
+ def self.count(_path, _value); end
12
+
13
+ def self.timing(_path, _value); end
14
+ end
15
+
16
+ before(:each) do
17
+ DHC::Monitoring.statsd = Statsd
18
+ Rails.cache.clear
19
+ allow(Statsd).to receive(:count).with('dhc.dummy.test.depay_fi.get.before_request', 1)
20
+ allow(Statsd).to receive(:count).with('dhc.dummy.test.depay_fi.get.count', 1)
21
+ allow(Statsd).to receive(:count).with('dhc.dummy.test.depay_fi.get.after_request', 1)
22
+ allow(Statsd).to receive(:count).with('dhc.dummy.test.depay_fi.get.200', 1)
23
+ end
24
+
25
+ context 'interceptors configured correctly' do
26
+ before do
27
+ DHC.config.interceptors = [DHC::Caching, DHC::Monitoring]
28
+ end
29
+
30
+ context 'requesting with cache option' do
31
+ it 'monitors miss/hit for caching' do
32
+ stub
33
+ expect(Statsd).to receive(:count).with('dhc.dummy.test.depay_fi.get.cache.miss', 1)
34
+ expect(Statsd).to receive(:count).with('dhc.dummy.test.depay_fi.get.cache.hit', 1)
35
+ DHC.get('http://depay.fi', cache: true)
36
+ DHC.get('http://depay.fi', cache: true)
37
+ end
38
+ end
39
+
40
+ context 'request uncached' do
41
+ it 'requesting without cache option' do
42
+ stub
43
+ expect(Statsd).not_to receive(:count).with('dhc.dummy.test.depay_fi.get.cache.miss', 1)
44
+ expect(Statsd).not_to receive(:count).with('dhc.dummy.test.depay_fi.get.cache.hit', 1)
45
+ DHC.get('http://depay.fi')
46
+ DHC.get('http://depay.fi')
47
+ end
48
+ end
49
+ end
50
+
51
+ context 'wrong interceptor order' do
52
+ before(:each) do
53
+ DHC.config.interceptors = [DHC::Monitoring, DHC::Caching] # monitoring needs to be after Caching
54
+ end
55
+
56
+ it 'does monitors miss/hit for caching and warns about wrong order of interceptors' do
57
+ stub
58
+ expect(Statsd).not_to receive(:count).with('dhc.dummy.test.depay_fi.get.cache.miss', 1)
59
+ expect(Statsd).not_to receive(:count).with('dhc.dummy.test.depay_fi.get.cache.hit', 1)
60
+ expect(-> {
61
+ DHC.get('http://depay.fi', cache: true)
62
+ DHC.get('http://depay.fi', cache: true)
63
+ }).to output("[WARNING] Your interceptors must include DHC::Caching and DHC::Monitoring and also in that order.\n[WARNING] Your interceptors must include DHC::Caching and DHC::Monitoring and also in that order.\n").to_stderr
64
+ end
65
+ end
66
+ end