restify 2.1.0 → 3.0.0.rc2

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 (213) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +48 -0
  3. data/README.md +30 -4
  4. data/lib/restify/adapter/base.rb +10 -1
  5. data/lib/restify/adapter/ethon.rb +462 -0
  6. data/lib/restify/adapter/loop_lock.rb +124 -0
  7. data/lib/restify/adapter/telemetry.rb +6 -4
  8. data/lib/restify/adapter/typhoeus.rb +48 -23
  9. data/lib/restify/context.rb +5 -5
  10. data/lib/restify/error.rb +13 -0
  11. data/lib/restify/global.rb +9 -30
  12. data/lib/restify/logging.rb +10 -4
  13. data/lib/restify/processors/base/parsing.rb +13 -1
  14. data/lib/restify/promise.rb +20 -2
  15. data/lib/restify/registry.rb +2 -2
  16. data/lib/restify/relation.rb +8 -8
  17. data/lib/restify/request.rb +2 -2
  18. data/lib/restify/resource.rb +20 -2
  19. data/lib/restify/response.rb +39 -3
  20. data/lib/restify/timeout.rb +11 -5
  21. data/lib/restify/version.rb +3 -3
  22. data/lib/restify/webmock.rb +126 -0
  23. data/lib/restify.rb +5 -1
  24. data/spec/restify/adapter/ethon_spec.rb +263 -0
  25. data/spec/restify/adapter/loop_lock_spec.rb +144 -0
  26. data/spec/restify/adapter/telemetry_spec.rb +61 -0
  27. data/spec/restify/adapter/typhoeus_spec.rb +26 -0
  28. data/spec/restify/cache_spec.rb +1 -3
  29. data/spec/restify/context_spec.rb +55 -0
  30. data/spec/restify/features/protocols_spec.rb +28 -0
  31. data/spec/restify/features/redirects_spec.rb +45 -0
  32. data/spec/restify/features/request_errors_spec.rb +1 -1
  33. data/spec/restify/features/response_bodies_spec.rb +45 -0
  34. data/spec/restify/features/response_encoding_spec.rb +82 -0
  35. data/spec/restify/features/response_errors_spec.rb +22 -1
  36. data/spec/restify/features/webmock_spec.rb +92 -1
  37. data/spec/restify/global_spec.rb +78 -10
  38. data/spec/restify/processors/json_spec.rb +36 -0
  39. data/spec/restify/processors/msgpack_spec.rb +36 -0
  40. data/spec/restify/promise_spec.rb +82 -0
  41. data/spec/restify/relation_spec.rb +2 -6
  42. data/spec/restify/request_spec.rb +26 -0
  43. data/spec/restify/timeout_spec.rb +11 -0
  44. data/spec/spec_helper.rb +10 -6
  45. data/spec/support/stub_server.rb +4 -4
  46. metadata +26 -209
  47. data/vendor/bundle/ruby/4.0.0/bundler/gems/my-rubocop-2b861962124a/LICENSE +0 -21
  48. data/vendor/bundle/ruby/4.0.0/bundler/gems/my-rubocop-2b861962124a/README.md +0 -23
  49. data/vendor/bundle/ruby/4.0.0/gems/activesupport-8.1.3/CHANGELOG.md +0 -561
  50. data/vendor/bundle/ruby/4.0.0/gems/activesupport-8.1.3/MIT-LICENSE +0 -20
  51. data/vendor/bundle/ruby/4.0.0/gems/activesupport-8.1.3/README.rdoc +0 -40
  52. data/vendor/bundle/ruby/4.0.0/gems/addressable-2.9.0/CHANGELOG.md +0 -326
  53. data/vendor/bundle/ruby/4.0.0/gems/addressable-2.9.0/LICENSE.txt +0 -202
  54. data/vendor/bundle/ruby/4.0.0/gems/addressable-2.9.0/README.md +0 -121
  55. data/vendor/bundle/ruby/4.0.0/gems/ast-2.4.3/LICENSE.MIT +0 -20
  56. data/vendor/bundle/ruby/4.0.0/gems/ast-2.4.3/README.YARD.md +0 -12
  57. data/vendor/bundle/ruby/4.0.0/gems/base64-0.3.0/README.md +0 -48
  58. data/vendor/bundle/ruby/4.0.0/gems/bigdecimal-4.1.2/LICENSE +0 -56
  59. data/vendor/bundle/ruby/4.0.0/gems/concurrent-ruby-1.3.7/CHANGELOG.md +0 -615
  60. data/vendor/bundle/ruby/4.0.0/gems/concurrent-ruby-1.3.7/LICENSE.txt +0 -21
  61. data/vendor/bundle/ruby/4.0.0/gems/concurrent-ruby-1.3.7/README.md +0 -409
  62. data/vendor/bundle/ruby/4.0.0/gems/connection_pool-3.0.2/LICENSE +0 -20
  63. data/vendor/bundle/ruby/4.0.0/gems/connection_pool-3.0.2/README.md +0 -182
  64. data/vendor/bundle/ruby/4.0.0/gems/crack-1.0.1/LICENSE +0 -20
  65. data/vendor/bundle/ruby/4.0.0/gems/crack-1.0.1/README.md +0 -43
  66. data/vendor/bundle/ruby/4.0.0/gems/diff-lcs-1.6.2/CHANGELOG.md +0 -518
  67. data/vendor/bundle/ruby/4.0.0/gems/diff-lcs-1.6.2/README.md +0 -92
  68. data/vendor/bundle/ruby/4.0.0/gems/docile-1.4.1/LICENSE +0 -21
  69. data/vendor/bundle/ruby/4.0.0/gems/docile-1.4.1/README.md +0 -409
  70. data/vendor/bundle/ruby/4.0.0/gems/drb-2.2.3/LICENSE.txt +0 -22
  71. data/vendor/bundle/ruby/4.0.0/gems/ethon-0.18.0/CHANGELOG.md +0 -417
  72. data/vendor/bundle/ruby/4.0.0/gems/ethon-0.18.0/LICENSE +0 -20
  73. data/vendor/bundle/ruby/4.0.0/gems/ethon-0.18.0/README.md +0 -118
  74. data/vendor/bundle/ruby/4.0.0/gems/ffi-1.17.4-x86_64-linux-gnu/CHANGELOG.md +0 -507
  75. data/vendor/bundle/ruby/4.0.0/gems/ffi-1.17.4-x86_64-linux-gnu/LICENSE +0 -24
  76. data/vendor/bundle/ruby/4.0.0/gems/ffi-1.17.4-x86_64-linux-gnu/LICENSE.SPECS +0 -22
  77. data/vendor/bundle/ruby/4.0.0/gems/ffi-1.17.4-x86_64-linux-gnu/README.md +0 -138
  78. data/vendor/bundle/ruby/4.0.0/gems/hashdiff-1.2.1/LICENSE +0 -19
  79. data/vendor/bundle/ruby/4.0.0/gems/hashdiff-1.2.1/README.md +0 -323
  80. data/vendor/bundle/ruby/4.0.0/gems/hashdiff-1.2.1/changelog.md +0 -127
  81. data/vendor/bundle/ruby/4.0.0/gems/hitimes-3.2.0/LICENSE.txt +0 -16
  82. data/vendor/bundle/ruby/4.0.0/gems/hitimes-3.2.0/README.md +0 -187
  83. data/vendor/bundle/ruby/4.0.0/gems/i18n-1.15.2/MIT-LICENSE +0 -20
  84. data/vendor/bundle/ruby/4.0.0/gems/i18n-1.15.2/README.md +0 -139
  85. data/vendor/bundle/ruby/4.0.0/gems/json-2.19.9/README.md +0 -310
  86. data/vendor/bundle/ruby/4.0.0/gems/language_server-protocol-3.17.0.5/LICENSE.txt +0 -21
  87. data/vendor/bundle/ruby/4.0.0/gems/language_server-protocol-3.17.0.5/README.md +0 -88
  88. data/vendor/bundle/ruby/4.0.0/gems/lint_roller-1.1.0/CHANGELOG.md +0 -15
  89. data/vendor/bundle/ruby/4.0.0/gems/lint_roller-1.1.0/LICENSE.txt +0 -21
  90. data/vendor/bundle/ruby/4.0.0/gems/lint_roller-1.1.0/README.md +0 -173
  91. data/vendor/bundle/ruby/4.0.0/gems/little-plugger-1.1.4/README.rdoc +0 -53
  92. data/vendor/bundle/ruby/4.0.0/gems/logger-1.7.0/README.md +0 -104
  93. data/vendor/bundle/ruby/4.0.0/gems/logging-2.4.0/LICENSE +0 -22
  94. data/vendor/bundle/ruby/4.0.0/gems/logging-2.4.0/README.md +0 -140
  95. data/vendor/bundle/ruby/4.0.0/gems/minitest-6.0.6/README.rdoc +0 -763
  96. data/vendor/bundle/ruby/4.0.0/gems/msgpack-1.8.3/ChangeLog +0 -383
  97. data/vendor/bundle/ruby/4.0.0/gems/msgpack-1.8.3/LICENSE +0 -177
  98. data/vendor/bundle/ruby/4.0.0/gems/msgpack-1.8.3/README.md +0 -302
  99. data/vendor/bundle/ruby/4.0.0/gems/multi_json-1.21.1/LICENSE.md +0 -20
  100. data/vendor/bundle/ruby/4.0.0/gems/multi_json-1.21.1/README.md +0 -281
  101. data/vendor/bundle/ruby/4.0.0/gems/nio4r-2.7.5/ext/libev/LICENSE +0 -37
  102. data/vendor/bundle/ruby/4.0.0/gems/nio4r-2.7.5/ext/libev/README +0 -59
  103. data/vendor/bundle/ruby/4.0.0/gems/nio4r-2.7.5/license.md +0 -80
  104. data/vendor/bundle/ruby/4.0.0/gems/nio4r-2.7.5/readme.md +0 -91
  105. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-api-1.10.0/CHANGELOG.md +0 -219
  106. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-api-1.10.0/LICENSE +0 -201
  107. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-api-1.10.0/README.md +0 -68
  108. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-common-0.25.0/CHANGELOG.md +0 -110
  109. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-common-0.25.0/LICENSE +0 -201
  110. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-common-0.25.0/README.md +0 -62
  111. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-base-0.26.1/CHANGELOG.md +0 -97
  112. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-base-0.26.1/LICENSE +0 -201
  113. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-base-0.26.1/README.md +0 -155
  114. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-ethon-0.29.0/CHANGELOG.md +0 -192
  115. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-ethon-0.29.0/LICENSE +0 -201
  116. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-ethon-0.29.0/README.md +0 -66
  117. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-registry-0.6.0/CHANGELOG.md +0 -31
  118. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-registry-0.6.0/LICENSE +0 -201
  119. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-registry-0.6.0/README.md +0 -40
  120. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-sdk-1.12.0/CHANGELOG.md +0 -302
  121. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-sdk-1.12.0/LICENSE +0 -201
  122. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-sdk-1.12.0/README.md +0 -101
  123. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-semantic_conventions-1.41.0/CHANGELOG.md +0 -65
  124. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-semantic_conventions-1.41.0/LICENSE +0 -201
  125. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-semantic_conventions-1.41.0/README.md +0 -115
  126. data/vendor/bundle/ruby/4.0.0/gems/parallel-2.1.0/MIT-LICENSE.txt +0 -20
  127. data/vendor/bundle/ruby/4.0.0/gems/parser-3.3.11.1/LICENSE.txt +0 -26
  128. data/vendor/bundle/ruby/4.0.0/gems/prism-1.9.0/CHANGELOG.md +0 -786
  129. data/vendor/bundle/ruby/4.0.0/gems/prism-1.9.0/LICENSE.md +0 -7
  130. data/vendor/bundle/ruby/4.0.0/gems/prism-1.9.0/README.md +0 -143
  131. data/vendor/bundle/ruby/4.0.0/gems/public_suffix-7.0.5/CHANGELOG.md +0 -649
  132. data/vendor/bundle/ruby/4.0.0/gems/public_suffix-7.0.5/LICENSE.txt +0 -22
  133. data/vendor/bundle/ruby/4.0.0/gems/public_suffix-7.0.5/README.md +0 -231
  134. data/vendor/bundle/ruby/4.0.0/gems/puma-8.0.2/LICENSE +0 -29
  135. data/vendor/bundle/ruby/4.0.0/gems/puma-8.0.2/README.md +0 -484
  136. data/vendor/bundle/ruby/4.0.0/gems/puma-8.0.2/docs/jungle/README.md +0 -9
  137. data/vendor/bundle/ruby/4.0.0/gems/puma-8.0.2/docs/jungle/rc.d/README.md +0 -74
  138. data/vendor/bundle/ruby/4.0.0/gems/racc-1.8.1/ChangeLog +0 -846
  139. data/vendor/bundle/ruby/4.0.0/gems/racc-1.8.1/README.ja.rdoc +0 -58
  140. data/vendor/bundle/ruby/4.0.0/gems/racc-1.8.1/README.rdoc +0 -60
  141. data/vendor/bundle/ruby/4.0.0/gems/rack-3.2.6/CHANGELOG.md +0 -1346
  142. data/vendor/bundle/ruby/4.0.0/gems/rack-3.2.6/MIT-LICENSE +0 -20
  143. data/vendor/bundle/ruby/4.0.0/gems/rack-3.2.6/README.md +0 -384
  144. data/vendor/bundle/ruby/4.0.0/gems/rainbow-3.1.1/Changelog.md +0 -101
  145. data/vendor/bundle/ruby/4.0.0/gems/rainbow-3.1.1/LICENSE +0 -20
  146. data/vendor/bundle/ruby/4.0.0/gems/rainbow-3.1.1/README.markdown +0 -227
  147. data/vendor/bundle/ruby/4.0.0/gems/rake-13.4.2/MIT-LICENSE +0 -21
  148. data/vendor/bundle/ruby/4.0.0/gems/rake-13.4.2/README.rdoc +0 -155
  149. data/vendor/bundle/ruby/4.0.0/gems/rake-release-1.4.0/LICENSE +0 -21
  150. data/vendor/bundle/ruby/4.0.0/gems/rake-release-1.4.0/README.md +0 -107
  151. data/vendor/bundle/ruby/4.0.0/gems/regexp_parser-2.12.0/LICENSE +0 -22
  152. data/vendor/bundle/ruby/4.0.0/gems/rexml-3.4.4/LICENSE.txt +0 -22
  153. data/vendor/bundle/ruby/4.0.0/gems/rexml-3.4.4/README.md +0 -57
  154. data/vendor/bundle/ruby/4.0.0/gems/rspec-3.13.2/LICENSE.md +0 -27
  155. data/vendor/bundle/ruby/4.0.0/gems/rspec-3.13.2/README.md +0 -47
  156. data/vendor/bundle/ruby/4.0.0/gems/rspec-core-3.13.6/Changelog.md +0 -2447
  157. data/vendor/bundle/ruby/4.0.0/gems/rspec-core-3.13.6/LICENSE.md +0 -26
  158. data/vendor/bundle/ruby/4.0.0/gems/rspec-core-3.13.6/README.md +0 -389
  159. data/vendor/bundle/ruby/4.0.0/gems/rspec-expectations-3.13.5/Changelog.md +0 -1366
  160. data/vendor/bundle/ruby/4.0.0/gems/rspec-expectations-3.13.5/LICENSE.md +0 -25
  161. data/vendor/bundle/ruby/4.0.0/gems/rspec-expectations-3.13.5/README.md +0 -326
  162. data/vendor/bundle/ruby/4.0.0/gems/rspec-mocks-3.13.8/Changelog.md +0 -1351
  163. data/vendor/bundle/ruby/4.0.0/gems/rspec-mocks-3.13.8/LICENSE.md +0 -25
  164. data/vendor/bundle/ruby/4.0.0/gems/rspec-mocks-3.13.8/README.md +0 -465
  165. data/vendor/bundle/ruby/4.0.0/gems/rspec-support-3.13.7/Changelog.md +0 -444
  166. data/vendor/bundle/ruby/4.0.0/gems/rspec-support-3.13.7/LICENSE.md +0 -23
  167. data/vendor/bundle/ruby/4.0.0/gems/rspec-support-3.13.7/README.md +0 -40
  168. data/vendor/bundle/ruby/4.0.0/gems/rubocop-1.88.0/LICENSE.txt +0 -20
  169. data/vendor/bundle/ruby/4.0.0/gems/rubocop-1.88.0/README.md +0 -255
  170. data/vendor/bundle/ruby/4.0.0/gems/rubocop-ast-1.49.1/LICENSE.txt +0 -20
  171. data/vendor/bundle/ruby/4.0.0/gems/rubocop-ast-1.49.1/README.md +0 -54
  172. data/vendor/bundle/ruby/4.0.0/gems/rubocop-capybara-2.23.0/CHANGELOG.md +0 -116
  173. data/vendor/bundle/ruby/4.0.0/gems/rubocop-capybara-2.23.0/MIT-LICENSE.md +0 -21
  174. data/vendor/bundle/ruby/4.0.0/gems/rubocop-capybara-2.23.0/README.md +0 -91
  175. data/vendor/bundle/ruby/4.0.0/gems/rubocop-factory_bot-2.28.0/CHANGELOG.md +0 -135
  176. data/vendor/bundle/ruby/4.0.0/gems/rubocop-factory_bot-2.28.0/MIT-LICENSE.md +0 -21
  177. data/vendor/bundle/ruby/4.0.0/gems/rubocop-factory_bot-2.28.0/README.md +0 -91
  178. data/vendor/bundle/ruby/4.0.0/gems/rubocop-performance-1.26.1/LICENSE.txt +0 -20
  179. data/vendor/bundle/ruby/4.0.0/gems/rubocop-performance-1.26.1/README.md +0 -100
  180. data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec-3.10.2/CHANGELOG.md +0 -1136
  181. data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec-3.10.2/MIT-LICENSE.md +0 -21
  182. data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec-3.10.2/README.md +0 -112
  183. data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec_rails-2.32.0/CHANGELOG.md +0 -98
  184. data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec_rails-2.32.0/MIT-LICENSE.md +0 -21
  185. data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec_rails-2.32.0/README.md +0 -93
  186. data/vendor/bundle/ruby/4.0.0/gems/ruby-progressbar-1.13.0/LICENSE.txt +0 -19
  187. data/vendor/bundle/ruby/4.0.0/gems/ruby-progressbar-1.13.0/README.md +0 -131
  188. data/vendor/bundle/ruby/4.0.0/gems/securerandom-0.4.1/README.md +0 -72
  189. data/vendor/bundle/ruby/4.0.0/gems/simplecov-0.22.0/CHANGELOG.md +0 -191
  190. data/vendor/bundle/ruby/4.0.0/gems/simplecov-0.22.0/LICENSE +0 -20
  191. data/vendor/bundle/ruby/4.0.0/gems/simplecov-0.22.0/README.md +0 -974
  192. data/vendor/bundle/ruby/4.0.0/gems/simplecov-cobertura-3.2.0/LICENSE +0 -202
  193. data/vendor/bundle/ruby/4.0.0/gems/simplecov-cobertura-3.2.0/README.md +0 -65
  194. data/vendor/bundle/ruby/4.0.0/gems/simplecov-html-0.13.2/CHANGELOG.md +0 -114
  195. data/vendor/bundle/ruby/4.0.0/gems/simplecov-html-0.13.2/LICENSE +0 -20
  196. data/vendor/bundle/ruby/4.0.0/gems/simplecov-html-0.13.2/README.md +0 -30
  197. data/vendor/bundle/ruby/4.0.0/gems/simplecov_json_formatter-0.1.4/CHANGELOG.md +0 -13
  198. data/vendor/bundle/ruby/4.0.0/gems/simplecov_json_formatter-0.1.4/README.md +0 -29
  199. data/vendor/bundle/ruby/4.0.0/gems/typhoeus-1.6.0/CHANGELOG.md +0 -465
  200. data/vendor/bundle/ruby/4.0.0/gems/typhoeus-1.6.0/LICENSE +0 -22
  201. data/vendor/bundle/ruby/4.0.0/gems/typhoeus-1.6.0/README.md +0 -588
  202. data/vendor/bundle/ruby/4.0.0/gems/tzinfo-2.0.6/LICENSE +0 -19
  203. data/vendor/bundle/ruby/4.0.0/gems/tzinfo-2.0.6/README.md +0 -406
  204. data/vendor/bundle/ruby/4.0.0/gems/unicode-display_width-3.2.0/CHANGELOG.md +0 -299
  205. data/vendor/bundle/ruby/4.0.0/gems/unicode-display_width-3.2.0/MIT-LICENSE.txt +0 -22
  206. data/vendor/bundle/ruby/4.0.0/gems/unicode-display_width-3.2.0/README.md +0 -194
  207. data/vendor/bundle/ruby/4.0.0/gems/unicode-emoji-4.2.0/CHANGELOG.md +0 -202
  208. data/vendor/bundle/ruby/4.0.0/gems/unicode-emoji-4.2.0/MIT-LICENSE.txt +0 -20
  209. data/vendor/bundle/ruby/4.0.0/gems/unicode-emoji-4.2.0/README.md +0 -205
  210. data/vendor/bundle/ruby/4.0.0/gems/uri-1.1.1/README.md +0 -55
  211. data/vendor/bundle/ruby/4.0.0/gems/webmock-3.26.2/CHANGELOG.md +0 -2148
  212. data/vendor/bundle/ruby/4.0.0/gems/webmock-3.26.2/LICENSE +0 -20
  213. data/vendor/bundle/ruby/4.0.0/gems/webmock-3.26.2/README.md +0 -1229
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Restify::Adapter::Telemetry do
6
+ subject(:call) { adapter.call(request).value! }
7
+
8
+ let(:adapter) do
9
+ Class.new(Restify::Adapter::Base) do
10
+ def call_native(request, writer)
11
+ writer.fulfill(Restify::Response.new(request, request.uri, 200, {}, ''))
12
+ end
13
+ end.new
14
+ end
15
+
16
+ let(:request) { Restify::Request.new(uri:) }
17
+ let(:span) { OTLE_EXPORTER.finished_spans.last }
18
+
19
+ before { call }
20
+
21
+ context 'with an explicit port' do
22
+ let(:uri) { Addressable::URI.parse('https://example.org:8443/resources') }
23
+
24
+ it 'records the given port' do
25
+ expect(span.name).to eq 'GET https://example.org:8443'
26
+ expect(span.attributes).to eq({
27
+ 'http.request.method' => 'GET',
28
+ 'http.response.status_code' => 200,
29
+ 'server.address' => 'example.org',
30
+ 'server.port' => 8443,
31
+ 'url.full' => 'https://example.org:8443/resources',
32
+ 'url.scheme' => 'https',
33
+ })
34
+ end
35
+ end
36
+
37
+ context 'without a port' do
38
+ let(:uri) { Addressable::URI.parse('https://example.org/resources') }
39
+
40
+ it 'records the default port of the scheme' do
41
+ expect(span.name).to eq 'GET https://example.org:443'
42
+ expect(span.attributes).to include(
43
+ 'server.port' => 443,
44
+ 'url.full' => 'https://example.org/resources',
45
+ )
46
+ end
47
+ end
48
+
49
+ context 'with a URI given as a string' do
50
+ let(:uri) { 'http://example.org/resources' }
51
+
52
+ it 'parses it' do
53
+ expect(span.name).to eq 'GET http://example.org:80'
54
+ expect(span.attributes).to include(
55
+ 'server.address' => 'example.org',
56
+ 'server.port' => 80,
57
+ 'url.full' => 'http://example.org/resources',
58
+ )
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Restify::Adapter::Typhoeus do
6
+ let(:request) { Restify::Request.new(uri: 'http://localhost:9292/base') }
7
+
8
+ before do
9
+ stub_request(:get, 'http://stubserver/base').to_return(status: 200, body: '{}')
10
+ end
11
+
12
+ describe 'sync' do
13
+ let(:adapter) { described_class.new(sync: true) }
14
+
15
+ it 'runs requests from many threads' do
16
+ threads = Array.new(4) do
17
+ Thread.new { Array.new(10) { adapter.call(request).value!.code } }
18
+ end
19
+
20
+ # Do not hang the suite on a deadlock.
21
+ expect(threads.map {|thread| thread.join(5)&.value }).to all eq Array.new(10, 200)
22
+ ensure
23
+ threads&.each(&:kill)
24
+ end
25
+ end
26
+ end
@@ -1,13 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'spec_helper'
4
- require 'active_support'
5
- require 'active_support/cache'
6
4
 
