restify 2.0.2 → 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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -0
  3. data/README.md +31 -5
  4. data/lib/restify/adapter/base.rb +11 -2
  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/cache.rb +1 -1
  10. data/lib/restify/context.rb +5 -5
  11. data/lib/restify/error.rb +13 -0
  12. data/lib/restify/global.rb +9 -30
  13. data/lib/restify/logging.rb +10 -4
  14. data/lib/restify/processors/base/parsing.rb +13 -1
  15. data/lib/restify/promise.rb +20 -2
  16. data/lib/restify/registry.rb +2 -2
  17. data/lib/restify/relation.rb +9 -17
  18. data/lib/restify/request.rb +2 -2
  19. data/lib/restify/resource.rb +20 -2
  20. data/lib/restify/response.rb +41 -5
  21. data/lib/restify/timeout.rb +11 -5
  22. data/lib/restify/version.rb +3 -3
  23. data/lib/restify/webmock.rb +126 -0
  24. data/lib/restify.rb +5 -1
  25. data/spec/restify/adapter/base_spec.rb +33 -0
  26. data/spec/restify/adapter/ethon_spec.rb +263 -0
  27. data/spec/restify/adapter/loop_lock_spec.rb +144 -0
  28. data/spec/restify/adapter/telemetry_spec.rb +61 -0
  29. data/spec/restify/adapter/typhoeus_spec.rb +26 -0
  30. data/spec/restify/cache_spec.rb +1 -3
  31. data/spec/restify/context_spec.rb +57 -2
  32. data/spec/restify/features/opentelemetry_spec.rb +1 -1
  33. data/spec/restify/features/protocols_spec.rb +28 -0
  34. data/spec/restify/features/redirects_spec.rb +45 -0
  35. data/spec/restify/features/request_errors_spec.rb +1 -1
  36. data/spec/restify/features/response_bodies_spec.rb +45 -0
  37. data/spec/restify/features/response_encoding_spec.rb +82 -0
  38. data/spec/restify/features/response_errors_spec.rb +22 -1
  39. data/spec/restify/features/webmock_spec.rb +92 -1
  40. data/spec/restify/global_spec.rb +118 -1
  41. data/spec/restify/link_spec.rb +13 -3
  42. data/spec/restify/processors/json_spec.rb +36 -0
  43. data/spec/restify/processors/msgpack_spec.rb +36 -0
  44. data/spec/restify/promise_spec.rb +82 -0
  45. data/spec/restify/relation_spec.rb +22 -7
  46. data/spec/restify/request_spec.rb +26 -0
  47. data/spec/restify/resource_spec.rb +6 -0
  48. data/spec/restify/timeout_spec.rb +11 -0
  49. data/spec/restify_spec.rb +1 -1
  50. data/spec/spec_helper.rb +10 -7
  51. data/spec/support/stub_server.rb +4 -4
  52. metadata +27 -105
  53. data/vendor/bundle/ruby/3.4.0/bundler/gems/my-rubocop-3bcd2110ca87/LICENSE +0 -21
  54. data/vendor/bundle/ruby/3.4.0/bundler/gems/my-rubocop-3bcd2110ca87/README.md +0 -23
  55. data/vendor/bundle/ruby/3.4.0/gems/activesupport-8.0.2/CHANGELOG.md +0 -255
  56. data/vendor/bundle/ruby/3.4.0/gems/activesupport-8.0.2/MIT-LICENSE +0 -20
  57. data/vendor/bundle/ruby/3.4.0/gems/activesupport-8.0.2/README.rdoc +0 -40
  58. data/vendor/bundle/ruby/3.4.0/gems/addressable-2.8.7/CHANGELOG.md +0 -301
  59. data/vendor/bundle/ruby/3.4.0/gems/addressable-2.8.7/LICENSE.txt +0 -202
  60. data/vendor/bundle/ruby/3.4.0/gems/addressable-2.8.7/README.md +0 -121
  61. data/vendor/bundle/ruby/3.4.0/gems/base64-0.2.0/LICENSE.txt +0 -22
  62. data/vendor/bundle/ruby/3.4.0/gems/base64-0.2.0/README.md +0 -48
  63. data/vendor/bundle/ruby/3.4.0/gems/benchmark-0.4.0/README.md +0 -138
  64. data/vendor/bundle/ruby/3.4.0/gems/bigdecimal-3.1.9/LICENSE +0 -56
  65. data/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/CHANGELOG.md +0 -603
  66. data/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/LICENSE.txt +0 -21
  67. data/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/README.md +0 -407
  68. data/vendor/bundle/ruby/3.4.0/gems/connection_pool-2.5.3/LICENSE +0 -20
  69. data/vendor/bundle/ruby/3.4.0/gems/connection_pool-2.5.3/README.md +0 -167
  70. data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.1/LICENSE.txt +0 -22
  71. data/vendor/bundle/ruby/3.4.0/gems/ethon-0.16.0/CHANGELOG.md +0 -375
  72. data/vendor/bundle/ruby/3.4.0/gems/ethon-0.16.0/LICENSE +0 -20
  73. data/vendor/bundle/ruby/3.4.0/gems/ethon-0.16.0/README.md +0 -118
  74. data/vendor/bundle/ruby/3.4.0/gems/ffi-1.17.2-x86_64-linux-gnu/CHANGELOG.md +0 -473
  75. data/vendor/bundle/ruby/3.4.0/gems/ffi-1.17.2-x86_64-linux-gnu/LICENSE +0 -24
  76. data/vendor/bundle/ruby/3.4.0/gems/ffi-1.17.2-x86_64-linux-gnu/LICENSE.SPECS +0 -22
  77. data/vendor/bundle/ruby/3.4.0/gems/ffi-1.17.2-x86_64-linux-gnu/README.md +0 -137
  78. data/vendor/bundle/ruby/3.4.0/gems/hitimes-3.1.0/LICENSE.txt +0 -16
  79. data/vendor/bundle/ruby/3.4.0/gems/hitimes-3.1.0/README.md +0 -187
  80. data/vendor/bundle/ruby/3.4.0/gems/i18n-1.14.7/MIT-LICENSE +0 -20
  81. data/vendor/bundle/ruby/3.4.0/gems/i18n-1.14.7/README.md +0 -127
  82. data/vendor/bundle/ruby/3.4.0/gems/little-plugger-1.1.4/README.rdoc +0 -53
  83. data/vendor/bundle/ruby/3.4.0/gems/logger-1.7.0/README.md +0 -104
  84. data/vendor/bundle/ruby/3.4.0/gems/logging-2.4.0/LICENSE +0 -22
  85. data/vendor/bundle/ruby/3.4.0/gems/logging-2.4.0/README.md +0 -140
  86. data/vendor/bundle/ruby/3.4.0/gems/minitest-5.25.5/README.rdoc +0 -842
  87. data/vendor/bundle/ruby/3.4.0/gems/msgpack-1.8.0/ChangeLog +0 -368
  88. data/vendor/bundle/ruby/3.4.0/gems/msgpack-1.8.0/LICENSE +0 -177
  89. data/vendor/bundle/ruby/3.4.0/gems/msgpack-1.8.0/README.md +0 -302
  90. data/vendor/bundle/ruby/3.4.0/gems/multi_json-1.15.0/CHANGELOG.md +0 -275
  91. data/vendor/bundle/ruby/3.4.0/gems/multi_json-1.15.0/LICENSE.md +0 -20
  92. data/vendor/bundle/ruby/3.4.0/gems/multi_json-1.15.0/README.md +0 -121
  93. data/vendor/bundle/ruby/3.4.0/gems/opentelemetry-api-1.5.0/CHANGELOG.md +0 -193
  94. data/vendor/bundle/ruby/3.4.0/gems/opentelemetry-api-1.5.0/LICENSE +0 -201
  95. data/vendor/bundle/ruby/3.4.0/gems/opentelemetry-api-1.5.0/README.md +0 -68
  96. data/vendor/bundle/ruby/3.4.0/gems/opentelemetry-common-0.22.0/CHANGELOG.md +0 -98
  97. data/vendor/bundle/ruby/3.4.0/gems/opentelemetry-common-0.22.0/LICENSE +0 -201
  98. data/vendor/bundle/ruby/3.4.0/gems/opentelemetry-common-0.22.0/README.md +0 -62
  99. data/vendor/bundle/ruby/3.4.0/gems/public_suffix-6.0.2/CHANGELOG.md +0 -498
  100. data/vendor/bundle/ruby/3.4.0/gems/public_suffix-6.0.2/LICENSE.txt +0 -22
  101. data/vendor/bundle/ruby/3.4.0/gems/public_suffix-6.0.2/README.md +0 -222
  102. data/vendor/bundle/ruby/3.4.0/gems/rack-3.1.15/CHANGELOG.md +0 -1146
  103. data/vendor/bundle/ruby/3.4.0/gems/rack-3.1.15/MIT-LICENSE +0 -20
  104. data/vendor/bundle/ruby/3.4.0/gems/rack-3.1.15/README.md +0 -355
  105. data/vendor/bundle/ruby/3.4.0/gems/rake-13.2.1/MIT-LICENSE +0 -21
  106. data/vendor/bundle/ruby/3.4.0/gems/rake-13.2.1/README.rdoc +0 -155
  107. data/vendor/bundle/ruby/3.4.0/gems/rake-release-1.3.0/LICENSE +0 -21
  108. data/vendor/bundle/ruby/3.4.0/gems/rake-release-1.3.0/README.md +0 -107
  109. data/vendor/bundle/ruby/3.4.0/gems/securerandom-0.4.1/README.md +0 -72
  110. data/vendor/bundle/ruby/3.4.0/gems/typhoeus-1.4.1/CHANGELOG.md +0 -410
  111. data/vendor/bundle/ruby/3.4.0/gems/typhoeus-1.4.1/LICENSE +0 -22
  112. data/vendor/bundle/ruby/3.4.0/gems/typhoeus-1.4.1/README.md +0 -588
  113. data/vendor/bundle/ruby/3.4.0/gems/tzinfo-2.0.6/LICENSE +0 -19
  114. data/vendor/bundle/ruby/3.4.0/gems/tzinfo-2.0.6/README.md +0 -406
  115. data/vendor/bundle/ruby/3.4.0/gems/uri-1.0.3/README.md +0 -55
