73 changes: 57 additions & 16 deletions test/arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ test('`url` is required', async t => {
// @ts-expect-error No argument on purpose.
got(),
{
instanceOf: RequestError,
message: 'Missing `url` property',
},
);
Expand All @@ -30,6 +31,7 @@ test('`url` should be utf-8 encoded', async t => {
await t.throwsAsync(
got('https://example.com/%D2%E0%EB%EB%E8%ED'),
{
instanceOf: RequestError,
message: 'URI malformed',
},
);
Expand All @@ -38,12 +40,14 @@ test('`url` should be utf-8 encoded', async t => {
test('throws if no arguments provided', async t => {
// @ts-expect-error Error tests
await t.throwsAsync(got(), {
instanceOf: RequestError,
message: 'Missing `url` property',
});
});

test('throws if the url option is missing', async t => {
await t.throwsAsync(got({}), {
instanceOf: RequestError,
message: 'Missing `url` property',
});
});
Expand Down Expand Up @@ -93,17 +97,22 @@ test('throws an error when legacy URL is passed', withServer, async (t, server)
await t.throwsAsync(
// @ts-expect-error Error tests
got(parse(`${server.url}/test`)),
{
instanceOf: RequestError,
message: 'Expected value which is `predicate returns truthy for any value`, received values of types `Object`.',
},
);

// TODO: Assert message above.

await t.throwsAsync(
got({
protocol: 'http:',
hostname: 'localhost',
port: server.port,
} as any),
{message: 'Unexpected option: protocol'},
{
instanceOf: RequestError,
message: 'Unexpected option: protocol',
},
);
});

Expand Down Expand Up @@ -163,6 +172,7 @@ test('ignores empty searchParams object', withServer, async (t, server, got) =>

test('throws when passing body with a non payload method', async t => {
await t.throwsAsync(got('https://example.com', {body: 'asdf'}), {
instanceOf: RequestError,
message: 'The `GET` method cannot be used with a body',
});
});
Expand Down Expand Up @@ -206,6 +216,7 @@ test('throws when `options.hooks` is not an object', async t => {
// @ts-expect-error Error tests
got('https://example.com', {hooks: 'not object'}),
{
instanceOf: RequestError,
message: 'Expected value which is `Object`, received value of type `string`.',
},
);
Expand All @@ -215,16 +226,19 @@ test('throws when known `options.hooks` value is not an array', async t => {
await t.throwsAsync(
// @ts-expect-error Error tests
got('https://example.com', {hooks: {beforeRequest: {}}}),
{
instanceOf: RequestError,
message: 'Expected value which is `predicate returns truthy for any value`, received values of types `Object`.',
},
);

// TODO: Assert message above.
});

test('throws when known `options.hooks` array item is not a function', async t => {
await t.throwsAsync(
// @ts-expect-error Error tests
got('https://example.com', {hooks: {beforeRequest: [{}]}}),
{
instanceOf: RequestError,
message: 'Expected value which is `Function`, received value of type `Object`.',
},
);
Expand All @@ -235,6 +249,7 @@ test('does not allow extra keys in `options.hooks`', withServer, async (t, serve

// @ts-expect-error Error tests
await t.throwsAsync(got('test', {hooks: {extra: []}}), {
instanceOf: RequestError,
message: 'Unexpected hook event: extra',
});
});
Expand Down Expand Up @@ -286,9 +301,11 @@ test('throws if the `searchParams` value is invalid', async t => {
// @ts-expect-error Error tests
foo: [],
},
}));

// TODO: Assert message above.
}),
{
instanceOf: RequestError,
message: 'Expected value which is `predicate returns truthy for any value`, received values of types `Array`.',
});
});

test.failing('`context` option is enumerable', withServer, async (t, server, got) => {
Expand Down Expand Up @@ -365,20 +382,29 @@ test('throws if `options.encoding` is `null`', async t => {
await t.throwsAsync(got('https://example.com', {
// @ts-expect-error For testing purposes
encoding: null,
}), {message: 'To get a Buffer, set `options.responseType` to `buffer` instead'});
}), {
instanceOf: RequestError,
message: 'To get a Buffer, set `options.responseType` to `buffer` instead',
});
});