7
5
  describe Restify::Cache do
8
6
  subject(:cache) { described_class.new(store) }
9
7
 
10
- let(:store) { instance_double(ActiveSupport::Cache::Store) }
8
+ let(:store) { Object.new }
11
9
 
12
10
  describe '#call' do
13
11
  let(:request) { instance_double(Restify::Request) }
@@ -71,4 +71,59 @@ describe Restify::Context do
71
71
  it_behaves_like 'serialization'
72
72
  end
73
73
  end
74
+
75
+ describe '#request' do
76
+ subject(:request) { context.request(:get, '/').value! }
77
+
78
+ let(:adapter) do
79
+ response = Restify::Response.new(nil, Addressable::URI.parse(uri), 200, {}, 'body')
80
+
81
+ instance_double(Restify::Adapter::Base).tap do |adapter|
82
+ allow(adapter).to receive(:call).and_return(Restify::Promise.fulfilled(response))
83
+ end
84
+ end
85
+
86
+ let(:cache) do
87
+ Object.new.tap do |cache|
88
+ def cache.call(request)
89
+ yield(request)
90
+ end
91
+ end
92
+ end
93
+
94
+ let(:kwargs) { {adapter:} }
95
+
96
+ it 'calls the adapter without a cache' do
97
+ expect(adapter).to receive(:call).with(Restify::Request)
98
+
99
+ request
100
+ end
101
+
102
+ context 'with a cache' do
103
+ let(:kwargs) { {adapter:, cache:} }
104
+
105
+ it 'lets the cache call the adapter' do
106
+ expect(cache).to receive(:call).with(Restify::Request).and_call_original
107
+ expect(adapter).to receive(:call).with(Restify::Request)
108
+
109
+ request
110
+ end
111
+ end
112
+
113
+ context 'with a global cache' do
114
+ around do |example|
115
+ Restify.cache = cache
116
+ example.run
117
+ ensure
118
+ Restify.cache = nil
119
+ end
120
+
121
+ it 'lets the cache call the adapter' do
122
+ expect(cache).to receive(:call).with(Restify::Request).and_call_original
123
+ expect(adapter).to receive(:call).with(Restify::Request)
124
+
125
+ request
126
+ end
127
+ end
128
+ end
74
129
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Restify do
6
+ # Restify follows relations from URLs in server responses, and libcurl
7
+ # supports far more than HTTP. A server must therefore never be able
8
+ # to make it access e.g. a `file://` or `gopher://` URL.
9
+ describe 'Protocol restrictions' do
10
+ subject(:request) { Restify.new(uri).get.value! }
11
+
12
+ context 'with a file URI' do
13
+ let(:uri) { "file://#{File.expand_path(__FILE__)}" }
14
+
15
+ it 'refuses the request' do
16
+ expect { request }.to raise_error Restify::NetworkError, /protocol/i
17
+ end
18
+ end
19
+
20
+ context 'with a gopher URI' do
21
+ let(:uri) { 'gopher://localhost:70/1' }
22
+
23
+ it 'refuses the request' do
24
+ expect { request }.to raise_error Restify::NetworkError, /protocol/i
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Restify do
6
+ subject(:resource) { Restify.new('http://localhost:9292/old/resource').get.value! }
7
+
8
+ before do
9
+ stub_request(:get, 'http://stubserver/old/resource')
10
+ .to_return do
11
+ <<~HTTP
12
+ HTTP/1.1 302 Found
13
+ Location: http://localhost:9292/new/resource
14
+
15
+ HTTP
16
+ end
17
+
18
+ stub_request(:get, 'http://stubserver/new/resource')
19
+ .to_return do
20
+ <<~HTTP
21
+ HTTP/1.1 200 OK
22
+ Content-Type: application/json
23
+
24
+ {"name":"John Smith","sibling_url":"./sibling"}
25
+ HTTP
26
+ end
27
+ end
28
+
29
+ describe 'following redirects' do
30
+ it 'returns the redirected resource' do
31
+ expect(resource['name']).to eq 'John Smith'
32
+ expect(resource.response.code).to eq 200
33
+ end
34
+
35
+ it 'exposes the effective URI on the response' do
36
+ expect(resource.response.uri.to_s)
37
+ .to eq 'http://localhost:9292/new/resource'
38
+ end
39
+
40
+ it 'resolves relative relations against the effective URI' do
41
+ expect(resource.rel(:sibling).expand({}).to_s)
42
+ .to eq 'http://localhost:9292/new/sibling'
43
+ end
44
+ end
45
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Restify, adapter: 'Restify::Adapter::Typhoeus' do
5
+ describe Restify do
6
6
  before do