@@ -21,7 +21,7 @@ describe Restify::Global do
21
21
  end
22
22
 
23
23
  context 'with registry symbol' do
24
- subject(:restify) { global.new(uri, **options) }
24
+ subject(:restify) { global.new(name, **options) }
25
25
 
26
26
  let(:name) { :registry_item_name }
27
27
  let(:uri) { 'http://api.github.com/' }
@@ -38,4 +38,121 @@ describe Restify::Global do
38
38
  end
39
39
  end
40
40
  end
41
+
42
+ describe '#adapter' do
43
+ subject(:adapter) { global.adapter }
44
+
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
56
+ end
57
+ end
58
+
59
+ describe '#adapter=' do
60
+ let(:stub) { Object.new }
61
+
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
71
+
72
+ it 'sets a new adapter' do
73
+ global.adapter = stub
74
+ expect(global.adapter).to be stub
75
+ end
76
+ end
77
+
78
+ describe '#cache' do
79
+ subject(:cache) { global.cache }
80
+
81
+ it 'is not set by default' do
82
+ expect(cache).to be_nil
83
+ end
84
+ end
85
+
86
+ describe '#cache=' do
87
+ let(:stub) { Object.new }
88
+
89
+ # Ensure to reset cache after these specs!
90
+ after { Restify.cache = nil }
91
+
92
+ it 'sets a new cache' do
93
+ global.cache = stub
94
+ expect(global.cache).to be stub
95
+ end
96
+ end
97
+
98
+ describe '#logger' do
99
+ subject(:logger) { global.logger }
100
+
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)'
156
+ end
157
+ end
41
158
  end