test('`url` option and input argument are mutually exclusive', async t => {
await t.throwsAsync(got('https://example.com', {
url: 'https://example.com',
}), {message: 'The `url` option is mutually exclusive with the `input` argument'});
}), {
instanceOf: RequestError,
message: 'The `url` option is mutually exclusive with the `input` argument',
});
});

test('throws a helpful error when passing `followRedirects`', async t => {
await t.throwsAsync(got('https://example.com', {
// @ts-expect-error For testing purposes
followRedirects: true,
}), {message: 'The `followRedirects` option does not exist. Use `followRedirect` instead.'});
}), {
instanceOf: RequestError,
message: 'The `followRedirects` option does not exist. Use `followRedirect` instead.',
});
});

test('merges `searchParams` instances', t => {
Expand All @@ -400,12 +426,14 @@ test('throws a helpful error when passing `auth`', async t => {
// @ts-expect-error For testing purposes
auth: 'username:password',
}), {
instanceOf: RequestError,
message: 'Parameter `auth` is deprecated. Use `username` / `password` instead.',
});
});

test('throws on leading slashes', async t => {
await t.throwsAsync(got('/asdf', {prefixUrl: 'https://example.com'}), {
instanceOf: RequestError,
message: '`url` must not start with a slash',
});
});
Expand All @@ -414,9 +442,11 @@ test('throws on invalid `dnsCache` option', async t => {
await t.throwsAsync(got('https://example.com', {
// @ts-expect-error Error tests
dnsCache: 123,
}));

// TODO: Assert message above.
}),
{
instanceOf: RequestError,
message: 'Expected value which is `predicate returns truthy for any value`, received values of types `number`.',
});
});

test('throws on invalid `agent` option', async t => {
Expand All @@ -425,7 +455,10 @@ test('throws on invalid `agent` option', async t => {
// @ts-expect-error Error tests
asdf: 123,
},
}), {message: 'Unexpected agent option: asdf'});
}), {
instanceOf: RequestError,
message: 'Unexpected agent option: asdf',
});
});

test('fallbacks to native http if `request(...)` returns undefined', withServer, async (t, server, got) => {
Expand Down Expand Up @@ -557,6 +590,7 @@ test('throws on too large noise', t => {
},
});
}, {
instanceOf: Error,
message: 'The maximum acceptable retry noise is +/- 100ms, got 101',
});

Expand All @@ -567,6 +601,7 @@ test('throws on too large noise', t => {
},
});
}, {
instanceOf: Error,
message: 'The maximum acceptable retry noise is +/- 100ms, got -101',
});

Expand All @@ -577,6 +612,7 @@ test('throws on too large noise', t => {
},
});
}, {
instanceOf: Error,
message: 'The maximum acceptable retry noise is +/- 100ms, got Infinity',
});

Expand All @@ -587,6 +623,7 @@ test('throws on too large noise', t => {
},
});
}, {
instanceOf: Error,
message: 'The maximum acceptable retry noise is +/- 100ms, got -Infinity',
});

Expand All @@ -608,6 +645,7 @@ test('options have url even if some are invalid', async t => {
}));

t.is((error.options.url as URL).href, 'https://example.com/');
t.true(error instanceof Error);
});

test('options have url even if some are invalid - got.extend', async t => {
Expand All @@ -623,5 +661,8 @@ test('options have url even if some are invalid - got.extend', async t => {
await t.throwsAsync(instance('https://example.com', {
// @ts-expect-error Testing purposes
invalid: true,
}));
}),
{
instanceOf: Error,
});
});
17 changes: 11 additions & 6 deletions test/cancel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import delay from 'delay';
import {pEvent} from 'p-event';
import getStream from 'get-stream';
import {Handler} from 'express';
import got, {CancelError, TimeoutError} from '../source/index.js';
import got, {CancelError, TimeoutError, RequestError} from '../source/index.js';
import slowDataStream from './helpers/slow-data-stream.js';
import {GlobalClock} from './helpers/types.js';
import {ExtendedHttpTestServer} from './helpers/create-http-test-server.js';
Expand Down Expand Up @@ -180,7 +180,10 @@ test.serial('cancel immediately', withServerAndFakeTimers, async (t, server, got
const gotPromise = got('abort');
gotPromise.cancel();

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

Expand Down Expand Up @@ -230,9 +233,8 @@ test.serial('throws on incomplete (canceled) response - promise', withServerAndF
);
});