7
7
  stub_request(:get, 'http://stubserver/base').to_timeout
8
8
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Restify do
6
+ subject(:resource) { Restify.new('http://localhost:9292/base').get.value! }
7
+
8
+ before do
9
+ stub_request(:get, 'http://stubserver/base')
10
+ .to_return(
11
+ status: 200,
12
+ headers: {
13
+ 'Content-Type' => 'application/json',
14
+ 'Link' => '<http://localhost:9292/other>; rel="other"',
15
+ },
16
+ body:,
17
+ )
18
+ end
19
+
20
+ describe 'Response bodies' do
21
+ # #31: A content type is set, but the body is empty.
22
+ context 'when empty' do
23
+ let(:body) { '' }
24
+
25
+ it 'returns a resource without data' do
26
+ expect(resource.data).to be_nil
27
+ expect(resource).to have_relation :other
28
+ end
29
+ end
30
+
31
+ context 'when invalid' do
32
+ let(:body) { '{"name": ' }
33
+
34
+ it 'returns a resource with relations from headers' do
35
+ expect(resource).to have_relation :other
36
+ end
37
+
38
+ it 'raises on accessing data' do
39
+ expect { resource.data }.to raise_error(Restify::ParseError, /Could not parse response body/) do |error|
40
+ expect(error.cause).to be_a JSON::ParserError
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Restify do
6
+ describe 'Response body encoding' do
7
+ subject(:response) { Restify.new('http://localhost:9292/base').get.value!.response }
8
+
9
+ let(:text) { 'Grüße, Ærø – 日本' }
10
+
11
+ before do
12
+ stub_request(:get, 'http://stubserver/base').to_return(
13
+ status: 200,
14
+ headers: {'Content-Type' => content_type},
15
+ body: body,
16
+ )
17
+ end
18
+
19
+ context 'with an UTF-8 charset' do
20
+ let(:content_type) { 'text/plain; charset=UTF-8' }
21
+ let(:body) { text.encode(Encoding::UTF_8) }
22
+
23
+ it 'returns the body in the given encoding' do
24
+ expect(response.body.encoding).to eq Encoding::UTF_8
25
+ expect(response.body).to eq text
26
+ end
27
+ end
28
+
29
+ context 'with an ISO-8859-1 charset' do
30
+ let(:content_type) { 'text/plain; charset=ISO-8859-1' }
31
+ let(:text) { 'Grüße, Ærø' }
32
+ let(:body) { text.encode(Encoding::ISO_8859_1) }
33
+
34
+ it 'returns the body in the given encoding' do
35
+ expect(response.body.encoding).to eq Encoding::ISO_8859_1
36
+ expect(response.body).to eq text.encode(Encoding::ISO_8859_1)
37
+ expect(response.body.encode(Encoding::UTF_8)).to eq text
38
+ end
39
+ end
40
+
41
+ context 'with a quoted charset' do
42
+ let(:content_type) { 'text/plain; charset="utf-8"' }
43
+ let(:body) { text.encode(Encoding::UTF_8) }
44
+
45
+ it 'returns the body in the given encoding' do
46
+ expect(response.body.encoding).to eq Encoding::UTF_8
47
+ expect(response.body).to eq text
48
+ end
49
+ end
50
+
51
+ context 'with an unknown charset' do
52
+ let(:content_type) { 'text/plain; charset=nonsense' }
53
+ let(:body) { text.encode(Encoding::UTF_8) }
54
+
55
+ it 'returns the body as binary' do
56
+ expect(response.body.encoding).to eq Encoding::BINARY
57
+ expect(response.body.b).to eq text.b
58
+ end
59
+ end
60
+
61
+ context 'without a charset' do
62
+ let(:content_type) { 'text/plain' }
63
+ let(:body) { text.encode(Encoding::UTF_8) }
64
+
65
+ it 'returns the body as binary' do
66
+ expect(response.body.encoding).to eq Encoding::BINARY
67
+ expect(response.body.b).to eq text.b
68
+ end
69
+ end
70
+
71
+ context 'with a JSON body' do
72
+ let(:content_type) { 'application/json; charset=ISO-8859-1' }
73
+ let(:text) { 'Grüße, Ærø' }
74
+ let(:body) { %("#{text}").encode(Encoding::ISO_8859_1) }
75
+
76
+ it 'decodes the body using the given encoding' do
77
+ expect(response.body.encoding).to eq Encoding::ISO_8859_1
78
+ expect(response.decoded_body).to eq text
79
+ end
80
+ end
81
+ end
82
+ end
@@ -5,11 +5,12 @@ require 'spec_helper'
5
5
  describe Restify do