@@ -9,7 +9,7 @@ describe Restify::Link do
9
9
  links = described_class
10
10
  .parse('<http://example.org/search{?query}>; rel="search"')
11
11
 
12
- expect(links).to have(1).item
12
+ expect(links.size).to eq(1)
13
13
  expect(links[0].uri).to eq 'http://example.org/search{?query}'
14
14
  expect(links[0].metadata).to eq 'rel' => 'search'
15
15
  end
@@ -18,7 +18,7 @@ describe Restify::Link do
18
18
  links = described_class
19
19
  .parse('<http://example.org/search{?query}>; rel=search')
20
20
 
21
- expect(links).to have(1).item
21
+ expect(links.size).to eq(1)
22
22
  expect(links[0].uri).to eq 'http://example.org/search{?query}'
23
23
  expect(links[0].metadata).to eq 'rel' => 'search'
24
24
  end
@@ -27,7 +27,7 @@ describe Restify::Link do
27
27
  links = described_class
28
28
  .parse('<p://h.tld/p>; rel=abc, <p://h.tld/b>; a=b; c="d"')
29
29
 
30
- expect(links).to have(2).item
30
+ expect(links.size).to eq(2)
31
31
  expect(links[0].uri).to eq 'p://h.tld/p'
32
32
  expect(links[0].metadata).to eq 'rel' => 'abc'
