Skip to content

Commit

Permalink
Defaulting code property on error objects
Browse files Browse the repository at this point in the history
  • Loading branch information
Drewfergusson committed Jun 14, 2021
1 parent f896aa5 commit b40be7c
Show file tree
Hide file tree
Showing 11 changed files with 78 additions and 25 deletions.
21 changes: 11 additions & 10 deletions readme.md
Expand Up @@ -1705,43 +1705,44 @@ Additionaly, the errors may have `request` (Got Stream) and `response` (Got Resp
#### got.RequestError
When a request fails. Contains a `code` property with error class code, like `ECONNREFUSED`. All the errors below inherit this one.
When a request fails. Contains a `code` property with error class code, like `ECONNREFUSED`. If there is no specific code supplied `code` defaults to `ERR_GEN_REQ_ERROR` All the errors below inherit this one.
#### got.CacheError
When a cache method fails, for example, if the database goes down or there's a filesystem error.
When a cache method fails, for example, if the database goes down or there's a filesystem error. Contains a `code` property with `ERR_CACHE_ACCESS` or a more specific failure code.
#### got.ReadError
When reading from response stream fails.
When reading from response stream fails. Contains a `code` property with `ERR_READING_RESPONSE_STREAM` or a more specific failure code.
#### got.ParseError
When server response code is 2xx, and parsing body fails. Includes a `response` property.
When server response code is 2xx, and parsing body fails. Includes a `response` property. Contains a `code` property with `ERR_BODY_PARSE_FAILURE` or a more specific failure code.
#### got.UploadError
When the request body is a stream and an error occurs while reading from that stream.
When the request body is a stream and an error occurs while reading from that stream. Contains a `code` property with `ERR_UPLOAD` or a more specific failure code.
#### got.HTTPError
When the server response code is not 2xx nor 3xx if `options.followRedirect` is `true`, but always except for 304. Includes a `response` property.
When the server response code is not 2xx nor 3xx if `options.followRedirect` is `true`, but always except for 304. Includes a `response` property. Contains a `code` property with `ERR_NON_2XX_3XX_RESPONSE` or a more specific failure code.
#### got.MaxRedirectsError
When the server redirects you more than ten times. Includes a `response` property.
When the server redirects you more than ten times. Includes a `response` property. Contains a `code` property with `ERR_MAX_REDIRECTS`.
#### got.UnsupportedProtocolError
When given an unsupported protocol.
When given an unsupported protocol. Contains a `code` property with `ERR_UNSUPPORTED_PROTOCOL`.
#### got.TimeoutError
When the request is aborted due to a [timeout](#timeout). Includes an `event` and `timings` property.
When the request is aborted due to a [timeout](#timeout). Includes an `event` and `timings` property. Contains a `code` property with `ETIMEDOUT`.
#### got.CancelError
When the request is aborted with `.cancel()`.
When the request is aborted with `.cancel()`. Contains a `code` property with `ERR_CANCELED`.
## Aborting the request
Expand Down
2 changes: 2 additions & 0 deletions source/as-promise/types.ts
Expand Up @@ -267,6 +267,7 @@ export class ParseError extends RequestError {

super(`${error.message} in "${options.url.toString()}"`, error, response.request);
this.name = 'ParseError';
this.code = this.code === 'ERR_GEN_REQ_ERROR' ? 'ERR_BODY_PARSE_FAILURE' : this.code;
}
}

Expand All @@ -279,6 +280,7 @@ export class CancelError extends RequestError {
constructor(request: Request) {
super('Promise was canceled', {}, request);
this.name = 'CancelError';
this.code = 'ERR_CANCELED';
}

get isCanceled() {
Expand Down
8 changes: 7 additions & 1 deletion source/core/index.ts
Expand Up @@ -1191,7 +1191,7 @@ export class RequestError extends Error {
Error.captureStackTrace(this, this.constructor);

this.name = 'RequestError';
this.code = error.code;
this.code = error.code ?? 'ERR_GEN_REQ_ERROR';

if (self instanceof Request) {
Object.defineProperty(this, 'request', {
Expand Down Expand Up @@ -1249,6 +1249,7 @@ export class MaxRedirectsError extends RequestError {
constructor(request: Request) {
super(`Redirected ${request.options.maxRedirects} times. Aborting.`, {}, request);
this.name = 'MaxRedirectsError';
this.code = 'ERR_MAX_REDIRECTS';
}
}

Expand All @@ -1264,6 +1265,7 @@ export class HTTPError extends RequestError {
constructor(response: Response) {
super(`Response code ${response.statusCode} (${response.statusMessage!})`, {}, response.request);
this.name = 'HTTPError';
this.code = 'ERR_NON_2XX_3XX_RESPONSE';
}
}
/**
Expand All @@ -1276,6 +1278,7 @@ export class CacheError extends RequestError {
constructor(error: Error, request: Request) {
super(error.message, error, request);
this.name = 'CacheError';
this.code = this.code === 'ERR_GEN_REQ_ERROR' ? 'ERR_CACHE_ACCESS' : this.code;
}
}

Expand All @@ -1288,6 +1291,7 @@ export class UploadError extends RequestError {
constructor(error: Error, request: Request) {
super(error.message, error, request);
this.name = 'UploadError';
this.code = this.code === 'ERR_GEN_REQ_ERROR' ? 'ERR_UPLOAD' : this.code;
}
}

Expand Down Expand Up @@ -1319,6 +1323,7 @@ export class ReadError extends RequestError {
constructor(error: Error, request: Request) {
super(error.message, error, request);
this.name = 'ReadError';
this.code = this.code === 'ERR_GEN_REQ_ERROR' ? 'ERR_READING_RESPONSE_STREAM' : this.code;
}
}

Expand All @@ -1329,6 +1334,7 @@ export class UnsupportedProtocolError extends RequestError {
constructor(options: NormalizedOptions) {
super(`Unsupported protocol "${options.url.protocol}"`, {}, options);
this.name = 'UnsupportedProtocolError';
this.code = 'ERR_UNSUPPORTED_PROTOCOL';
}
}

Expand Down
5 changes: 4 additions & 1 deletion test/cache.ts
Expand Up @@ -120,7 +120,10 @@ test('cache error throws `got.CacheError`', withServer, async (t, server, got) =
const cache = {};

// @ts-expect-error Error tests
await t.throwsAsync(got({cache}), {instanceOf: got.CacheError});
await t.throwsAsync(got({cache}), {
instanceOf: got.CacheError,
code: 'ERR_CACHE_ACCESS'
});
});

test('doesn\'t cache response when received HTTP error', withServer, async (t, server, got) => {
Expand Down
35 changes: 28 additions & 7 deletions test/cancel.ts
Expand Up @@ -79,7 +79,10 @@ test.serial('does not retry after cancelation', withServerAndFakeTimers, async (
gotPromise.cancel();
});

await t.throwsAsync(gotPromise, {instanceOf: CancelError});
await t.throwsAsync(gotPromise, {
instanceOf: CancelError,
code: 'ERR_CANCELED'
});
await t.notThrowsAsync(promise, 'Request finished instead of aborting.');
});

Expand All @@ -102,7 +105,10 @@ test.serial('cleans up request timeouts', withServer, async (t, server, got) =>
}
});

await t.throwsAsync(gotPromise, {instanceOf: CancelError});
await t.throwsAsync(gotPromise, {
instanceOf: CancelError,
code: 'ERR_CANCELED'
});

// Wait for unhandled errors
await delay(40);
Expand All @@ -124,7 +130,10 @@ test.serial('cancels in-progress request', withServerAndFakeTimers, async (t, se
body.push(null);
});

await t.throwsAsync(gotPromise, {instanceOf: CancelError});
await t.throwsAsync(gotPromise, {
instanceOf: CancelError,
code: 'ERR_CANCELED'
});
await t.notThrowsAsync(promise, 'Request finished instead of aborting.');
});

Expand All @@ -144,7 +153,10 @@ test.serial('cancels in-progress request with timeout', withServerAndFakeTimers,
body.push(null);
});

await t.throwsAsync(gotPromise, {instanceOf: CancelError});
await t.throwsAsync(gotPromise, {
instanceOf: CancelError,
code: 'ERR_CANCELED'
});
await t.notThrowsAsync(promise, 'Request finished instead of aborting.');
});

Expand Down Expand Up @@ -220,7 +232,10 @@ test.serial('throws on incomplete (canceled) response - promise #2', withServerA
promise.cancel();
});

await t.throwsAsync(promise, {instanceOf: got.CancelError});
await t.throwsAsync(promise, {
instanceOf: got.CancelError,
code: 'ERR_CANCELED'
});
});

test.serial('throws on incomplete (canceled) response - stream', withServerAndFakeTimers, async (t, server, got, clock) => {
Expand Down Expand Up @@ -250,7 +265,10 @@ test('throws when canceling cached request', withServer, async (t, server, got)
promise.cancel();
});

await t.throwsAsync(promise, {instanceOf: got.CancelError});
await t.throwsAsync(promise, {
instanceOf: got.CancelError,
code: 'ERR_CANCELED'
});
});

test('throws when canceling cached request #2', withServer, async (t, server, got) => {
Expand All @@ -265,5 +283,8 @@ test('throws when canceling cached request #2', withServer, async (t, server, go
const promise = got({cache});
promise.cancel();

await t.throwsAsync(promise, {instanceOf: got.CancelError});
await t.throwsAsync(promise, {
instanceOf: got.CancelError,
code: 'ERR_CANCELED'
});
});
7 changes: 5 additions & 2 deletions test/error.ts
Expand Up @@ -28,7 +28,7 @@ test('properties', withServer, async (t, server, got) => {
// Class fields will always be initialized, even though they are undefined
// A test to check for undefined is in place below
// t.false({}.hasOwnProperty.call(error, 'code'));
t.is(error.code, undefined);
t.is(error.code, 'ERR_NON_2XX_3XX_RESPONSE');
t.is(error.message, 'Response code 404 (Not Found)');
t.deepEqual(error.options.url, url);
t.is(error.response.headers.connection, 'close');
Expand All @@ -41,6 +41,7 @@ test('catches dns errors', async t => {
t.regex(error.message, /ENOTFOUND|EAI_AGAIN/);
t.is(error.options.url.host, 'doesntexist');
t.is(error.options.method, 'GET');
t.is(error.code, 'ENOTFOUND');
});

test('`options.body` form error message', async t => {
Expand Down Expand Up @@ -130,6 +131,7 @@ test('`http.request` error', async t => {
}
}), {
instanceOf: got.RequestError,
code: 'ERR_GEN_REQ_ERROR',
message: 'The header content contains invalid characters'
});
});
Expand Down Expand Up @@ -215,7 +217,8 @@ test('promise does not hang on timeout on HTTP error', withServer, async (t, ser
await t.throwsAsync(got({
timeout: 100
}), {
instanceOf: TimeoutError
instanceOf: TimeoutError,
code: 'ETIMEDOUT'
});
});

Expand Down
2 changes: 2 additions & 0 deletions test/http.ts
Expand Up @@ -92,6 +92,7 @@ test('doesn\'t throw if `options.throwHttpErrors` is false', withServer, async (
test('invalid protocol throws', async t => {
await t.throwsAsync(got('c:/nope.com').json(), {
instanceOf: UnsupportedProtocolError,
code: 'ERR_UNSUPPORTED_PROTOCOL',
message: 'Unsupported protocol "c:"'
});
});
Expand Down Expand Up @@ -225,6 +226,7 @@ test('throws an error if the server aborted the request', withServer, async (t,
});

const error = await t.throwsAsync<ReadError>(got(''), {
code: 'ECONNRESET',
message: 'The server aborted pending request'
});

Expand Down
1 change: 1 addition & 0 deletions test/post.ts
Expand Up @@ -354,6 +354,7 @@ test('throws on upload error', withServer, async (t, server, got) => {
}
})), {
instanceOf: UploadError,
code: 'ERR_UPLOAD',
message
});
});
10 changes: 8 additions & 2 deletions test/promise.ts
Expand Up @@ -71,8 +71,14 @@ test('promise.json() can be called before a file stream body is open', withServe

const promise = got({body});
const checks = [
t.throwsAsync(promise, {instanceOf: CancelError}),
t.throwsAsync(promise.json(), {instanceOf: CancelError})
t.throwsAsync(promise, {
instanceOf: CancelError,
code: 'ERR_CANCELED'
}),
t.throwsAsync(promise.json(), {
instanceOf: CancelError,
code: 'ERR_CANCELED'
})
];

promise.cancel();
Expand Down
8 changes: 6 additions & 2 deletions test/redirects.ts
Expand Up @@ -84,6 +84,7 @@ test('throws on endless redirects - default behavior', withServer, async (t, ser
const error = await t.throwsAsync<MaxRedirectsError>(got(''), {message: 'Redirected 10 times. Aborting.'});

t.deepEqual(error.response.redirectUrls, new Array(10).fill(`${server.url}/`));
t.is(error.code, 'ERR_MAX_REDIRECTS');
});

test('custom `maxRedirects` option', withServer, async (t, server, got) => {
Expand All @@ -97,6 +98,7 @@ test('custom `maxRedirects` option', withServer, async (t, server, got) => {
const error = await t.throwsAsync<MaxRedirectsError>(got('', {maxRedirects: 5}), {message: 'Redirected 5 times. Aborting.'});

t.deepEqual(error.response.redirectUrls, new Array(5).fill(`${server.url}/`));
t.is(error.code, 'ERR_MAX_REDIRECTS');
});

test('searchParams are not breaking redirects', withServer, async (t, server, got) => {
Expand All @@ -123,7 +125,8 @@ test('redirects GET and HEAD requests', withServer, async (t, server, got) => {
});

await t.throwsAsync(got.get(''), {
instanceOf: got.MaxRedirectsError
instanceOf: got.MaxRedirectsError,
code: 'ERR_MAX_REDIRECTS'
});
});

Expand All @@ -136,7 +139,8 @@ test('redirects POST requests', withServer, async (t, server, got) => {
});

await t.throwsAsync(got.post({body: 'wow'}), {
instanceOf: got.MaxRedirectsError
instanceOf: got.MaxRedirectsError,
code: 'ERR_MAX_REDIRECTS'
});
});

Expand Down
4 changes: 4 additions & 0 deletions test/response-parse.ts
Expand Up @@ -90,6 +90,7 @@ test('throws an error on invalid response type', withServer, async (t, server, g
t.regex(error.message, /^Unknown body type 'invalid'/);
t.true(error.message.includes(error.options.url.hostname));
t.is(error.options.url.pathname, '/');
t.is(error.code, 'ERR_BODY_PARSE_FAILURE');
});

test('wraps parsing errors', withServer, async (t, server, got) => {
Expand All @@ -100,6 +101,7 @@ test('wraps parsing errors', withServer, async (t, server, got) => {
const error = await t.throwsAsync<ParseError>(got({responseType: 'json'}), {instanceOf: got.ParseError});
t.true(error.message.includes(error.options.url.hostname));
t.is(error.options.url.pathname, '/');
t.is(error.code, 'ERR_BODY_PARSE_FAILURE');
});

test('parses non-200 responses', withServer, async (t, server, got) => {
Expand Down Expand Up @@ -136,6 +138,7 @@ test('parse errors have `response` property', withServer, async (t, server, got)

t.is(error.response.statusCode, 200);
t.is(error.response.body, '/');
t.is(error.code, 'ERR_BODY_PARSE_FAILURE');
});

test('sets correct headers', withServer, async (t, server, got) => {
Expand Down Expand Up @@ -186,6 +189,7 @@ test('shortcuts throw ParseErrors', withServer, async (t, server, got) => {

await t.throwsAsync(got('').json(), {
instanceOf: ParseError,
code: 'ERR_BODY_PARSE_FAILURE',
message: /^Unexpected token o in JSON at position 1 in/
});
});
Expand Down

0 comments on commit b40be7c

Please sign in to comment.