6
6
  before do
7
7
  stub_request(:get, 'http://stubserver/base')
8
- .to_return(status: http_status, headers:)
8
+ .to_return(status: http_status, headers:, body:)
9
9
  end
10
10
 
11
11
  let(:http_status) { '200 OK' }
12
12
  let(:headers) { {} }
13
+ let(:body) { '' }
13
14
 
14
15
  describe 'Error handling' do
15
16
  subject(:request) { Restify.new('http://localhost:9292/base').get.value! }
@@ -20,6 +21,26 @@ describe Restify do
20
21
  it 'throws a BadRequest exception' do
21
22
  expect { request }.to raise_error Restify::BadRequest
22
23
  end
24
+
25
+ # #31: Rails sets a JSON content type even for empty bodies.
26
+ context 'with a JSON content type and an empty body' do
27
+ let(:headers) { {'Content-Type' => 'application/json'} }
28
+
29
+ it 'throws a BadRequest exception' do
30
+ expect { request }.to raise_error Restify::BadRequest
31
+ end
32
+ end
33
+
34
+ context 'with an invalid JSON body' do
35
+ let(:headers) { {'Content-Type' => 'application/json'} }
36
+ let(:body) { '{"errors": ' }
37
+
38
+ it 'throws a BadRequest exception with the raw body as errors' do
39
+ expect { request }.to raise_error(Restify::BadRequest) do |error|
40
+ expect(error.errors).to eq '{"errors": '
41
+ end
42
+ end
43
+ end
23
44
  end