33
33
  expect(links[1].uri).to eq 'p://h.tld/b'
@@ -50,6 +50,16 @@ describe Restify::Link do
50
50
  expect { described_class.parse('</>; rel="s", abc-invalid') }
51
51
  .to raise_error ArgumentError, /Invalid token at \d+:/i
52
52
  end
53
+
54
+ it 'errors on invalid param' do
55
+ expect { described_class.parse('<https://example.org>; rel') }
56
+ .to raise_error ArgumentError, /Invalid token at \d+:/i
57
+ end
58
+
59
+ it 'errors on invalid param value' do
60
+ expect { described_class.parse('<https://example.org>; rel=') }
61
+ .to raise_error ArgumentError, /Invalid token at \d+:/i
62
+ end
53
63
  end
54
64
  end
55
65
  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
@@ -41,6 +41,42 @@ describe Restify::Processors::Msgpack do
41
41
 
42
42
  before { allow(response).to receive(:body).and_return(body) }
43
43
 
44
+ describe 'empty body' do
45
+ let(:body) { '' }
46
+
47
+ it { is_expected.to be_a Restify::Resource }
48
+ it { expect(resource.data).to be_nil }
49
+ end
50
+
51
+ describe 'invalid body' do
52
+ let(:body) { "\xC1".b }
53
+
54
+ before do
55
+ allow(response).to receive_messages(
56
+ uri: Addressable::URI.parse('http://test.host/'),
57
+ links: Restify::Link.parse('<http://test.host/other>; rel="other"'),
58
+ )
59
+ end
60
+
61
+ it { is_expected.to be_a Restify::Resource }
62
+ it { is_expected.to have_relation :other }
63
+ it { expect(resource.response).to be response }
64
+
65
+ it 'raises on accessing data' do
66
+ expect { resource.data }.to raise_error(Restify::ParseError) do |error|
67
+ expect(error.response).to be response
68
+ expect(error.cause).to be_a MessagePack::MalformedFormatError
69
+ end
70
+ end
71
+
72
+ it 'raises on accessing delegated data' do
73
+ expect { resource['json'] }.to raise_error Restify::ParseError
74
+ end
75
+
76
+ it { is_expected.not_to respond_to :each }
77
+ it { expect(resource.inspect).to include '@error=' }
78
+ end
79
+
44
80
  describe 'parsing' do
45
81
  context 'single object' do
46
82
  let(:body) do
@@ -81,6 +81,43 @@ describe Restify::Promise do
81
81
  end
82
82
  end
83
83
 