// TODO: Use `fakeTimers` here
test.serial('throws on incomplete (canceled) response - promise #2', withServer, async (t, server, got) => {
server.get('/', downloadHandler());
test.serial('throws on incomplete (canceled) response - promise #2', withServerAndFakeTimers, async (t, server, got, clock) => {
server.get('/', downloadHandler(clock));

const promise = got('');

Expand All @@ -256,7 +258,10 @@ test.serial('throws on incomplete (canceled) response - stream', withServerAndFa
stream.destroy(new Error(errorString));
});

await t.throwsAsync(getStream(stream), {message: errorString});
await t.throwsAsync(getStream(stream), {
instanceOf: RequestError,
message: errorString,
});
});

test('throws when canceling cached request', withServer, async (t, server, got) => {
Expand Down
27 changes: 21 additions & 6 deletions test/cookies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import net from 'net';
import test from 'ava';
import toughCookie from 'tough-cookie';
import delay from 'delay';
import got from '../source/index.js';
import {got, RequestError} from '../source/index.js';
import withServer from './helpers/with-server.js';

test('reads a cookie', withServer, async (t, server, got) => {
Expand Down Expand Up @@ -63,7 +63,10 @@ test('throws on invalid cookies', withServer, async (t, server, got) => {

const cookieJar = new toughCookie.CookieJar();

await t.throwsAsync(got({cookieJar}), {message: 'Cookie has domain set to a public suffix'});
await t.throwsAsync(got({cookieJar}), {
instanceOf: RequestError,
message: 'Cookie has domain set to a public suffix',
});
});

test('does not throw on invalid cookies when options.ignoreInvalidCookies is set', withServer, async (t, server, got) => {
Expand Down Expand Up @@ -98,7 +101,10 @@ test('catches store errors', async t => {
synchronous: false,
});

await t.throwsAsync(got('https://example.com', {cookieJar}), {message: error});
await t.throwsAsync(got('https://example.com', {cookieJar}), {
instanceOf: RequestError,
message: error,
});
});

test('overrides options.headers.cookie', withServer, async (t, server, got) => {
Expand Down Expand Up @@ -139,7 +145,10 @@ test('no unhandled errors', async t => {
},
};

await t.throwsAsync(got(`http://127.0.0.1:${(server.address() as net.AddressInfo).port}`, options), {message});
await t.throwsAsync(got(`http://127.0.0.1:${(server.address() as net.AddressInfo).port}`, options), {
instanceOf: RequestError,
message,
});
await delay(500);
t.pass();

Expand Down Expand Up @@ -177,7 +186,10 @@ test('throws on invalid `options.cookieJar.setCookie`', async t => {
// @ts-expect-error Error tests
setCookie: 123,
},
}), {message: 'Expected value which is `Function`, received value of type `number`.'});
}), {
instanceOf: RequestError,
message: 'Expected value which is `Function`, received value of type `number`.',
});
});

test('throws on invalid `options.cookieJar.getCookieString`', async t => {
Expand All @@ -187,7 +199,10 @@ test('throws on invalid `options.cookieJar.getCookieString`', async t => {
// @ts-expect-error Error tests
getCookieString: 123,
},
}), {message: 'Expected value which is `Function`, received value of type `number`.'});
}), {
instanceOf: RequestError,
message: 'Expected value which is `Function`, received value of type `number`.',
});
});

test('cookies are cleared when redirecting to a different hostname (no cookieJar)', withServer, async (t, server, got) => {
Expand Down
5 changes: 4 additions & 1 deletion test/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,10 @@ test('async handlers can throw', async t => {
],
});

await t.throwsAsync(instance('https://example.com'), {message});
await t.throwsAsync(instance('https://example.com'), {
instanceOf: Error,
message,
});
});