24
45
 
25
46
  context 'for 401 status codes' do
@@ -4,11 +4,13 @@ require 'spec_helper'
4
4
 
5
5
  describe Restify do
6
6
  before do
7
- WebMock.enable!
7
+ WebMock.enable!(except: %i[typhoeus])
8
8
  end
9
9
 
10
10
  after do
11
11
  WebMock.disable!(except: %i[net_http])
12
+ WebMock.disable_net_connect!
13
+ WebMock::CallbackRegistry.reset
12
14
  end
13
15
 
14
16
  describe 'WebMock' do
@@ -23,5 +25,94 @@ describe Restify do
23
25
  it 'raises error for not stubbed requests' do
24
26
  expect { resource }.to raise_error WebMock::NetConnectNotAllowedError
25
27
  end
28
+
29
+ it 'returns stubbed responses' do
30
+ stub_request(:get, 'http://www.example.com/base')
31
+ .to_return(
32
+ headers: {'Content-Type' => 'application/json', 'X-Custom' => 'yes'},
33
+ body: '{"name": "restify", "self_url": "http://www.example.com/self"}',
34
+ )
35
+
36
+ expect(resource['name']).to eq 'restify'
37
+ expect(resource).to have_relation :self
38
+ expect(resource.response.headers).to include('X_CUSTOM' => 'yes')
39
+ end
40
+
41
+ it 'records requests' do
42
+ stub_request(:post, 'http://www.example.com/base')
43
+
44
+ Restify.new('http://www.example.com/base').post({name: 'restify'}).value!
45
+
46
+ expect(
47
+ a_request(:post, 'http://www.example.com/base')
48
+ .with(body: {name: 'restify'}.to_json, headers: {'Content-Type' => 'application/json'}),
49
+ ).to have_been_made.once
50
+ end
51
+
52
+ it 'raises network errors on stubbed timeouts' do
53
+ stub_request(:get, 'http://www.example.com/base').to_timeout
54
+
55
+ expect { resource }.to raise_error Restify::NetworkError, /timeout/i
56
+ end
57
+
58
+ it 'raises stubbed errors' do
59
+ stub_request(:get, 'http://www.example.com/base').to_raise(Errno::ECONNREFUSED)
60
+
61
+ expect { resource }.to raise_error Errno::ECONNREFUSED
62
+ end
63
+
64
+ it 'creates a span for stubbed requests' do
65
+ stub_request(:get, 'http://www.example.com/base')
66
+
67
+ resource
68
+
69
+ expect(OTLE_EXPORTER.finished_spans.map {|span| span.instrumentation_scope.name }).to eq ['restify']
70
+ end
71
+
72
+ context 'when disabled' do
73
+ it 'does not intercept requests' do
74
+ stub_request(:get, 'http://localhost:9292/echo').to_return(body: 'stubbed')
75
+ WebMock.disable!(except: %i[net_http])
76
+
77
+ expect(Restify.new('http://localhost:9292/echo').get.value!.response.body).not_to eq 'stubbed'
78
+ end
79
+ end
80
+
81
+ # Like WebMock does for other libcurl-based libraries: Redirects are
82
+ # returned as they are.
83
+ it 'returns stubbed redirects as they are' do
84
+ stub_request(:get, 'http://www.example.com/base')
85
+ .to_return(status: 302, headers: {'Location' => '/other'})
86
+
87
+ expect(resource.response.code).to eq 302
88
+ expect(resource.response.headers).to include('LOCATION' => '/other')
89
+ expect(a_request(:get, 'http://www.example.com/other')).not_to have_been_made
90
+ end
91
+
92
+ context 'with net connect allowed' do
93
+ before { WebMock.allow_net_connect! }
94
+
95
+ it 'passes requests on to the adapter' do
96
+ expect(Restify.new('http://localhost:9292/echo').get.value!.data).to include('REQUEST_METHOD' => 'GET')
97
+ end
98
+
99
+ it 'invokes callbacks for real requests once' do
100
+ responses = Queue.new
101
+ WebMock.after_request(real_requests_only: true) do |signature, response|
102
+ responses << [signature.uri.to_s, response.status[0]]
103
+ end
104
+
105
+ Restify.new('http://localhost:9292/echo').get.value!
106
+
107
+ expect(responses.pop(timeout: 1)).to eq ['http://localhost:9292/echo', 200]
108
+ expect(responses).to be_empty
109
+ end
110
+
111
+ it 'records real requests once' do
112
+ Restify.new('http://localhost:9292/echo').get.value!
113
+
114
+ expect(a_request(:get, 'http://localhost:9292/echo')).to have_been_made.once
115
+ end
116
+ end
26
117
  end