84
+ context 'when resolving the promise from a block' do
85
+ subject(:promise) do
86
+ described_class.create do |writer|
87
+ writer.set { block.call }
88
+ end
89
+ end
90
+
91
+ context 'with a returned value' do
92
+ let(:block) { -> { 42 } }
93
+
94
+ it 'fulfills the promise with it' do
95
+ expect(promise.fulfilled?).to be true
96
+ expect(promise.value!).to eq 42
97
+ end
98
+ end
99
+
100
+ context 'with a raised exception' do
101
+ let(:block) { -> { raise ArgumentError.new('nope') } }
102
+
103
+ it 'rejects the promise with it' do
104
+ expect(promise.rejected?).to be true
105
+ expect { promise.value! }.to raise_error ArgumentError, 'nope'
106
+ end
107
+ end
108
+
109
+ # Handlers can run from e.g. a libcurl callback, from where no
110
+ # exception must ever escape, not even a non-StandardError.
111
+ context 'with a raised non-StandardError' do
112
+ let(:block) { -> { raise NotImplementedError.new('nope') } }
113
+
114
+ it 'rejects the promise with it' do
115
+ expect(promise.rejected?).to be true
116
+ expect { promise.value! }.to raise_error NotImplementedError, 'nope'
117
+ end
118
+ end
119
+ end
120
+
84
121
  context 'when fulfilling the promise asynchronously' do
85
122
  subject(:promise) do
86
123
  described_class.create do |writer|
@@ -180,6 +217,51 @@ describe Restify::Promise do
180
217
  it 'can time out' do
181
218
  expect { promise.wait(0.1) }.to raise_error Timeout::Error
182
219
  end
220
+
221
+ context 'with a driver' do
222
+ subject(:promise) { described_class.create(driver:) {|w| writers << w } }
223
+
224
+ let(:driver) { instance_double(Restify::Adapter::Ethon) }
225
+ let(:writers) { [] }
226
+ let(:writer) { writers.first }
227
+
228
+ before { promise }
229
+
230
+ it 'lets the driver complete the promise in the waiting thread' do
231
+ expect(driver).to receive(:drive).once do |p, timeout|
232
+ expect(p).to be promise
233
+ expect(timeout).to be_a Restify::Timeout
234
+ writer.fulfill 42
235
+ true
236
+ end
237
+
238
+ expect(promise.value!).to eq 42
239
+ end
240
+
241
+ it 'waits as usual when the driver declines' do
242
+ allow(driver).to receive(:drive).and_return(false)
243
+
244
+ Thread.new do
245
+ sleep 0.05
246
+ writer.fulfill 42
247
+ end
248
+
249
+ expect(promise.value!).to eq 42
250
+ end
251
+
252
+ it 'times out when the driver returns without completing it' do
253
+ allow(driver).to receive(:drive).and_return(true)
254
+
255
+ expect { promise.wait(0.1) }.to raise_error Timeout::Error
256
+ end
257
+
258
+ it 'does not call the driver for a complete promise' do
259
+ expect(driver).not_to receive(:drive)
260
+ writer.fulfill 42
261
+
262
+ expect(promise.value!).to eq 42
263
+ end
264
+ end
183
265
  end
184
266
 
185
267
  describe '#value' do
@@ -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 }
@@ -47,6 +46,16 @@ describe Restify::Relation do
47
46
  it { expect(expanded.to_s).to eq 'http://test.host/resource/true' }
48
47
  end
49
48
 
