restify 2.1.0 → 3.0.0.rc3

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 (222) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +75 -0
  3. data/README.md +30 -5
  4. data/lib/restify/adapter/base.rb +10 -1
  5. data/lib/restify/adapter/ethon/easy.rb +147 -0
  6. data/lib/restify/adapter/ethon/event_loop.rb +255 -0
  7. data/lib/restify/adapter/ethon/options.rb +81 -0
  8. data/lib/restify/adapter/ethon/pool.rb +43 -0
  9. data/lib/restify/adapter/ethon.rb +211 -0
  10. data/lib/restify/adapter/loop_lock.rb +124 -0
  11. data/lib/restify/adapter/telemetry.rb +7 -5
  12. data/lib/restify/adapter/typhoeus.rb +52 -23
  13. data/lib/restify/context.rb +16 -6
  14. data/lib/restify/error.rb +13 -0
  15. data/lib/restify/global.rb +9 -30
  16. data/lib/restify/logging.rb +10 -4
  17. data/lib/restify/processors/base/parsing.rb +43 -26
  18. data/lib/restify/promise.rb +194 -42
  19. data/lib/restify/registry.rb +2 -2
  20. data/lib/restify/relation.rb +27 -13
  21. data/lib/restify/request.rb +2 -2
  22. data/lib/restify/resource.rb +20 -2
  23. data/lib/restify/response.rb +39 -3
  24. data/lib/restify/timeout.rb +23 -34
  25. data/lib/restify/version.rb +3 -3
  26. data/lib/restify/webmock.rb +126 -0
  27. data/lib/restify.rb +5 -2
  28. data/spec/restify/adapter/ethon/easy_spec.rb +111 -0
  29. data/spec/restify/adapter/ethon/event_loop_spec.rb +96 -0
  30. data/spec/restify/adapter/ethon/options_spec.rb +104 -0
  31. data/spec/restify/adapter/ethon/pool_spec.rb +81 -0
  32. data/spec/restify/adapter/ethon_spec.rb +307 -0
  33. data/spec/restify/adapter/loop_lock_spec.rb +144 -0
  34. data/spec/restify/adapter/telemetry_spec.rb +61 -0
  35. data/spec/restify/adapter/typhoeus_spec.rb +26 -0
  36. data/spec/restify/cache_spec.rb +1 -3
  37. data/spec/restify/context_spec.rb +69 -0
  38. data/spec/restify/features/opentelemetry_spec.rb +2 -0
  39. data/spec/restify/features/protocols_spec.rb +28 -0
  40. data/spec/restify/features/redirects_spec.rb +45 -0
  41. data/spec/restify/features/request_errors_spec.rb +1 -1
  42. data/spec/restify/features/response_bodies_spec.rb +45 -0
  43. data/spec/restify/features/response_encoding_spec.rb +82 -0
  44. data/spec/restify/features/response_errors_spec.rb +22 -1
  45. data/spec/restify/features/webmock_spec.rb +92 -1
  46. data/spec/restify/global_spec.rb +78 -10
  47. data/spec/restify/processors/json_spec.rb +103 -0
  48. data/spec/restify/processors/msgpack_spec.rb +36 -0
  49. data/spec/restify/promise_spec.rb +288 -9
  50. data/spec/restify/relation_spec.rb +25 -6
  51. data/spec/restify/request_spec.rb +26 -0
  52. data/spec/restify/timeout_spec.rb +43 -33
  53. data/spec/spec_helper.rb +10 -6
  54. data/spec/support/stub_server.rb +4 -4
  55. metadata +32 -221
  56. data/vendor/bundle/ruby/4.0.0/bundler/gems/my-rubocop-2b861962124a/LICENSE +0 -21
  57. data/vendor/bundle/ruby/4.0.0/bundler/gems/my-rubocop-2b861962124a/README.md +0 -23
  58. data/vendor/bundle/ruby/4.0.0/gems/activesupport-8.1.3/CHANGELOG.md +0 -561
  59. data/vendor/bundle/ruby/4.0.0/gems/activesupport-8.1.3/MIT-LICENSE +0 -20
  60. data/vendor/bundle/ruby/4.0.0/gems/activesupport-8.1.3/README.rdoc +0 -40
  61. data/vendor/bundle/ruby/4.0.0/gems/addressable-2.9.0/CHANGELOG.md +0 -326
  62. data/vendor/bundle/ruby/4.0.0/gems/addressable-2.9.0/LICENSE.txt +0 -202
  63. data/vendor/bundle/ruby/4.0.0/gems/addressable-2.9.0/README.md +0 -121
  64. data/vendor/bundle/ruby/4.0.0/gems/ast-2.4.3/LICENSE.MIT +0 -20
  65. data/vendor/bundle/ruby/4.0.0/gems/ast-2.4.3/README.YARD.md +0 -12
  66. data/vendor/bundle/ruby/4.0.0/gems/base64-0.3.0/README.md +0 -48
  67. data/vendor/bundle/ruby/4.0.0/gems/bigdecimal-4.1.2/LICENSE +0 -56
  68. data/vendor/bundle/ruby/4.0.0/gems/concurrent-ruby-1.3.7/CHANGELOG.md +0 -615
  69. data/vendor/bundle/ruby/4.0.0/gems/concurrent-ruby-1.3.7/LICENSE.txt +0 -21
  70. data/vendor/bundle/ruby/4.0.0/gems/concurrent-ruby-1.3.7/README.md +0 -409
  71. data/vendor/bundle/ruby/4.0.0/gems/connection_pool-3.0.2/LICENSE +0 -20
  72. data/vendor/bundle/ruby/4.0.0/gems/connection_pool-3.0.2/README.md +0 -182
  73. data/vendor/bundle/ruby/4.0.0/gems/crack-1.0.1/LICENSE +0 -20
  74. data/vendor/bundle/ruby/4.0.0/gems/crack-1.0.1/README.md +0 -43
  75. data/vendor/bundle/ruby/4.0.0/gems/diff-lcs-1.6.2/CHANGELOG.md +0 -518
  76. data/vendor/bundle/ruby/4.0.0/gems/diff-lcs-1.6.2/README.md +0 -92
  77. data/vendor/bundle/ruby/4.0.0/gems/docile-1.4.1/LICENSE +0 -21
  78. data/vendor/bundle/ruby/4.0.0/gems/docile-1.4.1/README.md +0 -409
  79. data/vendor/bundle/ruby/4.0.0/gems/drb-2.2.3/LICENSE.txt +0 -22
  80. data/vendor/bundle/ruby/4.0.0/gems/ethon-0.18.0/CHANGELOG.md +0 -417
  81. data/vendor/bundle/ruby/4.0.0/gems/ethon-0.18.0/LICENSE +0 -20
  82. data/vendor/bundle/ruby/4.0.0/gems/ethon-0.18.0/README.md +0 -118
  83. data/vendor/bundle/ruby/4.0.0/gems/ffi-1.17.4-x86_64-linux-gnu/CHANGELOG.md +0 -507
  84. data/vendor/bundle/ruby/4.0.0/gems/ffi-1.17.4-x86_64-linux-gnu/LICENSE +0 -24
  85. data/vendor/bundle/ruby/4.0.0/gems/ffi-1.17.4-x86_64-linux-gnu/LICENSE.SPECS +0 -22
  86. data/vendor/bundle/ruby/4.0.0/gems/ffi-1.17.4-x86_64-linux-gnu/README.md +0 -138
  87. data/vendor/bundle/ruby/4.0.0/gems/hashdiff-1.2.1/LICENSE +0 -19
  88. data/vendor/bundle/ruby/4.0.0/gems/hashdiff-1.2.1/README.md +0 -323
  89. data/vendor/bundle/ruby/4.0.0/gems/hashdiff-1.2.1/changelog.md +0 -127
  90. data/vendor/bundle/ruby/4.0.0/gems/hitimes-3.2.0/LICENSE.txt +0 -16
  91. data/vendor/bundle/ruby/4.0.0/gems/hitimes-3.2.0/README.md +0 -187
  92. data/vendor/bundle/ruby/4.0.0/gems/i18n-1.15.2/MIT-LICENSE +0 -20
  93. data/vendor/bundle/ruby/4.0.0/gems/i18n-1.15.2/README.md +0 -139
  94. data/vendor/bundle/ruby/4.0.0/gems/json-2.19.9/README.md +0 -310
  95. data/vendor/bundle/ruby/4.0.0/gems/language_server-protocol-3.17.0.5/LICENSE.txt +0 -21
  96. data/vendor/bundle/ruby/4.0.0/gems/language_server-protocol-3.17.0.5/README.md +0 -88
  97. data/vendor/bundle/ruby/4.0.0/gems/lint_roller-1.1.0/CHANGELOG.md +0 -15
  98. data/vendor/bundle/ruby/4.0.0/gems/lint_roller-1.1.0/LICENSE.txt +0 -21
  99. data/vendor/bundle/ruby/4.0.0/gems/lint_roller-1.1.0/README.md +0 -173
  100. data/vendor/bundle/ruby/4.0.0/gems/little-plugger-1.1.4/README.rdoc +0 -53
  101. data/vendor/bundle/ruby/4.0.0/gems/logger-1.7.0/README.md +0 -104
  102. data/vendor/bundle/ruby/4.0.0/gems/logging-2.4.0/LICENSE +0 -22
  103. data/vendor/bundle/ruby/4.0.0/gems/logging-2.4.0/README.md +0 -140
  104. data/vendor/bundle/ruby/4.0.0/gems/minitest-6.0.6/README.rdoc +0 -763
  105. data/vendor/bundle/ruby/4.0.0/gems/msgpack-1.8.3/ChangeLog +0 -383
  106. data/vendor/bundle/ruby/4.0.0/gems/msgpack-1.8.3/LICENSE +0 -177
  107. data/vendor/bundle/ruby/4.0.0/gems/msgpack-1.8.3/README.md +0 -302
  108. data/vendor/bundle/ruby/4.0.0/gems/multi_json-1.21.1/LICENSE.md +0 -20
  109. data/vendor/bundle/ruby/4.0.0/gems/multi_json-1.21.1/README.md +0 -281
  110. data/vendor/bundle/ruby/4.0.0/gems/nio4r-2.7.5/ext/libev/LICENSE +0 -37
  111. data/vendor/bundle/ruby/4.0.0/gems/nio4r-2.7.5/ext/libev/README +0 -59
  112. data/vendor/bundle/ruby/4.0.0/gems/nio4r-2.7.5/license.md +0 -80
  113. data/vendor/bundle/ruby/4.0.0/gems/nio4r-2.7.5/readme.md +0 -91
  114. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-api-1.10.0/CHANGELOG.md +0 -219
  115. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-api-1.10.0/LICENSE +0 -201
  116. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-api-1.10.0/README.md +0 -68
  117. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-common-0.25.0/CHANGELOG.md +0 -110
  118. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-common-0.25.0/LICENSE +0 -201
  119. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-common-0.25.0/README.md +0 -62
  120. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-base-0.26.1/CHANGELOG.md +0 -97
  121. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-base-0.26.1/LICENSE +0 -201
  122. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-base-0.26.1/README.md +0 -155
  123. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-ethon-0.29.0/CHANGELOG.md +0 -192
  124. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-ethon-0.29.0/LICENSE +0 -201
  125. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-ethon-0.29.0/README.md +0 -66
  126. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-registry-0.6.0/CHANGELOG.md +0 -31
  127. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-registry-0.6.0/LICENSE +0 -201
  128. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-registry-0.6.0/README.md +0 -40
  129. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-sdk-1.12.0/CHANGELOG.md +0 -302
  130. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-sdk-1.12.0/LICENSE +0 -201
  131. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-sdk-1.12.0/README.md +0 -101
  132. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-semantic_conventions-1.41.0/CHANGELOG.md +0 -65
  133. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-semantic_conventions-1.41.0/LICENSE +0 -201
  134. data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-semantic_conventions-1.41.0/README.md +0 -115
  135. data/vendor/bundle/ruby/4.0.0/gems/parallel-2.1.0/MIT-LICENSE.txt +0 -20
  136. data/vendor/bundle/ruby/4.0.0/gems/parser-3.3.11.1/LICENSE.txt +0 -26
  137. data/vendor/bundle/ruby/4.0.0/gems/prism-1.9.0/CHANGELOG.md +0 -786
  138. data/vendor/bundle/ruby/4.0.0/gems/prism-1.9.0/LICENSE.md +0 -7
  139. data/vendor/bundle/ruby/4.0.0/gems/prism-1.9.0/README.md +0 -143
  140. data/vendor/bundle/ruby/4.0.0/gems/public_suffix-7.0.5/CHANGELOG.md +0 -649
  141. data/vendor/bundle/ruby/4.0.0/gems/public_suffix-7.0.5/LICENSE.txt +0 -22
  142. data/vendor/bundle/ruby/4.0.0/gems/public_suffix-7.0.5/README.md +0 -231
  143. data/vendor/bundle/ruby/4.0.0/gems/puma-8.0.2/LICENSE +0 -29
  144. data/vendor/bundle/ruby/4.0.0/gems/puma-8.0.2/README.md +0 -484
  145. data/vendor/bundle/ruby/4.0.0/gems/puma-8.0.2/docs/jungle/README.md +0 -9
  146. data/vendor/bundle/ruby/4.0.0/gems/puma-8.0.2/docs/jungle/rc.d/README.md +0 -74
  147. data/vendor/bundle/ruby/4.0.0/gems/racc-1.8.1/ChangeLog +0 -846
  148. data/vendor/bundle/ruby/4.0.0/gems/racc-1.8.1/README.ja.rdoc +0 -58
  149. data/vendor/bundle/ruby/4.0.0/gems/racc-1.8.1/README.rdoc +0 -60
  150. data/vendor/bundle/ruby/4.0.0/gems/rack-3.2.6/CHANGELOG.md +0 -1346
  151. data/vendor/bundle/ruby/4.0.0/gems/rack-3.2.6/MIT-LICENSE +0 -20
  152. data/vendor/bundle/ruby/4.0.0/gems/rack-3.2.6/README.md +0 -384
  153. data/vendor/bundle/ruby/4.0.0/gems/rainbow-3.1.1/Changelog.md +0 -101
  154. data/vendor/bundle/ruby/4.0.0/gems/rainbow-3.1.1/LICENSE +0 -20
  155. data/vendor/bundle/ruby/4.0.0/gems/rainbow-3.1.1/README.markdown +0 -227
  156. data/vendor/bundle/ruby/4.0.0/gems/rake-13.4.2/MIT-LICENSE +0 -21
  157. data/vendor/bundle/ruby/4.0.0/gems/rake-13.4.2/README.rdoc +0 -155
  158. data/vendor/bundle/ruby/4.0.0/gems/rake-release-1.4.0/LICENSE +0 -21
  159. data/vendor/bundle/ruby/4.0.0/gems/rake-release-1.4.0/README.md +0 -107
  160. data/vendor/bundle/ruby/4.0.0/gems/regexp_parser-2.12.0/LICENSE +0 -22
  161. data/vendor/bundle/ruby/4.0.0/gems/rexml-3.4.4/LICENSE.txt +0 -22
  162. data/vendor/bundle/ruby/4.0.0/gems/rexml-3.4.4/README.md +0 -57
  163. data/vendor/bundle/ruby/4.0.0/gems/rspec-3.13.2/LICENSE.md +0 -27
  164. data/vendor/bundle/ruby/4.0.0/gems/rspec-3.13.2/README.md +0 -47
  165. data/vendor/bundle/ruby/4.0.0/gems/rspec-core-3.13.6/Changelog.md +0 -2447
  166. data/vendor/bundle/ruby/4.0.0/gems/rspec-core-3.13.6/LICENSE.md +0 -26
  167. data/vendor/bundle/ruby/4.0.0/gems/rspec-core-3.13.6/README.md +0 -389
  168. data/vendor/bundle/ruby/4.0.0/gems/rspec-expectations-3.13.5/Changelog.md +0 -1366
  169. data/vendor/bundle/ruby/4.0.0/gems/rspec-expectations-3.13.5/LICENSE.md +0 -25
  170. data/vendor/bundle/ruby/4.0.0/gems/rspec-expectations-3.13.5/README.md +0 -326
  171. data/vendor/bundle/ruby/4.0.0/gems/rspec-mocks-3.13.8/Changelog.md +0 -1351
  172. data/vendor/bundle/ruby/4.0.0/gems/rspec-mocks-3.13.8/LICENSE.md +0 -25
  173. data/vendor/bundle/ruby/4.0.0/gems/rspec-mocks-3.13.8/README.md +0 -465
  174. data/vendor/bundle/ruby/4.0.0/gems/rspec-support-3.13.7/Changelog.md +0 -444
  175. data/vendor/bundle/ruby/4.0.0/gems/rspec-support-3.13.7/LICENSE.md +0 -23
  176. data/vendor/bundle/ruby/4.0.0/gems/rspec-support-3.13.7/README.md +0 -40
  177. data/vendor/bundle/ruby/4.0.0/gems/rubocop-1.88.0/LICENSE.txt +0 -20
  178. data/vendor/bundle/ruby/4.0.0/gems/rubocop-1.88.0/README.md +0 -255
  179. data/vendor/bundle/ruby/4.0.0/gems/rubocop-ast-1.49.1/LICENSE.txt +0 -20
  180. data/vendor/bundle/ruby/4.0.0/gems/rubocop-ast-1.49.1/README.md +0 -54
  181. data/vendor/bundle/ruby/4.0.0/gems/rubocop-capybara-2.23.0/CHANGELOG.md +0 -116
  182. data/vendor/bundle/ruby/4.0.0/gems/rubocop-capybara-2.23.0/MIT-LICENSE.md +0 -21
  183. data/vendor/bundle/ruby/4.0.0/gems/rubocop-capybara-2.23.0/README.md +0 -91
  184. data/vendor/bundle/ruby/4.0.0/gems/rubocop-factory_bot-2.28.0/CHANGELOG.md +0 -135
  185. data/vendor/bundle/ruby/4.0.0/gems/rubocop-factory_bot-2.28.0/MIT-LICENSE.md +0 -21
  186. data/vendor/bundle/ruby/4.0.0/gems/rubocop-factory_bot-2.28.0/README.md +0 -91
  187. data/vendor/bundle/ruby/4.0.0/gems/rubocop-performance-1.26.1/LICENSE.txt +0 -20
  188. data/vendor/bundle/ruby/4.0.0/gems/rubocop-performance-1.26.1/README.md +0 -100
  189. data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec-3.10.2/CHANGELOG.md +0 -1136
  190. data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec-3.10.2/MIT-LICENSE.md +0 -21
  191. data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec-3.10.2/README.md +0 -112
  192. data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec_rails-2.32.0/CHANGELOG.md +0 -98
  193. data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec_rails-2.32.0/MIT-LICENSE.md +0 -21
  194. data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec_rails-2.32.0/README.md +0 -93
  195. data/vendor/bundle/ruby/4.0.0/gems/ruby-progressbar-1.13.0/LICENSE.txt +0 -19
  196. data/vendor/bundle/ruby/4.0.0/gems/ruby-progressbar-1.13.0/README.md +0 -131
  197. data/vendor/bundle/ruby/4.0.0/gems/securerandom-0.4.1/README.md +0 -72
  198. data/vendor/bundle/ruby/4.0.0/gems/simplecov-0.22.0/CHANGELOG.md +0 -191
  199. data/vendor/bundle/ruby/4.0.0/gems/simplecov-0.22.0/LICENSE +0 -20
  200. data/vendor/bundle/ruby/4.0.0/gems/simplecov-0.22.0/README.md +0 -974
  201. data/vendor/bundle/ruby/4.0.0/gems/simplecov-cobertura-3.2.0/LICENSE +0 -202
  202. data/vendor/bundle/ruby/4.0.0/gems/simplecov-cobertura-3.2.0/README.md +0 -65
  203. data/vendor/bundle/ruby/4.0.0/gems/simplecov-html-0.13.2/CHANGELOG.md +0 -114
  204. data/vendor/bundle/ruby/4.0.0/gems/simplecov-html-0.13.2/LICENSE +0 -20
  205. data/vendor/bundle/ruby/4.0.0/gems/simplecov-html-0.13.2/README.md +0 -30
  206. data/vendor/bundle/ruby/4.0.0/gems/simplecov_json_formatter-0.1.4/CHANGELOG.md +0 -13
  207. data/vendor/bundle/ruby/4.0.0/gems/simplecov_json_formatter-0.1.4/README.md +0 -29
  208. data/vendor/bundle/ruby/4.0.0/gems/typhoeus-1.6.0/CHANGELOG.md +0 -465
  209. data/vendor/bundle/ruby/4.0.0/gems/typhoeus-1.6.0/LICENSE +0 -22
  210. data/vendor/bundle/ruby/4.0.0/gems/typhoeus-1.6.0/README.md +0 -588
  211. data/vendor/bundle/ruby/4.0.0/gems/tzinfo-2.0.6/LICENSE +0 -19
  212. data/vendor/bundle/ruby/4.0.0/gems/tzinfo-2.0.6/README.md +0 -406
  213. data/vendor/bundle/ruby/4.0.0/gems/unicode-display_width-3.2.0/CHANGELOG.md +0 -299
  214. data/vendor/bundle/ruby/4.0.0/gems/unicode-display_width-3.2.0/MIT-LICENSE.txt +0 -22
  215. data/vendor/bundle/ruby/4.0.0/gems/unicode-display_width-3.2.0/README.md +0 -194
  216. data/vendor/bundle/ruby/4.0.0/gems/unicode-emoji-4.2.0/CHANGELOG.md +0 -202
  217. data/vendor/bundle/ruby/4.0.0/gems/unicode-emoji-4.2.0/MIT-LICENSE.txt +0 -20
  218. data/vendor/bundle/ruby/4.0.0/gems/unicode-emoji-4.2.0/README.md +0 -205
  219. data/vendor/bundle/ruby/4.0.0/gems/uri-1.1.1/README.md +0 -55
  220. data/vendor/bundle/ruby/4.0.0/gems/webmock-3.26.2/CHANGELOG.md +0 -2148
  221. data/vendor/bundle/ruby/4.0.0/gems/webmock-3.26.2/LICENSE +0 -20
  222. data/vendor/bundle/ruby/4.0.0/gems/webmock-3.26.2/README.md +0 -1229