27
118
  end
@@ -42,16 +42,32 @@ describe Restify::Global do
42
42
  describe '#adapter' do
43
43
  subject(:adapter) { global.adapter }
44
44
 
45
- it 'defaults to Typhoeus adapter' do
46
- expect(adapter).to be_a Restify::Adapter::Typhoeus
45
+ around do |example|
46
+ Restify.adapter.tap do |configured|
47
+ Restify.adapter = nil
48
+ example.run
49
+ ensure
50
+ Restify.adapter = configured
51
+ end
52
+ end
53
+
54
+ it 'defaults to Ethon adapter' do
55
+ expect(adapter).to be_a Restify::Adapter::Ethon
47
56
  end
48
57
  end
49
58
 
50
59
  describe '#adapter=' do
51
60
  let(:stub) { Object.new }
52
61
 
53
- # Ensure to reset adapter after these specs!
54
- after { Restify.adapter = nil }
62
+ # Ensure to restore the configured adapter after these specs!
63
+ around do |example|
64
+ Restify.adapter.tap do |configured|
65
+ Restify.adapter = nil
66
+ example.run
67
+ ensure
68
+ Restify.adapter = configured
69
+ end
70
+ end
55
71
 
56
72
  it 'sets a new adapter' do
57
73
  global.adapter = stub
