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,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/500.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>We're sorry, but something went wrong.</h1>
62
+ </div>
63
+ <p>If you are the application owner check the logs for more information.</p>
64
+ </div>
65
+ </body>
66
+ </html>
File without changes
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC::Endpoint do
6
+ context 'compile' do
7
+ it 'uses parameters for interpolation' do
8
+ endpoint = LHC::Endpoint.new('{+datastore}/v2/{campaign_id}/feedbacks')
9
+ expect(
10
+ endpoint.compile(datastore: 'http://datastore', campaign_id: 'abc')
11
+ ).to eq "http://datastore/v2/abc/feedbacks"
12
+ end
13
+
14
+ it 'compiles when templates contain dots' do
15
+ endpoint = LHC::Endpoint.new('{+datastore}/entries/{id}.json')
16
+ expect(
17
+ endpoint.compile(datastore: 'http://datastore', id: 123)
18
+ ).to eq "http://datastore/entries/123.json"
19
+ end
20
+
21
+ it 'compiles complex urls containing all sort of characters' do
22
+ endpoint = LHC::Endpoint.new('{+ads}/?adrawdata/3.0/1108.1/2844859/0/0/header=yes;cookie=no;adct=204;alias={region_id}{lang}{product_type}{product};key=cat={category_id}')
23
+ expect(
24
+ endpoint.compile(ads: 'http://ads', region_id: 291, lang: 'de', product_type: 'ABC', product: 'xyz', category_id: 312)
25
+ ).to eq 'http://ads/?adrawdata/3.0/1108.1/2844859/0/0/header=yes;cookie=no;adct=204;alias=291deABCxyz;key=cat=312'
26
+ end
27
+
28
+ it 'compiles complex urls containing &' do
29
+ endpoint = LHC::Endpoint.new('http://{+weather_search_host}/forecast?format=json&period=hour&limit={limit}&areas={+zipcodes}&when={when}')
30
+ expect(
31
+ endpoint.compile(weather_search_host: 'weather', limit: 5, zipcodes: [8005, 8004].join(','), when: 'today')
32
+ ).to eq 'http://weather/forecast?format=json&period=hour&limit=5&areas=8005,8004&when=today'
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC::Endpoint do
6
+ context 'match' do
7
+ context 'matching' do
8
+ {
9
+ '{+datastore}/v2/places' => 'http://local.ch:8082/v2/places',
10
+ '{+datastore}/v2/places/{id}' => 'http://local.ch:8082/v2/places/ZW9OJyrbt4OZE9ueu80w-A',
11
+ '{+datastore}/v2/places/{namespace}/{id}' => 'http://local.ch:8082/v2/places/switzerland/ZW9OJyrbt',
12
+ '{+datastore}/addresses/{id}' => 'http://local.ch/addresses/123',
13
+ 'http://local.ch/addresses/{id}' => 'http://local.ch/addresses/123',
14
+ '{+datastore}/customers/{id}/addresses' => 'http://local.ch:80/server/rest/v1/customers/123/addresses',
15
+ '{+datastore}/entries/{id}.json' => 'http://local.ch/entries/123.json',
16
+ '{+datastore}/places/{place_id}/feedbacks' => 'http://local.ch/places/1/feedbacks?limit=10&offset=0',
17
+ 'http://local.ch/places/1/feedbacks' => 'http://local.ch/places/1/feedbacks?lang=en',
18
+ 'http://local.ch/places/1/feedbacks.json' => 'http://local.ch/places/1/feedbacks.json?lang=en'
19
+ }.each do |template, url|
20
+ it "#{url} matches #{template}" do
21
+ expect(LHC::Endpoint.match?(url, template)).to be(true)
22
+ end
23
+ end
24
+ end
25
+
26
+ context 'not matching' do
27
+ {
28
+ '{+datastore}/v2/places' => 'http://local.ch:8082/v2/places/ZW9OJyrbt4OZE9ueu80w-A',
29
+ '{+datastore}/{campaign_id}/feedbacks' => 'http://datastore.local.ch/feedbacks',
30
+ '{+datastore}/customers/{id}' => 'http://local.ch:80/server/rest/v1/customers/123/addresses',
31
+ '{+datastore}/entries/{id}' => 'http://local.ch/entries/123.json'
32
+ }.each do |template, url|
33
+ it "#{url} should not match #{template}" do
34
+ expect(
35
+ LHC::Endpoint.match?(url, template)
36
+ ).to be(false)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC::Endpoint do
6
+ context 'placeholders' do
7
+ it 'returns all placeholders alphabetically sorted' do
8
+ endpoint = LHC::Endpoint.new('{+datastore}/v2/{campaign_id}/feedbacks')
9
+ expect(
10
+ endpoint.placeholders
11
+ ).to eq [:campaign_id, :datastore]
12
+ end
13
+
14
+ it 'allows basic auth token in url, like used on github' do
15
+ stub_request(:get, "https://d123token:@api.github.com/search")
16
+ .to_return(body: {}.to_json)
17
+ expect(-> {
18
+ LHC.get("https://d123token:@api.github.com/search")
19
+ }).not_to raise_error
20
+ end
21
+
22
+ it 'allows complete basic auth (username password) in url, like used for the gemserer' do
23
+ stub_request(:get, "https://name:password@gemserver.com")
24
+ .to_return(body: {}.to_json)
25
+ expect(-> {
26
+ LHC.get("https://name:password@gemserver.com")
27
+ }).not_to raise_error
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC::Endpoint do
6
+ it 'removes params used for interpolation' do
7
+ params = {
8
+ datastore: 'http://datastore',
9
+ campaign_id: 'abc',
10
+ has_reviews: true
11
+ }
12
+ endpoint = LHC::Endpoint.new('{+datastore}/v2/{campaign_id}/feedbacks')
13
+ removed = endpoint.remove_interpolated_params!(params)
14
+ expect(params).to eq(has_reviews: true)
15
+ expect(removed).to eq(datastore: 'http://datastore', campaign_id: 'abc')
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC::Endpoint do
6
+ context 'values_as_params' do
7
+ [
8
+ ['{+datastore}/v2/places', 'http://local.ch:8082/v2/places', {
9
+ datastore: 'http://local.ch:8082'
10
+ }],
11
+ ['{+datastore}/v2/places/{id}', 'http://local.ch:8082/v2/places/ZW9OJyrbt4OZE9ueu80w-A', {
12
+ datastore: 'http://local.ch:8082',
13
+ id: 'ZW9OJyrbt4OZE9ueu80w-A'
14
+ }],
15
+ ['{+datastore}/v2/places/{namespace}/{id}', 'http://local.ch:8082/v2/places/switzerland/ZW9OJyrbt', {
16
+ datastore: 'http://local.ch:8082',
17
+ namespace: 'switzerland',
18
+ id: 'ZW9OJyrbt'
19
+ }]
20
+ ].each do |example|
21
+ template = example[0]
22
+ url = example[1]
23
+ params = example[2]
24
+
25
+ it "for the template #{template} it extracts #{params.keys.join(', ')} from the url" do
26
+ extracted = LHC::Endpoint.values_as_params(template, url)
27
+ expect(extracted).to eq(params)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC::Error do
6
+ it 'does not dup' do
7
+ options = { errors: [described_class] }
8
+ expect(
9
+ options.deep_dup[:errors].include?(described_class)
10
+ ).to eq true
11
+ end
12
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC::Error do
6
+ def response(code, timedout = false)
7
+ LHC::Response.new(OpenStruct.new(code: code, 'timed_out?' => timedout), nil)
8
+ end
9
+
10
+ context 'find' do
11
+ it 'finds error class by status code' do
12
+ expect(LHC::Error.find(response('0', true))).to eq LHC::Timeout
13
+ expect(LHC::Error.find(response('400'))).to eq LHC::BadRequest
14
+ expect(LHC::Error.find(response('401'))).to eq LHC::Unauthorized
15
+ expect(LHC::Error.find(response('402'))).to eq LHC::PaymentRequired
16
+ expect(LHC::Error.find(response('403'))).to eq LHC::Forbidden
17
+ expect(LHC::Error.find(response('403'))).to eq LHC::Forbidden
18
+ expect(LHC::Error.find(response('404'))).to eq LHC::NotFound
19
+ expect(LHC::Error.find(response('405'))).to eq LHC::MethodNotAllowed
20
+ expect(LHC::Error.find(response('406'))).to eq LHC::NotAcceptable
21
+ expect(LHC::Error.find(response('407'))).to eq LHC::ProxyAuthenticationRequired
22
+ expect(LHC::Error.find(response('408'))).to eq LHC::RequestTimeout
23
+ expect(LHC::Error.find(response('409'))).to eq LHC::Conflict
24
+ expect(LHC::Error.find(response('410'))).to eq LHC::Gone
25
+ expect(LHC::Error.find(response('411'))).to eq LHC::LengthRequired
26
+ expect(LHC::Error.find(response('412'))).to eq LHC::PreconditionFailed
27
+ expect(LHC::Error.find(response('413'))).to eq LHC::RequestEntityTooLarge
28
+ expect(LHC::Error.find(response('414'))).to eq LHC::RequestUriToLong
29
+ expect(LHC::Error.find(response('415'))).to eq LHC::UnsupportedMediaType
30
+ expect(LHC::Error.find(response('416'))).to eq LHC::RequestedRangeNotSatisfiable
31
+ expect(LHC::Error.find(response('417'))).to eq LHC::ExpectationFailed
32
+ expect(LHC::Error.find(response('422'))).to eq LHC::UnprocessableEntity
33
+ expect(LHC::Error.find(response('423'))).to eq LHC::Locked
34
+ expect(LHC::Error.find(response('424'))).to eq LHC::FailedDependency
35
+ expect(LHC::Error.find(response('426'))).to eq LHC::UpgradeRequired
36
+ expect(LHC::Error.find(response('500'))).to eq LHC::InternalServerError
37
+ expect(LHC::Error.find(response('501'))).to eq LHC::NotImplemented
38
+ expect(LHC::Error.find(response('502'))).to eq LHC::BadGateway
39
+ expect(LHC::Error.find(response('503'))).to eq LHC::ServiceUnavailable
40
+ expect(LHC::Error.find(response('504'))).to eq LHC::GatewayTimeout
41
+ expect(LHC::Error.find(response('505'))).to eq LHC::HttpVersionNotSupported
42
+ expect(LHC::Error.find(response('507'))).to eq LHC::InsufficientStorage
43
+ expect(LHC::Error.find(response('510'))).to eq LHC::NotExtended
44
+ end
45
+
46
+ it 'finds error class also by extended status codes' do
47
+ expect(LHC::Error.find(response('40001'))).to eq LHC::BadRequest
48
+ expect(LHC::Error.find(response('50002'))).to eq LHC::InternalServerError
49
+ end
50
+
51
+ it 'returns UnknownError if no specific error was found' do
52
+ expect(LHC::Error.find(response('0'))).to eq LHC::UnknownError
53
+ expect(LHC::Error.find(response(''))).to eq LHC::UnknownError
54
+ expect(LHC::Error.find(response('600'))).to eq LHC::UnknownError
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC::Error do
6
+ context 'response' do
7
+ it 'throws timeout exception in case of a timeout' do
8
+ stub_request(:any, 'local.ch').to_return(status: 403)
9
+ begin
10
+ LHC.get('local.ch')
11
+ rescue => e
12
+ expect(e.response).to be_kind_of(LHC::Response)
13
+ expect(e.response.code).to eq 403
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC::Error do
6
+ context 'timeout' do
7
+ it 'throws timeout exception in case of a timeout' do
8
+ stub_request(:any, 'local.ch').to_timeout
9
+ expect(lambda {
10
+ LHC.get('local.ch')
11
+ }).to raise_error LHC::Timeout
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC::Error do
6
+ context 'to_s' do
7
+ let(:invalid) { (+"in\xc3lid").force_encoding('ASCII-8BIT') }
8
+ let(:valid) { "vælid" }
9
+
10
+ context 'check assumptions' do
11
+ it 'joining raises an error' do
12
+ expect { [valid, invalid].join }.to raise_error Encoding::CompatibilityError
13
+ end
14
+ it 'interpolation raises an error' do
15
+ expect { "#{valid} #{invalid}" }.to raise_error Encoding::CompatibilityError
16
+ end
17
+ it 'to_json on an array raises an error' do
18
+ expect { [valid, invalid].to_json }.to raise_error Encoding::UndefinedConversionError
19
+ end
20
+
21
+ it 'to_s on a hash does not raise an error' do
22
+ expect { { valid: valid, invalid: invalid }.to_s }.not_to raise_error
23
+ end
24
+
25
+ it 'to_json on a hash does raise an error' do
26
+ expect { { valid: valid, invalid: invalid }.to_json }.to raise_error Encoding::UndefinedConversionError
27
+ end
28
+ end
29
+
30
+ it 'invalid body, valid message' do
31
+ stub_request(:get, 'http://local.ch')
32
+ .to_return(status: 200, body: "{ text : '#{invalid}' }")
33
+ response = LHC.get('http://local.ch')
34
+ expect { LHC::Error.new(valid, response).to_s }.not_to raise_error # Encoding::CompatibilityError
35
+ end
36
+
37
+ it 'valid body, invalid message' do
38
+ stub_request(:get, 'http://local.ch')
39
+ .to_return(status: 200, body: "{ text : '#{valid}' }")
40
+ response = LHC.get('http://local.ch')
41
+ expect { LHC::Error.new(invalid, response).to_s }.not_to raise_error # Encoding::CompatibilityError
42
+ end
43
+ # the other cases cannot be tested (for example what happens if the headers contain invalid data)
44
+ # because the mocking framework triggers the encoding error already
45
+
46
+ context 'some mocked response' do
47
+ let(:request) do
48
+ double('request',
49
+ method: 'GET',
50
+ url: 'http://example.com/sessions',
51
+ headers: { 'Bearer Token' => "aaaaaaaa-bbbb-cccc-dddd-eeee" },
52
+ options: { followlocation: true,
53
+ auth: { bearer: "aaaaaaaa-bbbb-cccc-dddd-eeee" },
54
+ params: { limit: 20 }, url: "http://example.com/sessions" })
55
+ end
56
+
57
+ let(:response) do
58
+ double('response',
59
+ request: request,
60
+ code: 500,
61
+ options: { return_code: :internal_error, response_headers: "" },
62
+ body: '{"status":500,"message":"undefined"}')
63
+ end
64
+
65
+ subject { LHC::Error.new('The error message', response) }
66
+
67
+ it 'produces correct debug output' do
68
+ expect(subject.to_s.split("\n")).to eq(<<-MSG.strip_heredoc.split("\n"))
69
+ GET http://example.com/sessions
70
+ Options: {:followlocation=>true, :auth=>{:bearer=>"aaaaaaaa-bbbb-cccc-dddd-eeee"}, :params=>{:limit=>20}, :url=>"http://example.com/sessions"}
71
+ Headers: {"Bearer Token"=>"aaaaaaaa-bbbb-cccc-dddd-eeee"}
72
+ Response Code: 500 (internal_error)
73
+ Response Options: {:return_code=>:internal_error, :response_headers=>""}
74
+ {"status":500,"message":"undefined"}
75
+ The error message
76
+ MSG
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC do
6
+ include ActionDispatch::TestProcess
7
+
8
+ context 'form' do
9
+ it 'formats requests to be application/x-www-form-urlencoded' do
10
+ stub = stub_request(:post, 'http://local.ch/')
11
+ .with(body: 'client_id=1234&client_secret=4567&grant_type=client_credentials')
12
+ .with(headers: { 'Content-Type': 'application/x-www-form-urlencoded' })
13
+ .to_return(status: 200)
14
+
15
+ LHC.form.post(
16
+ 'http://local.ch',
17
+ body: {
18
+ client_id: '1234',
19
+ client_secret: '4567',
20
+ grant_type: 'client_credentials'
21
+ }
22
+ )
23
+
24
+ expect(stub).to have_been_requested
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC do
6
+ context 'formats' do
7
+ it 'adds Content-Type, Accept and Accept-Charset Headers to the request' do
8
+ stub_request(:get, "http://local.ch/")
9
+ .with(headers: {
10
+ 'Accept' => 'application/json,application/vnd.api+json',
11
+ 'Content-Type' => 'application/json; charset=utf-8',
12
+ 'Accept-Charset' => 'utf-8'
13
+ })
14
+ .to_return(body: {}.to_json)
15
+ LHC.json.get('http://local.ch')
16
+ end
17
+
18
+ context 'header key as symbol' do
19
+ it 'raises an error when trying to set content-type header even though the format is used' do
20
+ expect(lambda {
21
+ LHC.post(
22
+ 'http://local.ch',
23
+ headers: {
24
+ 'Content-Type': 'multipart/form-data'
25
+ }
26
+ )
27
+ }).to raise_error 'Content-Type header is not allowed for formatted requests!\nSee https://github.com/local-ch/lhc#formats for more information.'
28
+ end
29
+
30
+ it 'raises an error when trying to set accept header even though the format is used' do
31
+ expect(lambda {
32
+ LHC.post(
33
+ 'http://local.ch',
34
+ headers: {
35
+ 'Accept': 'multipart/form-data'
36
+ }
37
+ )
38
+ }).to raise_error 'Accept header is not allowed for formatted requests!\nSee https://github.com/local-ch/lhc#formats for more information.'
39
+ end
40
+ end
41
+
42
+ context 'header key as string' do
43
+ it 'raises an error when trying to set content-type header even though the format is used' do
44
+ expect(lambda {
45
+ LHC.post(
46
+ 'http://local.ch',
47
+ headers: {
48
+ 'Content-Type' => 'multipart/form-data'
49
+ }
50
+ )
51
+ }).to raise_error 'Content-Type header is not allowed for formatted requests!\nSee https://github.com/local-ch/lhc#formats for more information.'
52
+ end
53
+
54
+ it 'raises an error when trying to set accept header even though the format is used' do
55
+ expect(lambda {
56
+ LHC.post(
57
+ 'http://local.ch',
58
+ headers: {
59
+ 'Accept' => 'multipart/form-data'
60
+ }
61
+ )
62
+ }).to raise_error 'Accept header is not allowed for formatted requests!\nSee https://github.com/local-ch/lhc#formats for more information.'
63
+ end
64
+ end
65
+ end
66
+ end