test('setting dnsCache to true points to global cache', t => {
Expand Down
37 changes: 32 additions & 5 deletions test/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ test('catches dns errors', async t => {
test('`options.body` form error message', async t => {
// @ts-expect-error Error tests
await t.throwsAsync(got.post('https://example.com', {body: Buffer.from('test'), form: ''}),
{
instanceOf: RequestError,
message: 'Expected value which is `predicate returns truthy for any value`, received values of types `string`.',
},
// {message: 'The `body`, `json` and `form` options are mutually exclusive'}
);
});
Expand All @@ -71,7 +75,11 @@ test('default status message', withServer, async (t, server, got) => {
response.end('body');
});

const error = await t.throwsAsync<HTTPError>(got(''));
const error = await t.throwsAsync<HTTPError>(got(''),
{
instanceOf: HTTPError,
message: 'Response code 400 (Bad Request)',
});
t.is(error.response.statusCode, 400);
t.is(error.response.statusMessage, 'Bad Request');
});
Expand All @@ -83,7 +91,11 @@ test('custom status message', withServer, async (t, server, got) => {
response.end('body');
});

const error = await t.throwsAsync<HTTPError>(got(''));
const error = await t.throwsAsync<HTTPError>(got(''),
{
instanceOf: HTTPError,
message: 'Response code 400 (Something Exploded)',
});
t.is(error.response.statusCode, 400);
t.is(error.response.statusMessage, 'Something Exploded');
});
Expand All @@ -94,7 +106,11 @@ test('custom body', withServer, async (t, server, got) => {
response.end('not');
});

const error = await t.throwsAsync<HTTPError>(got(''));
const error = await t.throwsAsync<HTTPError>(got(''),
{
instanceOf: HTTPError,
message: 'Response code 404 (Not Found)',
});
t.is(error.response.statusCode, 404);
t.is(error.response.body, 'not');
});
Expand All @@ -111,7 +127,11 @@ test('contains Got options', withServer, async (t, server, got) => {
},
} as const;

const error = await t.throwsAsync<HTTPError>(got(options));
const error = await t.throwsAsync<HTTPError>(got(options),
{
instanceOf: HTTPError,
message: 'Response code 404 (Not Found)',
});
t.is(error.response.statusCode, 404);
t.is(error.options.context.foo, options.context.foo);
});
Expand All @@ -122,7 +142,11 @@ test('empty status message is overriden by the default one', withServer, async (
response.end('body');
});

const error = await t.throwsAsync<HTTPError>(got(''));
const error = await t.throwsAsync<HTTPError>(got(''),
{
instanceOf: HTTPError,
message: 'Response code 400 (Bad Request)',
});
t.is(error.response.statusCode, 400);
t.is(error.response.statusMessage, http.STATUS_CODES[400]);
});
Expand Down Expand Up @@ -193,6 +217,7 @@ test('returns a stream even if normalization fails', async t => {
}) as unknown as Request;

await t.throwsAsync(getStream(stream), {
instanceOf: RequestError,
message: 'Expected value which is `Object`, received value of type `boolean`.',
});
});
Expand Down Expand Up @@ -265,6 +290,7 @@ test('no uncaught parse errors', async t => {
});

await t.throwsAsync(got.head(`http://localhost:${(server.address() as net.AddressInfo).port}`), {
instanceOf: RequestError,
message: /^Parse Error/,
});

Expand All @@ -290,6 +316,7 @@ test('no uncaught parse errors #2', async t => {
});

await t.throwsAsync(got(`http://localhost:${(server.address() as net.AddressInfo).port}`), {
instanceOf: RequestError,
message: /^Parse Error/,
});

Expand Down
40 changes: 40 additions & 0 deletions test/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,3 +375,43 @@ test('ClientRequest can throw before promise resolves', async t => {
message: /EINVAL|EHOSTUNREACH|ETIMEDOUT/,
});
});

test('status code 200 has response ok is true', withServer, async (t, server, got) => {
server.get('/', (_request, response) => {
response.statusCode = 200;
response.end();
});

const promise = got('');
await t.notThrowsAsync(promise);
const {statusCode, body, ok} = await promise;
t.true(ok);
t.is(statusCode, 200);
t.is(body, '');
});