@@ -62,8 +78,8 @@ describe Restify::Global do
62
78
  describe '#cache' do
63
79
  subject(:cache) { global.cache }
64
80
 
65
- it 'defaults to Typhoeus adapter' do
66
- expect(cache).to be_a Restify::Cache
81
+ it 'is not set by default' do
82
+ expect(cache).to be_nil
67
83
  end
68
84
  end
69
85
 
@@ -73,7 +89,7 @@ describe Restify::Global do
73
89
  # Ensure to reset cache after these specs!
74
90
  after { Restify.cache = nil }
75
91
 
76
- it 'sets a new adapter' do
92
+ it 'sets a new cache' do
77
93
  global.cache = stub
78
94
  expect(global.cache).to be stub
79
95
  end
@@ -82,9 +98,61 @@ describe Restify::Global do
82
98
  describe '#logger' do
83
99
  subject(:logger) { global.logger }
84
100
 
85
- it 'returns Restify root logger' do
86
- expect(logger).to be_a Logging::Logger
87
- expect(logger).to eq Logging.logger[Restify]
101
+ around do |example|
102
+ global.logger.tap do |configured|
103
+ global.logger = nil
104
+ example.run
105
+ ensure
106
+ global.logger = configured
107
+ end
108
+ end
109
+
110
+ it 'is not set by default' do
111
+ expect(logger).to be_nil
112
+ end
113
+
114
+ it 'does not log anything without a logger' do
115
+ loggable = Class.new { include Restify::Logging }.new
116
+
117
+ expect { loggable.debug('message') }.not_to output.to_stdout_from_any_process
118
+ expect { loggable.error(RuntimeError.new('kaboom')) }.not_to output.to_stderr_from_any_process
119
+ end
120
+ end
121
+
122
+ describe '#logger=' do
123
+ let(:configured) { Logger.new(nil) }
124
+
125
+ around do |example|
126
+ global.logger.tap do |previous|
127
+ example.run
128
+ ensure
129
+ global.logger = previous
130
+ end
131
+ end
132
+
133
+ it 'sets the logger' do
134
+ global.logger = configured
135
+
136
+ expect(global.logger).to be configured
137
+ end
138
+
139
+ it 'is used for messages from Restify' do
140
+ output = StringIO.new
141
+ global.logger = Logger.new(output, level: :debug)
142
+
143
+ Class.new { include Restify::Logging }.new.debug('message', key: 'value')
144
+
145
+ expect(output.string).to include 'message key=value'
146
+ end
147
+
148
+ it 'is used for errors from Restify' do
149
+ output = StringIO.new
150
+ global.logger = Logger.new(output)
151
+
152
+ stub_const('Loggable', Class.new { include Restify::Logging })
153
+ Loggable.new.error(RuntimeError.new('kaboom'))
154
+
155
+ expect(output.string).to include 'ERROR -- Loggable: kaboom (RuntimeError)'
88
156
  end