@@ -4,6 +4,26 @@ require 'spec_helper'
4
4
 
5
5
  describe Restify::Promise do
6
6
  let(:promise) { described_class.new }
7
+ let(:threads) { [] }
8
+
9
+ after do
10
+ threads.each(&:kill).each(&:join)
11
+ end
12
+
13
+ def pending_promise
14
+ writer = nil
15
+ promise = described_class.create {|w| writer = w }
16
+ [promise, writer]
17
+ end
18
+
19
+ # Run the block in another thread, and return once the thread blocks,
20
+ # e.g. waiting on a promise.
21
+ def blocking(&)
22
+ thread = Thread.new(&)
23
+ threads << thread
24
+ Thread.pass until thread.stop?
25
+ thread
26
+ end
7
27
 
8
28
  describe 'factory methods' do
9
29
  describe '#fulfilled' do
@@ -81,24 +101,58 @@ describe Restify::Promise do
81
101
  end
82
102
  end
83
103
 
84
- context 'when fulfilling the promise asynchronously' do
104
+ context 'when resolving the promise from a block' do
85
105
  subject(:promise) do
86
106
  described_class.create do |writer|
87
- Thread.new do
88
- sleep 0.1
89
- writer.fulfill 42
90
- end
107
+ writer.set { block.call }
108
+ end
109
+ end
110
+
111
+ context 'with a returned value' do
112
+ let(:block) { -> { 42 } }
113
+
114
+ it 'fulfills the promise with it' do
115
+ expect(promise.fulfilled?).to be true
116
+ expect(promise.value!).to eq 42
117
+ end
118
+ end
119
+
120
+ context 'with a raised exception' do
121
+ let(:block) { -> { raise ArgumentError.new('nope') } }
122
+
123
+ it 'rejects the promise with it' do
124
+ expect(promise.rejected?).to be true
125
+ expect { promise.value! }.to raise_error ArgumentError, 'nope'
91
126
  end
