Skip to content

Commit dc7d65b

Browse files
adsteeljonnyom
authored andcommittedNov 4, 2019
[bugfix] handle unexpected response bodies (intercom#492)
* [refactor] add "m" for easier testing * [refactor] address minitest deprecations in request_spec.rb * [refactor] remove unnecessary raise on failure If the JSON parsing errors, `parsed_body` is `nil` and the following line raises the error. * [refactor] DRY code formatting in if/else * [refactor] allow access to pry in tests * [refactor] clarify public methods in Request This will help us better reason about which methods should have tests, and which methods other objects are depending on specific behavior from. * [refactor] clean up request specs Done as a step toward fixing a bug in `#parse_body` - Moves client test to client_spec.rb - Replaces parse_body tests with higher level tests - Makes parse_body private - Organizes and cleans up the request_spec file generally * [refactor] simplify response body parsing `parse_body` was doing three things: - checking for response errors - handling parse errors - actually parsing the response body We get some cleaner code by separating the parsing from the response state checking. * [bugfix] raise an error on unexpected response Some responses do not have an expected error code and have non-JSON bodies that fail to parse. The unexpected `nil` caused runtime errors. This fix raises an error on those responses with more information that will allow developers to find a path forward, either by more fixes to this gem or by handling the new error in their own code. I'm giving this version bump a patch level bump because it looks like all existing functionality is preserved. We will now simply get better information instead of `undefined method '[]' for nil:NilClass`. Addresses intercom#491 * [refactor] inline private methods in Request.
1 parent 681bbc2 commit dc7d65b

File tree

8 files changed

+257
-162
lines changed

8 files changed

+257
-162
lines changed
 

‎README.md

+21
Original file line numberDiff line numberDiff line change
@@ -560,3 +560,24 @@ intercom = Intercom::Client.new(token: ENV['AT'], handle_rate_limit: true)
560560
- **Send coherent history**. Make sure each individual commit in your pull
561561
request is meaningful. If you had to make multiple intermediate commits while
562562
developing, please squash them before sending them to us.
563+
564+
### Development
565+
566+
#### Running tests
567+
568+
```bash
569+
# all tests
570+
bundle exec spec
571+
572+
# unit tests
573+
bundle exec spec:unit
574+
575+
# integration tests
576+
bundle exec spec:integration
577+
578+
# single test file
579+
bundle exec m spec/unit/intercom/job_spec.rb
580+
581+
# single test
582+
bundle exec m spec/unit/intercom/job_spec.rb:49
583+
```

‎intercom.gemspec

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
1919
spec.require_paths = ["lib"]
2020

2121
spec.add_development_dependency 'minitest', '~> 5.4'
22+
spec.add_development_dependency "m", "~> 1.5.0"
2223
spec.add_development_dependency 'rake', '~> 10.3'
2324
spec.add_development_dependency 'mocha', '~> 1.0'
2425
spec.add_development_dependency "fakeweb", ["~> 1.3"]

‎lib/intercom/errors.rb

+3
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ class BadRequestError < IntercomError; end
6363
# Raised when you have exceeded the API rate limit
6464
class RateLimitExceeded < IntercomError; end
6565

66+
# Raised when some attribute of the response cannot be handled
67+
class UnexpectedResponseError < IntercomError; end
68+
6669
# Raised when the request throws an error not accounted for
6770
class UnexpectedError < IntercomError; end
6871

‎lib/intercom/request.rb

+104-84
Original file line numberDiff line numberDiff line change
@@ -3,64 +3,48 @@
33

44
module Intercom
55
class Request
6-
attr_accessor :path, :net_http_method, :rate_limit_details, :handle_rate_limit
7-
8-
def initialize(path, net_http_method)
9-
self.path = path
10-
self.net_http_method = net_http_method
11-
self.handle_rate_limit = false
12-
end
13-
14-
def set_common_headers(method, base_uri)
15-
method.add_field('AcceptEncoding', 'gzip, deflate')
16-
end
17-
18-
def set_basic_auth(method, username, secret)
19-
method.basic_auth(CGI.unescape(username), CGI.unescape(secret))
20-
end
6+
class << self
7+
def get(path, params)
8+
new(path, Net::HTTP::Get.new(append_query_string_to_url(path, params), default_headers))
9+
end
2110

22-
def set_api_version(method, api_version)
23-
method.add_field('Intercom-Version', api_version)
24-
end
11+
def post(path, form_data)
12+
new(path, method_with_body(Net::HTTP::Post, path, form_data))
13+
end
2514

26-
def self.get(path, params)
27-
new(path, Net::HTTP::Get.new(append_query_string_to_url(path, params), default_headers))
28-
end
15+
def delete(path, params)
16+
new(path, method_with_body(Net::HTTP::Delete, path, params))
17+
end
2918

30-
def self.post(path, form_data)
31-
new(path, method_with_body(Net::HTTP::Post, path, form_data))
32-
end
19+
def put(path, form_data)
20+
new(path, method_with_body(Net::HTTP::Put, path, form_data))
21+
end
3322

34-
def self.delete(path, params)
35-
new(path, method_with_body(Net::HTTP::Delete, path, params))
36-
end
23+
private def method_with_body(http_method, path, params)
24+
request = http_method.send(:new, path, default_headers)
25+
request.body = params.to_json
26+
request["Content-Type"] = "application/json"
27+
request
28+
end
3729

38-
def self.put(path, form_data)
39-
new(path, method_with_body(Net::HTTP::Put, path, form_data))
40-
end
30+
private def default_headers
31+
{'Accept-Encoding' => 'gzip, deflate', 'Accept' => 'application/vnd.intercom.3+json', 'User-Agent' => "Intercom-Ruby/#{Intercom::VERSION}"}
32+
end
4133

42-
def self.method_with_body(http_method, path, params)
43-
request = http_method.send(:new, path, default_headers)
44-
request.body = params.to_json
45-
request["Content-Type"] = "application/json"
46-
request
34+
private def append_query_string_to_url(url, params)
35+
return url if params.empty?
36+
query_string = params.map { |k, v| "#{k.to_s}=#{CGI::escape(v.to_s)}" }.join('&')
37+
url + "?#{query_string}"
38+
end
4739
end
4840

49-
def self.default_headers
50-
{'Accept-Encoding' => 'gzip, deflate', 'Accept' => 'application/vnd.intercom.3+json', 'User-Agent' => "Intercom-Ruby/#{Intercom::VERSION}"}
41+
def initialize(path, net_http_method)
42+
self.path = path
43+
self.net_http_method = net_http_method
44+
self.handle_rate_limit = false
5145
end
5246

53-
def client(uri, read_timeout:, open_timeout:)
54-
net = Net::HTTP.new(uri.host, uri.port)
55-
if uri.is_a?(URI::HTTPS)
56-
net.use_ssl = true
57-
net.verify_mode = OpenSSL::SSL::VERIFY_PEER
58-
net.ca_file = File.join(File.dirname(__FILE__), '../data/cacert.pem')
59-
end
60-
net.read_timeout = read_timeout
61-
net.open_timeout = open_timeout
62-
net
63-
end
47+
attr_accessor :handle_rate_limit
6448

6549
def execute(target_base_url=nil, username:, secret: nil, read_timeout: 90, open_timeout: 30, api_version: nil)
6650
retries = 3
@@ -72,10 +56,16 @@ def execute(target_base_url=nil, username:, secret: nil, read_timeout: 90, open_
7256
client(base_uri, read_timeout: read_timeout, open_timeout: open_timeout).start do |http|
7357
begin
7458
response = http.request(net_http_method)
59+
7560
set_rate_limit_details(response)
76-
decoded_body = decode_body(response)
77-
parsed_body = parse_body(decoded_body, response)
7861
raise_errors_on_failure(response)
62+
63+
parsed_body = extract_response_body(response)
64+
65+
return nil if parsed_body.nil?
66+
67+
raise_application_errors_on_failure(parsed_body, response.code.to_i) if parsed_body['type'] == 'error.list'
68+
7969
parsed_body
8070
rescue Intercom::RateLimitExceeded => e
8171
if @handle_rate_limit
@@ -98,55 +88,91 @@ def execute(target_base_url=nil, username:, secret: nil, read_timeout: 90, open_
9888
end
9989
end
10090

101-
def decode_body(response)
102-
decode(response['content-encoding'], response.body)
103-
end
91+
attr_accessor :path,
92+
:net_http_method,
93+
:rate_limit_details
10494

105-
def parse_body(decoded_body, response)
106-
parsed_body = nil
107-
return parsed_body if decoded_body.nil? || decoded_body.strip.empty?
108-
begin
109-
parsed_body = JSON.parse(decoded_body)
110-
rescue JSON::ParserError => _
111-
raise_errors_on_failure(response)
95+
private :path,
96+
:net_http_method,
97+
:rate_limit_details
98+
99+
private def client(uri, read_timeout:, open_timeout:)
100+
net = Net::HTTP.new(uri.host, uri.port)
101+
if uri.is_a?(URI::HTTPS)
102+
net.use_ssl = true
103+
net.verify_mode = OpenSSL::SSL::VERIFY_PEER
104+
net.ca_file = File.join(File.dirname(__FILE__), '../data/cacert.pem')
112105
end
113-
raise_errors_on_failure(response) if parsed_body.nil?
114-
raise_application_errors_on_failure(parsed_body, response.code.to_i) if parsed_body['type'] == 'error.list'
115-
parsed_body
106+
net.read_timeout = read_timeout
107+
net.open_timeout = open_timeout
108+
net
116109
end
117110

118-
def set_rate_limit_details(response)
111+
private def extract_response_body(response)
112+
decoded_body = decode(response['content-encoding'], response.body)
113+
114+
json_parse_response(decoded_body, response.code)
115+
end
116+
117+
private def decode(content_encoding, body)
118+
return body if (!body) || body.empty? || content_encoding != 'gzip'
119+
Zlib::GzipReader.new(StringIO.new(body)).read.force_encoding("utf-8")
120+
end
121+
122+
private def json_parse_response(str, code)
123+
return nil if str.to_s.empty?
124+
125+
JSON.parse(str)
126+
rescue JSON::ParserError
127+
msg = <<~MSG.gsub(/[[:space:]]+/, " ").strip # #squish from ActiveSuppor
128+
Expected a JSON response body. Instead got '#{str}'
129+
with status code '#{code}'.
130+
MSG
131+
132+
raise UnexpectedResponseError, msg
133+
end
134+
135+
private def set_rate_limit_details(response)
119136
rate_limit_details = {}
120137
rate_limit_details[:limit] = response['X-RateLimit-Limit'].to_i if response['X-RateLimit-Limit']
121138
rate_limit_details[:remaining] = response['X-RateLimit-Remaining'].to_i if response['X-RateLimit-Remaining']
122139
rate_limit_details[:reset_at] = Time.at(response['X-RateLimit-Reset'].to_i) if response['X-RateLimit-Reset']
123140
@rate_limit_details = rate_limit_details
124141
end
125142

126-
def decode(content_encoding, body)
127-
return body if (!body) || body.empty? || content_encoding != 'gzip'
128-
Zlib::GzipReader.new(StringIO.new(body)).read.force_encoding("utf-8")
143+
private def set_common_headers(method, base_uri)
144+
method.add_field('AcceptEncoding', 'gzip, deflate')
129145
end
130146

131-
def raise_errors_on_failure(res)
132-
if res.code.to_i.eql?(404)
147+
private def set_basic_auth(method, username, secret)
148+
method.basic_auth(CGI.unescape(username), CGI.unescape(secret))
149+
end
150+
151+
private def set_api_version(method, api_version)
152+
method.add_field('Intercom-Version', api_version)
153+
end
154+
155+
private def raise_errors_on_failure(res)
156+
code = res.code.to_i
157+
158+
if code == 404
133159
raise Intercom::ResourceNotFound.new('Resource Not Found')
134-
elsif res.code.to_i.eql?(401)
160+
elsif code == 401
135161
raise Intercom::AuthenticationError.new('Unauthorized')
136-
elsif res.code.to_i.eql?(403)
162+
elsif code == 403
137163
raise Intercom::AuthenticationError.new('Forbidden')
138-
elsif res.code.to_i.eql?(429)
164+
elsif code == 429
139165
raise Intercom::RateLimitExceeded.new('Rate Limit Exceeded')
140-
elsif res.code.to_i.eql?(500)
166+
elsif code == 500
141167
raise Intercom::ServerError.new('Server Error')
142-
elsif res.code.to_i.eql?(502)
168+
elsif code == 502
143169
raise Intercom::BadGatewayError.new('Bad Gateway Error')
144-
elsif res.code.to_i.eql?(503)
170+
elsif code == 503
145171
raise Intercom::ServiceUnavailableError.new('Service Unavailable')
146172
end
147173
end
148174

149-
def raise_application_errors_on_failure(error_list_details, http_code)
175+
private def raise_application_errors_on_failure(error_list_details, http_code)
150176
# Currently, we don't support multiple errors
151177
error_details = error_list_details['errors'].first
152178
error_code = error_details['type'] || error_details['code']
@@ -198,18 +224,12 @@ def raise_application_errors_on_failure(error_list_details, http_code)
198224
end
199225
end
200226

201-
def message_for_unexpected_error_with_type(error_details, parsed_http_code)
227+
private def message_for_unexpected_error_with_type(error_details, parsed_http_code)
202228
"The error of type '#{error_details['type']}' is not recognized. It occurred with the message: #{error_details['message']} and http_code: '#{parsed_http_code}'. Please contact Intercom with these details."
203229
end
204230

205-
def message_for_unexpected_error_without_type(error_details, parsed_http_code)
231+
private def message_for_unexpected_error_without_type(error_details, parsed_http_code)
206232
"An unexpected error occured. It occurred with the message: #{error_details['message']} and http_code: '#{parsed_http_code}'. Please contact Intercom with these details."
207233
end
208-
209-
def self.append_query_string_to_url(url, params)
210-
return url if params.empty?
211-
query_string = params.map { |k, v| "#{k.to_s}=#{CGI::escape(v.to_s)}" }.join('&')
212-
url + "?#{query_string}"
213-
end
214234
end
215235
end

‎lib/intercom/version.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module Intercom #:nodoc:
2-
VERSION = "3.9.0"
2+
VERSION = "3.9.1"
33
end

‎spec/spec_helper.rb

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require 'mocha/setup'
44
require 'webmock'
55
require 'time'
6+
require 'pry'
67
include WebMock::API
78

89
def test_customer(email="bob@example.com")

‎spec/unit/intercom/client_spec.rb

+11-1
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,22 @@ module Intercom
44
describe Client do
55
let(:app_id) { 'myappid' }
66
let(:api_key) { 'myapikey' }
7-
let(:client) { Client.new(app_id: app_id, api_key: api_key) }
7+
let(:client) do
8+
Client.new(
9+
app_id: app_id,
10+
api_key: api_key,
11+
handle_rate_limit: true
12+
)
13+
end
814

915
it 'should set the base url' do
1016
client.base_url.must_equal('https://api.intercom.io')
1117
end
1218

19+
it 'should have handle_rate_limit set' do
20+
_(client.handle_rate_limit).must_equal(true)
21+
end
22+
1323
it 'should be able to change the base url' do
1424
prev = client.options(Intercom::Client.set_base_url('https://mymockintercom.io'))
1525
client.base_url.must_equal('https://mymockintercom.io')

‎spec/unit/intercom/request_spec.rb

+115-76
Original file line numberDiff line numberDiff line change
@@ -3,107 +3,146 @@
33

44
WebMock.enable!
55

6-
describe 'Intercom::Request' do
7-
it 'raises an error when a html error page rendered' do
8-
response = OpenStruct.new(:code => 500)
9-
req = Intercom::Request.new('path/', 'GET')
10-
proc {req.parse_body('<html>something</html>', response)}.must_raise(Intercom::ServerError)
6+
describe 'Intercom::Request', '#execute' do
7+
let(:uri) {"https://api.intercom.io/users"}
8+
let(:req) { Intercom::Request.get(uri, {}) }
9+
let(:default_body) { { data: "test" }.to_json }
10+
11+
def execute!
12+
req.execute(uri, username: 'ted', secret: '')
1113
end
1214

13-
it 'raises a RateLimitExceeded error when the response code is 429' do
14-
response = OpenStruct.new(:code => 429)
15-
req = Intercom::Request.new('path/', 'GET')
16-
proc {req.parse_body('<html>something</html>', response)}.must_raise(Intercom::RateLimitExceeded)
15+
it 'should call sleep for rate limit error three times and raise a rate limit error otherwise' do
16+
stub_request(:any, uri).to_return(
17+
status: [429, "Too Many Requests"],
18+
headers: { 'X-RateLimit-Reset' => (Time.now.utc + 10).to_i.to_s },
19+
body: default_body
20+
)
21+
22+
req.handle_rate_limit=true
23+
24+
req.expects(:sleep).times(3).with(any_parameters)
25+
26+
expect { execute! }.must_raise(Intercom::RateLimitExceeded)
1727
end
1828

19-
it 'parse_body raises an error if the decoded_body is "null"' do
20-
response = OpenStruct.new(:code => 500)
21-
req = Intercom::Request.new('path/', 'GET')
22-
proc { req.parse_body('null', response)}.must_raise(Intercom::ServerError)
29+
it 'should not call sleep for rate limit error' do
30+
stub_request(:any, uri).to_return(
31+
status: [200, "OK"],
32+
headers: { 'X-RateLimit-Reset' => Time.now.utc + 10 },
33+
body: default_body
34+
)
35+
36+
req.handle_rate_limit=true
37+
req.expects(:sleep).never.with(any_parameters)
38+
39+
execute!
2340
end
2441

25-
describe 'Intercom::Client' do
26-
let(:client) { Intercom::Client.new(token: 'foo', handle_rate_limit: true) }
27-
let(:uri) {"https://api.intercom.io/users"}
42+
it 'should call sleep for rate limit error just once' do
43+
stub_request(:any, uri).to_return(
44+
status: [429, "Too Many Requests"],
45+
headers: { 'X-RateLimit-Reset' => (Time.now.utc + 10).to_i.to_s },
46+
).then.to_return(status: [200, "OK"], body: default_body)
2847

29-
it 'should have handle_rate_limit set' do
30-
client.handle_rate_limit.must_equal(true)
31-
end
48+
req.handle_rate_limit=true
49+
req.expects(:sleep).with(any_parameters)
50+
51+
execute!
52+
end
53+
54+
it 'should not sleep if rate limit reset time has passed' do
55+
stub_request(:any, uri).to_return(
56+
status: [429, "Too Many Requests"],
57+
headers: { 'X-RateLimit-Reset' => Time.parse("February 25 2010").utc.to_i.to_s },
58+
body: default_body
59+
).then.to_return(status: [200, "OK"], body: default_body)
60+
61+
req.handle_rate_limit=true
62+
req.expects(:sleep).never.with(any_parameters)
63+
64+
execute!
65+
end
66+
67+
it 'handles an empty body gracefully' do
68+
stub_request(:any, uri).to_return(
69+
status: 200,
70+
body: nil
71+
)
3272

33-
it 'should call sleep for rate limit error three times and raise a rate limit error otherwise' do
34-
expect {
35-
stub_request(:any, uri).\
36-
to_return(status: [429, "Too Many Requests"], headers: { 'X-RateLimit-Reset' => (Time.now.utc + 10).to_i.to_s })
37-
req = Intercom::Request.get(uri, "")
38-
req.handle_rate_limit=true
39-
req.expects(:sleep).times(3).with(any_parameters)
40-
req.execute(uri, username: "ted", secret: "")
41-
}.must_raise(Intercom::RateLimitExceeded)
73+
assert_nil(execute!)
74+
end
75+
76+
describe 'HTTP error handling' do
77+
it 'raises an error when the response is successful but the body is not JSON' do
78+
stub_request(:any, uri).to_return(
79+
status: 200,
80+
body: '<html>something</html>'
81+
)
82+
83+
expect { execute! }.must_raise(Intercom::UnexpectedResponseError)
4284
end
4385

44-
it 'should not call sleep for rate limit error' do
45-
# Use webmock to mock the HTTP request
46-
stub_request(:any, uri).\
47-
to_return(status: [200, "OK"], headers: { 'X-RateLimit-Reset' => Time.now.utc + 10 })
48-
req = Intercom::Request.get(uri, "")
49-
req.handle_rate_limit=true
50-
req.expects(:sleep).never.with(any_parameters)
51-
req.execute(uri, username: "ted", secret: "")
86+
it 'raises an error when an html error page rendered' do
87+
stub_request(:any, uri).to_return(
88+
status: 500,
89+
body: '<html>something</html>'
90+
)
91+
92+
expect { execute! }.must_raise(Intercom::ServerError)
5293
end
5394

54-
it 'should call sleep for rate limit error just once' do
55-
# Use webmock to mock the HTTP request
56-
stub_request(:any, uri).\
57-
to_return(status: [429, "Too Many Requests"], headers: { 'X-RateLimit-Reset' => (Time.now.utc + 10).to_i.to_s }).\
58-
then.to_return(status: [200, "OK"])
59-
req = Intercom::Request.get(uri, "")
60-
req.handle_rate_limit=true
61-
req.expects(:sleep).with(any_parameters)
62-
req.execute(uri, username: "ted", secret: "")
95+
it 'raises an error if the decoded_body is "null"' do
96+
stub_request(:any, uri).to_return(
97+
status: 500,
98+
body: 'null'
99+
)
100+
101+
expect { execute! }.must_raise(Intercom::ServerError)
63102
end
64103

65-
it 'should not sleep if rate limit reset time has passed' do
66-
# Use webmock to mock the HTTP request
67-
stub_request(:any, uri).\
68-
to_return(status: [429, "Too Many Requests"], headers: { 'X-RateLimit-Reset' => Time.parse("February 25 2010").utc.to_i.to_s }).\
69-
then.to_return(status: [200, "OK"])
70-
req = Intercom::Request.get(uri, "")
71-
req.handle_rate_limit=true
72-
req.expects(:sleep).never.with(any_parameters)
73-
req.execute(uri, username: "ted", secret: "")
104+
it 'raises a RateLimitExceeded error when the response code is 429' do
105+
stub_request(:any, uri).to_return(
106+
status: 429,
107+
body: 'null'
108+
)
109+
110+
expect { execute! }.must_raise(Intercom::RateLimitExceeded)
74111
end
75112
end
76113

77-
78-
describe "Application errors on failure" do
114+
describe "application error handling" do
79115
let(:uri) {"https://api.intercom.io/conversations/reply"}
116+
let(:req) { Intercom::Request.put(uri, {}) }
117+
80118
it 'should raise ResourceNotUniqueError error on resource_conflict code' do
81-
# Use webmock to mock the HTTP request
82-
stub_request(:put, uri).\
83-
to_return(status: [409, "Resource Already Exists"], headers: { 'X-RateLimit-Reset' => (Time.now.utc + 10).to_i.to_s }, body: {type: "error.list", errors: [ code: "resource_conflict" ]}.to_json)
84-
req = Intercom::Request.put(uri, "")
85-
expect { req.execute(uri, username: "ted", secret: "") }.must_raise(Intercom::ResourceNotUniqueError)
119+
stub_request(:put, uri).to_return(
120+
status: [409, "Resource Already Exists"],
121+
headers: { 'X-RateLimit-Reset' => (Time.now.utc + 10).to_i.to_s },
122+
body: { type: "error.list", errors: [ code: "resource_conflict" ] }.to_json
123+
)
124+
125+
expect { execute! }.must_raise(Intercom::ResourceNotUniqueError)
86126
end
87127

88128
it 'should raise ApiVersionInvalid error on intercom_version_invalid code' do
89-
# Use webmock to mock the HTTP request
90-
stub_request(:put, uri).\
91-
to_return(status: [400, "Bad Request"], headers: { 'X-RateLimit-Reset' => (Time.now.utc + 10).to_i.to_s }, body: {type: "error.list", errors: [ code: "intercom_version_invalid" ]}.to_json)
92-
req = Intercom::Request.put(uri, "")
93-
expect { req.execute(uri, username: "ted", secret: "") }.must_raise(Intercom::ApiVersionInvalid)
129+
stub_request(:put, uri).to_return(
130+
status: [400, "Bad Request"],
131+
headers: { 'X-RateLimit-Reset' => (Time.now.utc + 10).to_i.to_s },
132+
body: { type: "error.list", errors: [ code: "intercom_version_invalid" ] }.to_json
133+
)
134+
135+
expect { execute! }.must_raise(Intercom::ApiVersionInvalid)
94136
end
95137

96138
it 'should raise ResourceNotFound error on company_not_found code' do
97-
stub_request(:put, uri).\
98-
to_return(status: [404, "Not Found"], headers: { 'X-RateLimit-Reset' => (Time.now.utc + 10).to_i.to_s }, body: {type: "error.list", errors: [ code: "company_not_found" ]}.to_json)
99-
req = Intercom::Request.put(uri, "")
100-
expect { req.execute(uri, username: "ted", secret: "") }.must_raise(Intercom::ResourceNotFound)
101-
end
102-
end
139+
stub_request(:put, uri).to_return(
140+
status: [404, "Not Found"],
141+
headers: { 'X-RateLimit-Reset' => (Time.now.utc + 10).to_i.to_s },
142+
body: { type: "error.list", errors: [ code: "company_not_found" ] }.to_json
143+
)
103144

104-
it 'parse_body returns nil if decoded_body is nil' do
105-
response = OpenStruct.new(:code => 500)
106-
req = Intercom::Request.new('path/', 'GET')
107-
assert_nil(req.parse_body(nil, response))
145+
expect { execute! }.must_raise(Intercom::ResourceNotFound)
146+
end
108147
end
109148
end

0 commit comments

Comments
 (0)
Please sign in to comment.