89
157
  end
90
158
  end
@@ -31,6 +31,42 @@ describe Restify::Processors::Json do
31
31
 
32
32
  before { allow(response).to receive(:body).and_return(body) }
33
33
 
34
+ describe 'empty body' do
35
+ let(:body) { '' }
36
+
37
+ it { is_expected.to be_a Restify::Resource }
38
+ it { expect(resource.data).to be_nil }
39
+ end
40
+
41
+ describe 'invalid body' do
42
+ let(:body) { '{"json": ' }
43
+
44
+ before do
45
+ allow(response).to receive_messages(
46
+ uri: Addressable::URI.parse('http://test.host/'),
47
+ links: Restify::Link.parse('<http://test.host/other>; rel="other"'),
48
+ )
49
+ end
50
+
51
+ it { is_expected.to be_a Restify::Resource }
52
+ it { is_expected.to have_relation :other }
53
+ it { expect(resource.response).to be response }
54
+
55
+ it 'raises on accessing data' do
56
+ expect { resource.data }.to raise_error(Restify::ParseError) do |error|
57
+ expect(error.response).to be response
58
+ expect(error.cause).to be_a JSON::ParserError
59
+ end
60
+ end
61
+
62
+ it 'raises on accessing delegated data' do
63
+ expect { resource['json'] }.to raise_error Restify::ParseError
64
+ end
65
+
66
+ it { is_expected.not_to respond_to :each }
67
+ it { expect(resource.inspect).to include '@error=' }
68
+ end
69
+
34
70
  describe 'parsing' do
35
71
  context 'single object' do
36
72
  let(:body) do