92
127
  end
93
128
 
129
+ # Handlers can run from e.g. a libcurl callback, from where no
130
+ # exception must ever escape, not even a non-StandardError.
131
+ context 'with a raised non-StandardError' do
132
+ let(:block) { -> { raise NotImplementedError.new('nope') } }
133
+
134
+ it 'rejects the promise with it' do
135
+ expect(promise.rejected?).to be true
136
+ expect { promise.value! }.to raise_error NotImplementedError, 'nope'
137
+ end
138
+ end
139
+ end
140
+
141
+ context 'when fulfilling the promise later' do
94
142
  it 'returns a pending promise' do
143
+ promise, = pending_promise
144
+
95
145
  expect(promise.fulfilled?).to be false
96
146
  expect(promise.rejected?).to be false
97
147
  expect(promise.pending?).to be true
98
148
  end
99
149
 
100
150
  it 'waits for the fulfillment value' do
101
- expect(promise.value!).to eq 42
151
+ promise, writer = pending_promise
152
+ waiter = blocking { promise.value! }
153
+
154
+ writer.fulfill 42
155
+ expect(waiter.value).to eq 42
102
156
  end
103
157
  end
104
158
  end
@@ -123,6 +177,23 @@ describe Restify::Promise do
123
177
  end
124
178
  end