test('status code 404 has response ok is false if error is not thrown', withServer, async (t, server, got) => {
server.get('/', (_request, response) => {
response.statusCode = 404;
response.end();
});

const promise = got('', {throwHttpErrors: false});
await t.notThrowsAsync(promise);
const {statusCode, body, ok} = await promise;
t.is(ok, false);
t.is(statusCode, 404);
t.is(body, '');
});

test('status code 404 has error response ok is false if error is thrown', withServer, async (t, server, got) => {
server.get('/', (_request, response) => {
response.statusCode = 404;
response.end('not');
});

const error = await t.throwsAsync<HTTPError>(got(''), {instanceOf: HTTPError});
t.is(error.response.statusCode, 404);
t.is(error.response.ok, false);
t.is(error.response.body, 'not');
});
31 changes: 30 additions & 1 deletion test/redirects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Buffer} from 'buffer';
import test from 'ava';
import {Handler} from 'express';
import nock from 'nock';
import got, {MaxRedirectsError} from '../source/index.js';
import got, {MaxRedirectsError, RequestError} from '../source/index.js';
import withServer, {withHttpsServer} from './helpers/with-server.js';

const reachedHandler: Handler = (_request, response) => {
Expand All @@ -28,6 +28,35 @@ const relativeHandler: Handler = (_request, response) => {
response.end();
};

const unixProtocol: Handler = (_request, response) => {
response.writeHead(302, {
location: 'unix:/var/run/docker.sock:/containers/json',
});
response.end();
};

const unixHostname: Handler = (_request, response) => {
response.writeHead(302, {
location: 'http://unix:/var/run/docker.sock:/containers/json',
});
response.end();
};

test('cannot redirect to unix protocol', withServer, async (t, server, got) => {
server.get('/protocol', unixProtocol);
server.get('/hostname', unixHostname);

await t.throwsAsync(got('protocol'), {
message: 'Cannot redirect to UNIX socket',
instanceOf: RequestError,
});

await t.throwsAsync(got('hostname'), {
message: 'Cannot redirect to UNIX socket',
instanceOf: RequestError,
});
});

test('follows redirect', withServer, async (t, server, got) => {
server.get('/', reachedHandler);
server.get('/finite', finiteHandler);
Expand Down
34 changes: 0 additions & 34 deletions test/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,26 +95,6 @@ test('returns writeable stream', withServer, async (t, server, got) => {
t.is(await promise, 'wow');
});

test('throws on write if body is specified', withServer, (t, server, got) => {
server.post('/', postHandler);

const streams = [
got.stream.post({body: 'wow'}),
got.stream.post({json: {}}),
got.stream.post({form: {}}),
];

for (const stream of streams) {
t.throws(() => {
stream.end('wow');
}, {
message: 'The payload has been already provided',
});

stream.destroy();
}
});

test('does not throw if using stream and passing a json option', withServer, async (t, server, got) => {
server.post('/', postHandler);

Expand All @@ -127,20 +107,6 @@ test('does not throw if using stream and passing a form option', withServer, asy
await t.notThrowsAsync(getStream(got.stream.post({form: {}})));
});

test('throws on write if no payload method is present', withServer, (t, server, got) => {
server.post('/', postHandler);

const stream = got.stream.get('');

t.throws(() => {
stream.end('wow');
}, {
message: 'The payload has been already provided',
});

stream.destroy();
});

test('has request event', withServer, async (t, server, got) => {
server.get('/', defaultHandler);

Expand Down
15 changes: 15 additions & 0 deletions test/unix-socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ const okHandler: Handler = (_request, response) => {
response.end('ok');
};

const redirectHandler: Handler = (_request, response) => {
response.writeHead(302, {
location: 'foo',
});
response.end();
};

if (process.platform !== 'win32') {
test('works', withSocketServer, async (t, server) => {
server.on('/', okHandler);
Expand Down Expand Up @@ -53,4 +60,12 @@ if (process.platform !== 'win32') {
const url = format('http://unix:%s:%s', server.socketPath, '/?a=1');
t.is((await got(url)).body, 'ok');
});

test('redirects work', withSocketServer, async (t, server) => {
server.on('/', redirectHandler);
server.on('/foo', okHandler);

const url = format('http://unix:%s:%s', server.socketPath, '/');
t.is((await got(url)).body, 'ok');
});
}