restify 1.13.0 → 1.15.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +98 -6
- data/lib/restify/adapter/em.rb +6 -13
- data/lib/restify/adapter/pooled_em.rb +35 -40
- data/lib/restify/adapter/typhoeus.rb +57 -51
- data/lib/restify/context.rb +5 -9
- data/lib/restify/error.rb +24 -0
- data/lib/restify/global.rb +1 -0
- data/lib/restify/logging.rb +1 -1
- data/lib/restify/processors/base/parsing.rb +5 -9
- data/lib/restify/processors/base.rb +2 -6
- data/lib/restify/promise.rb +1 -3
- data/lib/restify/request.rb +13 -5
- data/lib/restify/resource.rb +2 -2
- data/lib/restify/response.rb +0 -2
- data/lib/restify/timeout.rb +1 -3
- data/lib/restify/version.rb +3 -3
- data/spec/restify/cache_spec.rb +2 -2
- data/spec/restify/context_spec.rb +10 -7
- data/spec/restify/error_spec.rb +10 -0
- data/spec/restify/features/head_requests_spec.rb +7 -7
- data/spec/restify/features/request_bodies_spec.rb +84 -0
- data/spec/restify/features/request_errors_spec.rb +19 -0
- data/spec/restify/features/request_headers_spec.rb +16 -17
- data/spec/restify/features/response_errors_spec.rb +127 -0
- data/spec/restify/global_spec.rb +6 -6
- data/spec/restify/link_spec.rb +9 -9
- data/spec/restify/processors/base_spec.rb +1 -0
- data/spec/restify/processors/json_spec.rb +2 -1
- data/spec/restify/processors/msgpack_spec.rb +8 -7
- data/spec/restify/promise_spec.rb +8 -4
- data/spec/restify/registry_spec.rb +2 -2
- data/spec/restify/relation_spec.rb +18 -17
- data/spec/restify/resource_spec.rb +9 -8
- data/spec/restify/timeout_spec.rb +4 -4
- data/spec/restify_spec.rb +52 -57
- data/spec/spec_helper.rb +11 -8
- data/spec/support/stub_server.rb +106 -0
- metadata +30 -23
- data/spec/restify/features/response_errors.rb +0 -79
data/lib/restify/global.rb
CHANGED
data/lib/restify/logging.rb
CHANGED
@@ -21,12 +21,10 @@ module Restify
|
|
21
21
|
def parse(object, root: false)
|
22
22
|
case object
|
23
23
|
when Hash
|
24
|
-
data = object.each_with_object({},
|
25
|
-
relations = object.each_with_object({},
|
24
|
+
data = object.each_with_object({}) {|each, obj| parse_data(each, obj) }
|
25
|
+
relations = object.each_with_object({}) {|each, obj| parse_rels(each, obj) }
|
26
26
|
|
27
|
-
if self.class.indifferent_access?
|
28
|
-
data = with_indifferent_access(data)
|
29
|
-
end
|
27
|
+
data = with_indifferent_access(data) if self.class.indifferent_access?
|
30
28
|
|
31
29
|
Resource.new context,
|
32
30
|
data: data,
|
@@ -34,7 +32,7 @@ module Restify
|
|
34
32
|
relations: relations
|
35
33
|
|
36
34
|
when Array
|
37
|
-
object.map(
|
35
|
+
object.map {|each| parse(each) }
|
38
36
|
else
|
39
37
|
object
|
40
38
|
end
|
@@ -56,9 +54,7 @@ module Restify
|
|
56
54
|
return
|
57
55
|
end
|
58
56
|
|
59
|
-
if relations.key?(name) || pair[1].nil? || pair[1].to_s =~ /\A\w*\z/
|
60
|
-
return
|
61
|
-
end
|
57
|
+
return if relations.key?(name) || pair[1].nil? || pair[1].to_s =~ /\A\w*\z/
|
62
58
|
|
63
59
|
relations[name] = pair[1].to_s
|
64
60
|
end
|
@@ -5,9 +5,7 @@ module Restify
|
|
5
5
|
class Base
|
6
6
|
extend Forwardable
|
7
7
|
|
8
|
-
attr_reader :context
|
9
|
-
|
10
|
-
attr_reader :response
|
8
|
+
attr_reader :context, :response
|
11
9
|
|
12
10
|
def initialize(context, response)
|
13
11
|
@context = context
|
@@ -18,9 +16,7 @@ module Restify
|
|
18
16
|
@resource ||= begin
|
19
17
|
resource = load
|
20
18
|
|
21
|
-
unless resource.is_a? Restify::Resource
|
22
|
-
resource = Resource.new context, response: response, data: resource
|
23
|
-
end
|
19
|
+
resource = Resource.new context, response: response, data: resource unless resource.is_a? Restify::Resource
|
24
20
|
|
25
21
|
resource._restify_response = response
|
26
22
|
merge_relations! resource._restify_relations
|
data/lib/restify/promise.rb
CHANGED
@@ -11,9 +11,7 @@ module Restify
|
|
11
11
|
# When dependencies were passed in, but none are left after flattening,
|
12
12
|
# then we don't have to wait for explicit dependencies or resolution
|
13
13
|
# through a writer.
|
14
|
-
if !@task && @dependencies.empty? && dependencies.any?
|
15
|
-
complete true, [], nil
|
16
|
-
end
|
14
|
+
complete true, [], nil if !@task && @dependencies.empty? && dependencies.any?
|
17
15
|
end
|
18
16
|
|
19
17
|
def wait(timeout = nil)
|
data/lib/restify/request.rb
CHANGED
@@ -34,18 +34,26 @@ module Restify
|
|
34
34
|
@uri = opts.fetch(:uri) { raise ArgumentError.new ':uri required.' }
|
35
35
|
@data = opts.fetch(:data, nil)
|
36
36
|
@timeout = opts.fetch(:timeout, 300)
|
37
|
-
@headers = opts.fetch(:headers, {})
|
38
|
-
|
37
|
+
@headers = opts.fetch(:headers, {})
|
38
|
+
|
39
|
+
@headers['Content-Type'] ||= 'application/json' if json?
|
39
40
|
end
|
40
41
|
|
41
42
|
def body
|
42
|
-
@body ||=
|
43
|
-
JSON.dump(data) unless data.nil?
|
44
|
-
end
|
43
|
+
@body ||= json? ? JSON.dump(@data) : @data
|
45
44
|
end
|
46
45
|
|
47
46
|
def to_s
|
48
47
|
"#<#{self.class} #{method.upcase} #{uri}>"
|
49
48
|
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def json?
|
53
|
+
return false if @data.nil?
|
54
|
+
return false if @data.is_a? String
|
55
|
+
|
56
|
+
true
|
57
|
+
end
|
50
58
|
end
|
51
59
|
end
|
data/lib/restify/resource.rb
CHANGED
@@ -96,8 +96,8 @@ module Restify
|
|
96
96
|
def inspect
|
97
97
|
text = {
|
98
98
|
'@data' => data,
|
99
|
-
'@relations' => @relations
|
100
|
-
}.map {|k, v| k
|
99
|
+
'@relations' => @relations,
|
100
|
+
}.map {|k, v| "#{k}=#{v.inspect}" }.join(' ')
|
101
101
|
|
102
102
|
"#<#{self.class} #{text}>"
|
103
103
|
end
|
data/lib/restify/response.rb
CHANGED
@@ -89,7 +89,6 @@ module Restify
|
|
89
89
|
#
|
90
90
|
# @return [Array<Link>] Links.
|
91
91
|
#
|
92
|
-
# rubocop:disable Metrics/MethodLength
|
93
92
|
def links
|
94
93
|
@links ||= begin
|
95
94
|
if headers['LINK']
|
@@ -104,7 +103,6 @@ module Restify
|
|
104
103
|
end
|
105
104
|
end
|
106
105
|
end
|
107
|
-
# rubocop:enable all
|
108
106
|
|
109
107
|
# Return content type header from response headers.
|
110
108
|
#
|
data/lib/restify/timeout.rb
CHANGED
@@ -51,9 +51,7 @@ module Restify
|
|
51
51
|
"Timeout must be an number but is #{value}"
|
52
52
|
end
|
53
53
|
|
54
|
-
|
55
|
-
raise ArgumentError.new "Timeout must be > 0 but is #{value.inspect}."
|
56
|
-
end
|
54
|
+
raise ArgumentError.new "Timeout must be > 0 but is #{value.inspect}." unless value.positive?
|
57
55
|
|
58
56
|
value
|
59
57
|
end
|
data/lib/restify/version.rb
CHANGED
@@ -3,10 +3,10 @@
|
|
3
3
|
module Restify
|
4
4
|
module VERSION
|
5
5
|
MAJOR = 1
|
6
|
-
MINOR =
|
7
|
-
PATCH =
|
6
|
+
MINOR = 15
|
7
|
+
PATCH = 2
|
8
8
|
STAGE = nil
|
9
|
-
STRING = [MAJOR, MINOR, PATCH, STAGE].
|
9
|
+
STRING = [MAJOR, MINOR, PATCH, STAGE].compact.join('.').freeze
|
10
10
|
|
11
11
|
def self.to_s
|
12
12
|
STRING
|
data/spec/restify/cache_spec.rb
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe Restify::Cache do
|
6
|
+
subject { cache }
|
7
|
+
|
6
8
|
let(:store) { double 'store' }
|
7
9
|
let(:cache) { described_class.new store }
|
8
10
|
|
9
|
-
subject { cache }
|
10
|
-
|
11
11
|
describe '#call' do
|
12
12
|
let(:request) { double 'request' }
|
13
13
|
let(:promise0) { double 'promise0' }
|
@@ -18,27 +18,30 @@ describe Restify::Context do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
describe '#adapter' do
|
21
|
-
let(:kwargs) { {adapter: double('adapter')} }
|
22
21
|
subject { super().options[:adapter] }
|
23
22
|
|
23
|
+
let(:kwargs) { {adapter: double('adapter')} }
|
24
|
+
|
24
25
|
it 'adapter is not serialized' do
|
25
26
|
expect(subject).to equal nil
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
29
30
|
describe '#cache' do
|
30
|
-
let(:kwargs) { {adapter: double('cache')} }
|
31
31
|
subject { super().options[:cache] }
|
32
32
|
|
33
|
+
let(:kwargs) { {adapter: double('cache')} }
|
34
|
+
|
33
35
|
it 'cache is not serialized' do
|
34
36
|
expect(subject).to equal nil
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
38
40
|
describe '#headers' do
|
39
|
-
let(:kwargs) { {headers: {'Accept' => 'application/json'}} }
|
40
41
|
subject { super().options[:headers] }
|
41
42
|
|
43
|
+
let(:kwargs) { {headers: {'Accept' => 'application/json'}} }
|
44
|
+
|
42
45
|
it 'all headers are serialized' do
|
43
46
|
expect(subject).to eq('Accept' => 'application/json')
|
44
47
|
end
|
@@ -46,20 +49,20 @@ describe Restify::Context do
|
|
46
49
|
end
|
47
50
|
|
48
51
|
context 'YAML' do
|
52
|
+
subject { load }
|
53
|
+
|
49
54
|
let(:dump) { YAML.dump(context) }
|
50
55
|
let(:load) { YAML.load(dump) } # rubocop:disable Security/YAMLLoad
|
51
56
|
|
52
|
-
subject { load }
|
53
|
-
|
54
57
|
include_examples 'serialization'
|
55
58
|
end
|
56
59
|
|
57
60
|
context 'Marshall' do
|
61
|
+
subject { load }
|
62
|
+
|
58
63
|
let(:dump) { Marshal.dump(context) }
|
59
64
|
let(:load) { Marshal.load(dump) } # rubocop:disable Security/MarshalLoad
|
60
65
|
|
61
|
-
subject { load }
|
62
|
-
|
63
66
|
include_examples 'serialization'
|
64
67
|
end
|
65
68
|
end
|
data/spec/restify/error_spec.rb
CHANGED
@@ -19,51 +19,61 @@ describe Restify::ResponseError do
|
|
19
19
|
|
20
20
|
context 'with 400 Bad Request' do
|
21
21
|
let(:code) { 400 }
|
22
|
+
|
22
23
|
it { is_expected.to be_a ::Restify::BadRequest }
|
23
24
|
end
|
24
25
|
|
25
26
|
context 'with 401 Unauthorized' do
|
26
27
|
let(:code) { 401 }
|
28
|
+
|
27
29
|
it { is_expected.to be_a ::Restify::Unauthorized }
|
28
30
|
end
|
29
31
|
|
30
32
|
context 'with 404 Unauthorized' do
|
31
33
|
let(:code) { 404 }
|
34
|
+
|
32
35
|
it { is_expected.to be_a ::Restify::NotFound }
|
33
36
|
end
|
34
37
|
|
35
38
|
context 'with 406 Not Acceptable' do
|
36
39
|
let(:code) { 406 }
|
40
|
+
|
37
41
|
it { is_expected.to be_a ::Restify::NotAcceptable }
|
38
42
|
end
|
39
43
|
|
40
44
|
context 'with 410 Gone' do
|
41
45
|
let(:code) { 410 }
|
46
|
+
|
42
47
|
it { is_expected.to be_a ::Restify::Gone }
|
43
48
|
end
|
44
49
|
|
45
50
|
context 'with 422 Unprocessable Entity' do
|
46
51
|
let(:code) { 422 }
|
52
|
+
|
47
53
|
it { is_expected.to be_a ::Restify::UnprocessableEntity }
|
48
54
|
end
|
49
55
|
|
50
56
|
context 'with 500 Internal Server Error' do
|
51
57
|
let(:code) { 500 }
|
58
|
+
|
52
59
|
it { is_expected.to be_a ::Restify::InternalServerError }
|
53
60
|
end
|
54
61
|
|
55
62
|
context 'with 502 Bad Gateway' do
|
56
63
|
let(:code) { 502 }
|
64
|
+
|
57
65
|
it { is_expected.to be_a ::Restify::BadGateway }
|
58
66
|
end
|
59
67
|
|
60
68
|
context 'with 503 Service Unavailable' do
|
61
69
|
let(:code) { 503 }
|
70
|
+
|
62
71
|
it { is_expected.to be_a ::Restify::ServiceUnavailable }
|
63
72
|
end
|
64
73
|
|
65
74
|
context 'with 504 Gateway Timeout' do
|
66
75
|
let(:code) { 504 }
|
76
|
+
|
67
77
|
it { is_expected.to be_a ::Restify::GatewayTimeout }
|
68
78
|
end
|
69
79
|
end
|
@@ -4,20 +4,20 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe Restify do
|
6
6
|
let!(:request_stub) do
|
7
|
-
stub_request(:head, 'http://
|
7
|
+
stub_request(:head, 'http://stubserver/base')
|
8
8
|
.with(query: hash_including({}))
|
9
9
|
.to_return do
|
10
|
-
|
10
|
+
<<~HTTP
|
11
11
|
HTTP/1.1 200 OK
|
12
12
|
Content-Length: 333
|
13
|
-
|
14
|
-
|
15
|
-
RESPONSE
|
13
|
+
Link: <http://localhost:9292/other>; rel="neat"
|
14
|
+
HTTP
|
16
15
|
end
|
17
16
|
end
|
18
17
|
|
19
18
|
describe 'HEAD requests' do
|
20
|
-
subject { Restify.new('http://localhost/base').head(params).value! }
|
19
|
+
subject { Restify.new('http://localhost:9292/base').head(params).value! }
|
20
|
+
|
21
21
|
let(:params) { {} }
|
22
22
|
|
23
23
|
it 'returns a resource with access to headers' do
|
@@ -34,7 +34,7 @@ describe Restify do
|
|
34
34
|
it 'adds them to the query string' do
|
35
35
|
subject
|
36
36
|
expect(
|
37
|
-
request_stub.with(query: {foo: 'bar'})
|
37
|
+
request_stub.with(query: {foo: 'bar'}),
|
38
38
|
).to have_been_requested
|
39
39
|
end
|
40
40
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Restify do
|
6
|
+
let!(:request_stub) do
|
7
|
+
stub_request(:post, 'http://stubserver/base').to_return do
|
8
|
+
<<~HTTP
|
9
|
+
HTTP/1.1 200 OK
|
10
|
+
Link: <http://localhost:9292/other>; rel="neat"
|
11
|
+
HTTP
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'Request body' do
|
16
|
+
subject { Restify.new('http://localhost:9292/base').post(body, {}, {headers: headers}).value! }
|
17
|
+
|
18
|
+
let(:headers) { {} }
|
19
|
+
|
20
|
+
context 'with JSON-like data structures' do
|
21
|
+
let(:body) { {a: 'b', c: 'd'} }
|
22
|
+
|
23
|
+
it 'is serialized as JSON' do
|
24
|
+
subject
|
25
|
+
|
26
|
+
expect(
|
27
|
+
request_stub.with(body: '{"a":"b","c":"d"}'),
|
28
|
+
).to have_been_requested
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'gets a JSON media type for free' do
|
32
|
+
subject
|
33
|
+
|
34
|
+
expect(
|
35
|
+
request_stub.with(headers: {'Content-Type' => 'application/json'}),
|
36
|
+
).to have_been_requested
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'with overridden media type' do
|
40
|
+
let(:headers) { {'Content-Type' => 'application/vnd.api+json'} }
|
41
|
+
|
42
|
+
it 'respects the override' do
|
43
|
+
subject
|
44
|
+
|
45
|
+
expect(
|
46
|
+
request_stub.with(headers: {'Content-Type' => 'application/vnd.api+json'}),
|
47
|
+
).to have_been_requested
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'with strings' do
|
53
|
+
let(:body) { 'a=b&c=d' }
|
54
|
+
|
55
|
+
it 'is sent as provided' do
|
56
|
+
subject
|
57
|
+
|
58
|
+
expect(
|
59
|
+
request_stub.with(body: 'a=b&c=d'),
|
60
|
+
).to have_been_requested
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'does not get a JSON media type' do
|
64
|
+
subject
|
65
|
+
|
66
|
+
expect(
|
67
|
+
request_stub.with {|req| req.headers['Content-Type'] !~ /json/ },
|
68
|
+
).to have_been_requested
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with overridden media type' do
|
72
|
+
let(:headers) { {'Content-Type' => 'application/text'} }
|
73
|
+
|
74
|
+
it 'respects the override' do
|
75
|
+
subject
|
76
|
+
|
77
|
+
expect(
|
78
|
+
request_stub.with(headers: {'Content-Type' => 'application/text'}),
|
79
|
+
).to have_been_requested
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Restify, adapter: ::Restify::Adapter::Typhoeus do
|
6
|
+
before do
|
7
|
+
stub_request(:get, 'http://stubserver/base').to_timeout
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'Timeout' do
|
11
|
+
subject(:request) { Restify.new('http://localhost:9292/base').get({}, timeout: 0.1).value! }
|
12
|
+
|
13
|
+
it 'throws a network error' do
|
14
|
+
expect { request }.to raise_error Restify::NetworkError do |error|
|
15
|
+
expect(error.message).to match(/timeout/i)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -4,32 +4,31 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe Restify do
|
6
6
|
let!(:request_stub) do
|
7
|
-
stub_request(:get, 'http://
|
8
|
-
|
7
|
+
stub_request(:get, 'http://stubserver/base').to_return do
|
8
|
+
<<~HTTP
|
9
9
|
HTTP/1.1 200 OK
|
10
10
|
Content-Type: application/json
|
11
|
-
|
12
|
-
Link: <http://localhost/base>; rel="self"
|
11
|
+
Link: <http://localhost:9292/base>; rel="self"
|
13
12
|
|
14
13
|
{ "response": "success" }
|
15
|
-
|
14
|
+
HTTP
|
16
15
|
end
|
17
16
|
end
|
18
17
|
|
19
18
|
context 'with request headers configured for a single request' do
|
20
|
-
let(:context) { Restify.new('http://localhost/base') }
|
19
|
+
let(:context) { Restify.new('http://localhost:9292/base') }
|
21
20
|
|
22
21
|
it 'sends the headers only for that request' do
|
23
22
|
root = context.get(
|
24
23
|
{},
|
25
|
-
{headers: {'Accept' => 'application/msgpack, application/json'}}
|
24
|
+
{headers: {'Accept' => 'application/msgpack, application/json'}},
|
26
25
|
).value!
|
27
26
|
|
28
27
|
root.rel(:self).get.value!
|
29
28
|
|
30
29
|
expect(request_stub).to have_been_requested.twice
|
31
30
|
expect(
|
32
|
-
request_stub.with(headers: {'Accept' => 'application/msgpack, application/json'})
|
31
|
+
request_stub.with(headers: {'Accept' => 'application/msgpack, application/json'}),
|
33
32
|
).to have_been_requested.once
|
34
33
|
end
|
35
34
|
end
|
@@ -37,8 +36,8 @@ describe Restify do
|
|
37
36
|
context 'with request headers configured for context' do
|
38
37
|
let(:context) do
|
39
38
|
Restify.new(
|
40
|
-
'http://localhost/base',
|
41
|
-
headers: {'Accept' => 'application/msgpack, application/json'}
|
39
|
+
'http://localhost:9292/base',
|
40
|
+
headers: {'Accept' => 'application/msgpack, application/json'},
|
42
41
|
)
|
43
42
|
end
|
44
43
|
|
@@ -48,39 +47,39 @@ describe Restify do
|
|
48
47
|
root.rel(:self).get.value!
|
49
48
|
|
50
49
|
expect(
|
51
|
-
request_stub.with(headers: {'Accept' => 'application/msgpack, application/json'})
|
50
|
+
request_stub.with(headers: {'Accept' => 'application/msgpack, application/json'}),
|
52
51
|
).to have_been_requested.twice
|
53
52
|
end
|
54
53
|
|
55
54
|
it 'can overwrite headers for single requests' do
|
56
55
|
root = context.get(
|
57
56
|
{},
|
58
|
-
{headers: {'Accept' => 'application/xml'}}
|
57
|
+
{headers: {'Accept' => 'application/xml'}},
|
59
58
|
).value!
|
60
59
|
|
61
60
|
root.rel(:self).get.value!
|
62
61
|
|
63
62
|
expect(
|
64
|
-
request_stub.with(headers: {'Accept' => 'application/xml'})
|
63
|
+
request_stub.with(headers: {'Accept' => 'application/xml'}),
|
65
64
|
).to have_been_requested.once
|
66
65
|
expect(
|
67
|
-
request_stub.with(headers: {'Accept' => 'application/msgpack, application/json'})
|
66
|
+
request_stub.with(headers: {'Accept' => 'application/msgpack, application/json'}),
|
68
67
|
).to have_been_requested.once
|
69
68
|
end
|
70
69
|
|
71
70
|
it 'can add additional headers for single requests' do
|
72
71
|
root = context.get(
|
73
72
|
{},
|
74
|
-
{headers: {'X-Custom' => 'foobar'}}
|
73
|
+
{headers: {'X-Custom' => 'foobar'}},
|
75
74
|
).value!
|
76
75
|
|
77
76
|
root.rel(:self).get.value!
|
78
77
|
|
79
78
|
expect(
|
80
|
-
request_stub.with(headers: {'Accept' => 'application/msgpack, application/json'})
|
79
|
+
request_stub.with(headers: {'Accept' => 'application/msgpack, application/json'}),
|
81
80
|
).to have_been_requested.twice
|
82
81
|
expect(
|
83
|
-
request_stub.with(headers: {'Accept' => 'application/msgpack, application/json', 'X-Custom' => 'foobar'})
|
82
|
+
request_stub.with(headers: {'Accept' => 'application/msgpack, application/json', 'X-Custom' => 'foobar'}),
|
84
83
|
).to have_been_requested.once
|
85
84
|
end
|
86
85
|
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Restify do
|
6
|
+
before do
|
7
|
+
stub_request(:get, 'http://stubserver/base')
|
8
|
+
.to_return(status: http_status, headers: headers)
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:http_status) { '200 OK' }
|
12
|
+
let(:headers) { {} }
|
13
|
+
|
14
|
+
describe 'Error handling' do
|
15
|
+
subject(:request) { Restify.new('http://localhost:9292/base').get.value! }
|
16
|
+
|
17
|
+
context 'for 400 status codes' do
|
18
|
+
let(:http_status) { '400 Bad Request' }
|
19
|
+
|
20
|
+
it 'throws a BadRequest exception' do
|
21
|
+
expect { request }.to raise_error Restify::BadRequest
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'for 401 status codes' do
|
26
|
+
let(:http_status) { '401 Unauthorized' }
|
27
|
+
|
28
|
+
it 'throws an Unauthorized exception' do
|
29
|
+
expect { request }.to raise_error Restify::Unauthorized
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'for 404 status codes' do
|
34
|
+
let(:http_status) { '404 Not Found' }
|
35
|
+
|
36
|
+
it 'throws a ClientError exception' do
|
37
|
+
expect { request }.to raise_error Restify::NotFound
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'for 406 status codes' do
|
42
|
+
let(:http_status) { '406 Not Acceptable' }
|
43
|
+
|
44
|
+
it 'throws a NotAcceptable exception' do
|
45
|
+
expect { request }.to raise_error Restify::NotAcceptable
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'for 422 status codes' do
|
50
|
+
let(:http_status) { '422 Unprocessable Entity' }
|
51
|
+
|
52
|
+
it 'throws a UnprocessableEntity exception' do
|
53
|
+
expect { request }.to raise_error Restify::UnprocessableEntity
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'for 429 status codes' do
|
58
|
+
let(:http_status) { '429 Too Many Requests' }
|
59
|
+
|
60
|
+
it 'throws a TooManyRequests exception' do
|
61
|
+
expect { request }.to raise_error Restify::TooManyRequests
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'the exception' do
|
65
|
+
subject(:exception) do
|
66
|
+
exception = nil
|
67
|
+
begin
|
68
|
+
request
|
69
|
+
rescue Restify::TooManyRequests => e
|
70
|
+
exception = e
|
71
|
+
end
|
72
|
+
exception
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'by default' do
|
76
|
+
it 'does not know when to retry again' do
|
77
|
+
expect(exception.retry_after).to be_nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'with Retry-After header containing seconds' do
|
82
|
+
let(:headers) { {'Retry-After' => '120'} }
|
83
|
+
|
84
|
+
it 'determines the date correctly' do
|
85
|
+
now = DateTime.now
|
86
|
+
lower = now + Rational(119, 86_400)
|
87
|
+
upper = now + Rational(121, 86_400)
|
88
|
+
|
89
|
+
expect(exception.retry_after).to be_between(lower, upper)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'with Retry-After header containing HTTP date' do
|
94
|
+
let(:headers) { {'Retry-After' => 'Sun, 13 Mar 2033 13:03:33 GMT'} }
|
95
|
+
|
96
|
+
it 'parses the date correctly' do
|
97
|
+
expect(exception.retry_after.to_s).to eq '2033-03-13T13:03:33+00:00'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'with Retry-After header containing invalid date string' do
|
102
|
+
let(:headers) { {'Retry-After' => 'tomorrow 12:00:00'} }
|
103
|
+
|
104
|
+
it 'does not know when to retry again' do
|
105
|
+
expect(exception.retry_after).to be_nil
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'for any other 4xx status codes' do
|
112
|
+
let(:http_status) { '415 Unsupported Media Type' }
|
113
|
+
|
114
|
+
it 'throws a generic ClientError exception' do
|
115
|
+
expect { request }.to raise_error Restify::ClientError
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'for any 5xx status codes' do
|
120
|
+
let(:http_status) { '500 Internal Server Error' }
|
121
|
+
|
122
|
+
it 'throws a generic ServerError exception' do
|
123
|
+
expect { request }.to raise_error Restify::ServerError
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|