49
+ context 'with Object' do
50
+ # Without #to_param, e.g. from ActiveSupport
51
+ let(:object) { Class.new.new }
52
+ let(:params) { {id: object} }
53
+
54
+ it 'raise a TypeError from adressable' do
55
+ expect { expanded }.to raise_error(TypeError, /Can't convert #<Class:0x.*> into String or Array/)
56
+ end
57
+ end
58
+
50
59
  context 'with #to_param object' do
51
60
  let(:params) { {id: cls_to_param.new} }
52
61
 
@@ -213,15 +222,15 @@ describe Restify::Relation do
213
222
  end
214
223
 
215
224
  describe '#get' do
216
- include_examples 'non-data-request', :get
225
+ it_behaves_like 'non-data-request', :get
217
226
  end
218
227
 
219
228
  describe '#head' do
220
- include_examples 'non-data-request', :head
229
+ it_behaves_like 'non-data-request', :head
221
230
  end
222
231
 
223
232
  describe '#delete' do
224
- include_examples 'non-data-request', :delete
233
+ it_behaves_like 'non-data-request', :delete
225
234
  end
226
235
 
227
236
  shared_examples 'data-request' do |method|
@@ -274,14 +283,20 @@ describe Restify::Relation do
274
283
  end
275
284
 
276
285
  describe '#post' do
277
- include_examples 'data-request', :post
286
+ it_behaves_like 'data-request', :post
278
287
  end
279
288
 
280
289
  describe '#put' do
281
- include_examples 'data-request', :put
290
+ it_behaves_like 'data-request', :put
282
291
  end
283
292
 
284
293
  describe '#patch' do
285
- include_examples 'data-request', :patch
294
+ it_behaves_like 'data-request', :patch
295
+ end
296
+
297
+ describe '#to_s' do
298
+ subject(:string) { relation.to_s }
299
+
300
+ it { is_expected.to eq '/resource/{id}{?q*}' }
286
301
  end
287
302
  end
@@ -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
@@ -118,4 +118,10 @@ describe Restify::Resource do
118
118
  end
119
119
  end
120
120
  end
121
+
122
+ describe '#inspect' do
123
+ subject(:inspect) { resource.inspect }
124
+
125
+ it { is_expected.to eq '#<Restify::Resource @data={} @relations={}>' }
126
+ end
121
127
  end
@@ -21,6 +21,17 @@ describe Restify::Timeout do
21
21
  end
22
22
  end
23
23
 
24
+ describe '#remaining' do
25
+ it 'returns the seconds left' do
26
+ expect(timer.remaining).to be_within(0.05).of(0.2)
27
+ end
28
+
29
+ it 'is not positive after having timed out' do
30
+ sleep timer.remaining
31
+ expect(timer.remaining).not_to be_positive
32
+ end
33
+ end
34
+
24
35
  describe '#wait_on!' do
25
36
  it 'calls block on IVar timeout' do
26
37
  expect do
data/spec/restify_spec.rb CHANGED
@@ -161,7 +161,7 @@ describe Restify do
161
161
  users = users_relation.get.value!
162
162
 
163
163
  # We get a collection back (Restify::Collection).
164
- expect(users).to have(2).items
164
+ expect(users.size).to eq(2)
165
165
 
166
166
  # Let's get the first one.
167
167
  user = users.first
data/spec/spec_helper.rb CHANGED
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'logger'
3
4
  require 'rspec'
4
- require 'rspec/collection_matchers'
5
5
 
6
6
  require 'simplecov'
7
7
  require 'simplecov-cobertura'
8
8
 
9
9
  SimpleCov.start do
10
- add_filter 'spec'
10
+ skip %r{\Aspec/}i
11
11
  end
12
12
 
13
13
  SimpleCov.formatters = [
@@ -19,6 +19,9 @@ require 'restify'
19
19
 
20
20
  if ENV['ADAPTER']
21
21
  case ENV['ADAPTER'].to_s.downcase
22
+ when 'ethon'
23
+ require 'restify/adapter/ethon'
24
+ Restify.adapter = Restify::Adapter::Ethon.new
22
25
  when 'typhoeus'
23
26
  require 'restify/adapter/typhoeus'
24
27
  Restify.adapter = Restify::Adapter::Typhoeus.new
@@ -33,6 +36,8 @@ end
33
36
  require_relative 'support/opentelemetry'
34
37
  require_relative 'support/stub_server'
35
38
 
39
+ require 'restify/webmock'
40
+
36
41
  RSpec.configure do |config|
37
42
  config.order = 'random'
38
43
 
@@ -47,11 +52,9 @@ RSpec.configure do |config|
47
52
  skip 'Spec not enabled for current adapter'
48
53
  end
49
54
 
50
- config.before do
51
- Ethon.logger = Logging.logger[Ethon] if defined?(Ethon)
52
-
53
- Logging.logger.root.level = :debug
54
- 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
55
58
  end
56
59
 
57
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