125
179
 
180
+ context 'with a task raising an exception' do
181
+ let(:task) { proc { raise ArgumentError.new('kaboom') } }
182
+
183
+ it 'rejects the promise with it' do
184
+ expect { result }.to raise_error ArgumentError, 'kaboom'
185
+ end
186
+ end
187
+
188
+ # No exception must escape from a task, not even a non-StandardError.
189
+ context 'with a task raising a non-StandardError' do
190
+ let(:task) { proc { raise NotImplementedError.new('kaboom') } }
191
+
192
+ it 'rejects the promise with it' do
193
+ expect { result }.to raise_error NotImplementedError, 'kaboom'
194
+ end
195
+ end
196
+
126
197
  context 'with dependencies, but no task' do
127
198
  let(:dependencies) do
128
199
  [
@@ -178,19 +249,227 @@ describe Restify::Promise do
178
249
 
179
250
  describe '#wait' do
180
251
  it 'can time out' do
181
- expect { promise.wait(0.1) }.to raise_error Timeout::Error
252
+ expect { promise.wait(0.01) }.to raise_error Timeout::Error
253
+ end
254
+
255
+ context 'with a driver' do
256
+ subject(:promise) { described_class.create(driver:) {|w| writers << w } }
257
+
258
+ let(:driver) { instance_double(Restify::Adapter::Ethon) }
259
+ let(:writers) { [] }
260
+ let(:writer) { writers.first }
261
+
262
+ before { promise }
263
+
264
+ it 'lets the driver complete the promise in the waiting thread' do
265
+ expect(driver).to receive(:drive).once do |p, timeout|
266
+ expect(p).to be promise
267
+ expect(timeout).to be_a Restify::Timeout
268
+ writer.fulfill 42
269
+ true
270
+ end
271
+
272
+ expect(promise.value!).to eq 42
273
+ end
274
+
275
+ it 'waits as usual when the driver declines' do
276
+ allow(driver).to receive(:drive).and_return(false)
277
+ waiter = blocking { promise.value! }
278
+
279
+ writer.fulfill 42
280
+ expect(waiter.value).to eq 42
281
+ end
282
+
283
+ it 'times out when the driver returns without completing it' do
284
+ allow(driver).to receive(:drive).and_return(true)
285
+
286
+ expect { promise.wait(0.01) }.to raise_error Timeout::Error
287
+ end
288
+
289
+ it 'does not call the driver for a complete promise' do
290
+ expect(driver).not_to receive(:drive)
291
+ writer.fulfill 42
292
+
293
+ expect(promise.value!).to eq 42
294
+ end
295
+ end
296
+ end
297
+
298
+ describe 'timeouts' do
299
+ it 'uses the default timeout' do
300
+ previous = Restify::Timeout.default_timeout
301
+ Restify::Timeout.default_timeout = 0.01
302
+
303
+ expect { promise.wait }.to raise_error Restify::Timeout::Error
304
+ ensure
305
+ Restify::Timeout.default_timeout = previous
306
+ end
307
+
308
+ it 'does not reject the promise' do
309
+ dependency, writer = pending_promise
310
+ chained = dependency.then {|v| v + 1 }
311
+
312
+ expect { chained.value!(0.01) }.to raise_error Restify::Timeout::Error
313
+ expect(chained).to be_pending
314
+
315
+ writer.fulfill 1
316
+ expect(chained.value!).to eq 2
317
+ end
318
+
319
+ it 'does not run the task again after a timeout' do
320
+ calls = 0
321
+ inner, writer = pending_promise
322
+ chained = described_class.fulfilled(1).then do
323
+ calls += 1
324
+ inner
325
+ end
326
+
327
+ expect { chained.value!(0.01) }.to raise_error Restify::Timeout::Error
328
+
329
+ writer.fulfill 2
330
+ expect(chained.value!).to eq 2
331
+ expect(calls).to eq 1
332
+ end
333
+
334
+ it 'times out while another thread runs the task' do
335
+ dependency, writer = pending_promise
336
+ chained = dependency.then {|v| v + 1 }
337
+ running = blocking { chained.value! }
338
+
339
+ expect { chained.value!(0.01) }.to raise_error Restify::Timeout::Error
340
+ expect(running).to be_alive
341
+
342
+ writer.fulfill 1
343
+ expect(running.value).to eq 2
344
+ end
345
+
346
+ # Giving up after a timeout releases the claim the same way.
347
+ it 'lets a waiting thread take over when the running thread is killed' do
348
+ dependency, writer = pending_promise
349
+ chained = dependency.then {|v| v + 1 }
350
+ running = blocking { chained.value! }
351
+ waiting = blocking { chained.value! }
352
+
353
+ running.kill.join
354
+
355
+ writer.fulfill 1
356
+ expect(waiting.value).to eq 2
357
+ end
358
+
359
+ it 'runs the task once for concurrent waiters' do
360
+ dependency, writer = pending_promise
361
+ calls = Queue.new
362
+ chained = dependency.then {|v| calls << v }
363
+ waiters = Array.new(5) { blocking { chained.value! } }
364
+
365
+ writer.fulfill 1
366
+ waiters.each(&:join)
367
+ expect(calls.size).to eq 1
368
+ end
369
+ end
370
+
371
+ describe '#then' do
372
+ it 'waits on a promise returned from the task' do
373
+ chained = described_class.fulfilled(1).then do |v|
374
+ described_class.fulfilled(v + 1)
375
+ end
376
+
377
+ expect(chained.value!).to eq 2
378
+ end
379
+
380
+ it 'rejects with the reason of a rejected dependency' do
381
+ chained = described_class.rejected(ArgumentError.new('nope')).then { 42 }
382
+
383
+ expect { chained.value! }.to raise_error ArgumentError, 'nope'
384
+ end
385
+
386
+ it 'rejects with the reason of a promise returned from the task' do
387
+ chained = described_class.fulfilled(1).then do
388
+ described_class.rejected(ArgumentError.new('nope'))
389
+ end
390
+
391
+ expect { chained.value! }.to raise_error ArgumentError, 'nope'
392
+ end
393
+
394
+ it 'runs lazily' do
395
+ calls = 0
396
+ chained = described_class.fulfilled(1).then { calls += 1 }
397
+
398
+ expect(calls).to eq 0
399
+ chained.value!
400
+ expect(calls).to eq 1
401
+ end
402
+ end
403
+
404
+ describe '#add_observer' do
405
+ let(:writers) { [] }
406
+ let(:promise) { described_class.create {|w| writers << w } }
407
+ let(:writer) { writers.first }
408
+
409
+ before { promise }
410
+
411
+ it 'is called when fulfilled' do
412
+ calls = []
413
+ promise.add_observer {|value, reason| calls << [value, reason] }
414
+
415
+ expect(calls).to eq []
416
+ writer.fulfill 42
417
+ expect(calls).to eq [[42, nil]]
418
+ end
419
+
420
+ it 'is called when rejected' do
421
+ error = ArgumentError.new
422
+ calls = []
423
+ promise.add_observer {|value, reason| calls << [value, reason] }
424
+
425
+ writer.reject error
426
+ expect(calls).to eq [[nil, error]]
427
+ end
428
+
429
+ it 'is called right away when already complete' do
430
+ calls = []
431
+ writer.fulfill 42
432
+
433
+ promise.add_observer {|value, _| calls << value }
434
+ expect(calls).to eq [42]
435
+ end
436
+
437
+ it 'calls later observers when one raises' do
438
+ calls = []
439
+ promise.add_observer { raise 'kaboom' }
440
+ promise.add_observer {|value, _| calls << value }
441
+
442
+ expect { writer.fulfill 42 }.not_to raise_error
443
+ expect(calls).to eq [42]
444
+ end
445
+ end
446
+
447
+ describe 'Writer' do
448
+ let(:writers) { [] }
449
+ let(:promise) { described_class.create {|w| writers << w } }
450
+ let(:writer) { writers.first }
451
+
452
+ before { promise }
453
+
454
+ it 'cannot complete a promise twice' do
455
+ writer.fulfill 1
456
+
457
+ expect { writer.fulfill 2 }.to raise_error Restify::Promise::AlreadyCompleteError
458
+ expect { writer.reject ArgumentError }.to raise_error Restify::Promise::AlreadyCompleteError
459
+ expect { writer.set { 2 } }.to raise_error Restify::Promise::AlreadyCompleteError
460
+ expect(promise.value!).to eq 1
182
461
  end
183
462
  end
184
463
 
185
464
  describe '#value' do
186
465
  it 'can time out' do
187
- expect { promise.value(0.1) }.to raise_error Timeout::Error
466
+ expect { promise.value(0.01) }.to raise_error Timeout::Error
188
467
  end
189
468
  end
190
469
 
191
470
  describe '#value!' do
192
471
  it 'can time out' do
193
- expect { promise.value!(0.1) }.to raise_error Timeout::Error
472
+ expect { promise.value!(0.01) }.to raise_error Timeout::Error
194
473
  end
195
474
  end
196
475
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'spec_helper'
4
- require 'active_support'
5
4
 
6
5
  describe Restify::Relation do
7
6
  subject(:relation) { described_class.new context, pattern }
@@ -29,6 +28,29 @@ describe Restify::Relation do
29
28
 
30
29
  it { is_expected.to be_a Addressable::URI }
31
30
 
31
+ context 'without template variables' do
32
+ let(:pattern) { '/resource?a=1' }
33
+ let(:params) { {} }
34
+
35
+ it { expect(expanded.to_s).to eq 'http://test.host/resource?a=1' }
36
+
37
+ it 'reuses a frozen URI' do
38
+ expect(expanded).to be_frozen
39
+ expect(relation.expand({})).to be expanded
40
+ end
41
+
42
+ context 'with query parameters' do
43
+ let(:params) { {b: 2} }
44
+
45
+ it { expect(expanded.to_s).to eq 'http://test.host/resource?a=1&b=2' }
46
+
47
+ it 'does not change the reused URI' do
48
+ expanded
49
+ expect(relation.expand({}).to_s).to eq 'http://test.host/resource?a=1'
50
+ end
51
+ end
52
+ end
53
+
32
54
  context 'with nil' do
33
55
  let(:params) { {id: nil} }
34
56
 
@@ -48,11 +70,8 @@ describe Restify::Relation do
48
70
  end
49
71
 
50
72
  context 'with Object' do
51
- let(:object) do
52
- Class.new do
53
- undef to_param
54
- end.new
55
- end
73
+ # Without #to_param, e.g. from ActiveSupport
74
+ let(:object) { Class.new.new }
56
75
  let(:params) { {id: object} }
57
76
 
58
77
  it 'raise a TypeError from adressable' do
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Restify::Request do
6
+ subject(:request) { described_class.new(uri:) }
7
+
8
+ describe '#uri' do
9
+ context 'with a string' do
10
+ let(:uri) { 'https://example.org:8443/resources?page=2' }
11
+
12
+ it 'parses it' do
13
+ expect(request.uri).to be_a Addressable::URI
14
+ expect(request.uri.to_s).to eq uri
15
+ end
16
+ end
17
+
18
+ context 'with an Addressable::URI' do
19
+ let(:uri) { Addressable::URI.parse('https://example.org/resources') }
20
+
21
+ it 'keeps it as is' do
22
+ expect(request.uri).to be uri
23
+ end
24
+ end
25
+ end
26
+ end
@@ -5,50 +5,60 @@ require 'spec_helper'
5
5
  describe Restify::Timeout do
6
6
  let(:timer) { described_class.new(0.2) }
7
7
 
8
- describe '#timeout!' do
9
- context 'before having timed out' do
10
- it 'do nothing' do
11
- expect { timer.timeout! }.not_to raise_error
12
- end
8
+ describe '#remaining' do
9
+ it 'returns the seconds left' do
10
+ expect(timer.remaining).to be_within(0.05).of(0.2)
13
11
  end
14
12
 
15
- context 'after having timed out' do
16
- it 'calls given block' do
17
- expect { timer.timeout! }.not_to raise_error
18
- sleep timer.send(:wait_interval)
19
- expect { timer.timeout! }.to raise_error Restify::Timeout::Error
20
- end
13
+ it 'is not positive after having timed out' do
14
+ expired = described_class.new(0.01)
15
+ sleep expired.remaining
16
+
17
+ expect(expired.remaining).not_to be_positive
21
18
  end
22
19
  end
23
20
 
24
- describe '#wait_on!' do
25
- it 'calls block on IVar timeout' do
26
- expect do
27
- timer.wait_on!(Concurrent::IVar.new)
28
- end.to raise_error Restify::Timeout::Error
21
+ describe '#exception' do
22
+ it 'names the target and duration' do
23
+ error = described_class.new(0.2, :target).exception
24
+
25
+ expect(error).to be_a Restify::Timeout::Error
26
+ expect(error.target).to eq :target
27
+ expect(error.duration).to eq 0.2
28
+ expect(error.message).to eq 'Operation on target timed out after 0.2s'
29
+ end
30
+
31
+ it 'names the duration without a target' do
32
+ expect(timer.exception.message).to eq 'Operation timed out after 0.2s'
33
+ end
34
+ end
35
+
36
+ describe '.new' do
37
+ it 'returns a given timeout' do
38
+ expect(described_class.new(timer)).to be timer
39
+ end
40
+
41
+ it 'uses the default timeout for nil' do
42
+ expect(described_class.new(nil).duration).to eq described_class.default_timeout
43
+ end
44
+
45
+ it 'accepts numeric strings' do
46
+ expect(described_class.new('0.2').duration).to eq 0.2
29
47
  end
30
48
 
31
- it 'calls block on Promise timeout' do
32
- expect do
33
- timer.wait_on!(Restify::Promise.new)
34
- end.to raise_error Restify::Timeout::Error
49
+ it 'rejects infinite values' do
50
+ expect { described_class.new(Float::INFINITY) }.to raise_error ArgumentError, /must be finite/
35
51
  end
36
52
 
37
- it 'does nothing on successful IVar' do
38
- expect do
39
- Concurrent::IVar.new.tap do |ivar|
40
- Thread.new { ivar.set :success }
41
- expect(timer.wait_on!(ivar)).to eq :success
42
- end
43
- end.not_to raise_error
53
+ it 'rejects non-numeric values' do
54
+ expect { described_class.new('soon') }.to raise_error ArgumentError, /must be a number/
55
+ expect { described_class.new(Object.new) }.to raise_error ArgumentError, /must be a number/
44
56
  end
45
57
 
46
- it 'does nothing on successful Promise' do
47
- expect do
48
- Restify::Promise.fulfilled(:success).tap do |p|
49
- expect(timer.wait_on!(p)).to eq :success
50
- end
51
- end.not_to raise_error
58
+ it 'rejects non-positive values' do
59
+ expect { described_class.new(0) }.to raise_error ArgumentError, /must be > 0/
60
+ expect { described_class.new(-1) }.to raise_error ArgumentError, /must be > 0/
61
+ expect { described_class.new(Float::NAN) }.to raise_error ArgumentError, /must be > 0/
52
62
  end
53
63
  end
54
64
  end
data/spec/spec_helper.rb CHANGED
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'logger'
3
4
  require 'rspec'
4
5
 
5
6
  require 'simplecov'
6
7
  require 'simplecov-cobertura'
7
8
 
8
9
  SimpleCov.start do
9
- add_filter 'spec'
10
+ skip %r{\Aspec/}i
10
11
  end
11
12
 
12
13
  SimpleCov.formatters = [
@@ -18,6 +19,9 @@ require 'restify'
18
19
 
19
20
  if ENV['ADAPTER']
20
21
  case ENV['ADAPTER'].to_s.downcase
22
+ when 'ethon'
23
+ require 'restify/adapter/ethon'
24
+ Restify.adapter = Restify::Adapter::Ethon.new
21
25
  when 'typhoeus'
22
26
  require 'restify/adapter/typhoeus'
23
27
  Restify.adapter = Restify::Adapter::Typhoeus.new
@@ -32,6 +36,8 @@ end
32
36
  require_relative 'support/opentelemetry'
33
37
  require_relative 'support/stub_server'
34
38
 
39
+ require 'restify/webmock'
40
+
35
41
  RSpec.configure do |config|
36
42
  config.order = 'random'
37
43
 
@@ -46,11 +52,9 @@ RSpec.configure do |config|
46
52
  skip 'Spec not enabled for current adapter'
47
53
  end
48
54
 
49
- config.before do
50
- Ethon.logger = Logging.logger[Ethon] if defined?(Ethon)
51
-
52
- Logging.logger.root.level = :debug
53
- Logging.logger.root.add_appenders Logging.appenders.stdout
55
+ config.before(:suite) do
56
+ Restify.logger = Logger.new($stdout, level: ENV.fetch('LOG_LEVEL', 'debug'))
57
+ Ethon.logger = Restify.logger
54
58
  end
55
59
 
56
60
  config.warnings = true
@@ -65,8 +65,8 @@ module Stub
65
65
 
66
66
  class Exception < ::StandardError; end
67
67
 
68
- # Inject into base adapter to have HTTP 599 (missing stub) error raised as an
69
- # extra exception, not just a server error.
68
+ # Inject into base adapter to have HTTP 599 (missing stub) error
69
+ # raised as an extra exception, not just a server error.
70
70
  module Patch
71
71
  def call(request)
72
72
  super.then do |response|
@@ -98,8 +98,8 @@ RSpec.configure do |config|
98
98
  config.before(:suite) do
99
99
  Stub.start_server!
100
100
 
101
- # Net::HTTP adapter must be enabled, otherwise webmock fails to create mock
102
- # responses from raw strings.
101
+ # Net::HTTP adapter must be enabled, otherwise webmock fails to
102
+ # create mock responses from raw strings.
103
103
  WebMock.disable!(except: %i[net_http])
104
104
  end
105
105