mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
* test: fix type errors * chore: use MockedFunction --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
453 lines
10 KiB
TypeScript
453 lines
10 KiB
TypeScript
import { Buffer, File } from 'node:buffer';
|
|
import { URLSearchParams } from 'node:url';
|
|
import { DiscordSnowflake } from '@sapphire/snowflake';
|
|
import type { Snowflake } from 'discord-api-types/v10';
|
|
import { Routes } from 'discord-api-types/v10';
|
|
import { type FormData, fetch } from 'undici';
|
|
import { MockAgent, setGlobalDispatcher } from 'undici';
|
|
import type { Interceptable, MockInterceptor } from 'undici/types/mock-interceptor.js';
|
|
import { beforeEach, afterEach, test, expect, vitest } from 'vitest';
|
|
import { REST } from '../src/index.js';
|
|
import { genPath } from './util.js';
|
|
|
|
const newSnowflake: Snowflake = DiscordSnowflake.generate().toString();
|
|
|
|
const api = new REST().setToken('A-Very-Fake-Token');
|
|
|
|
const makeRequestMock = vitest.fn(fetch);
|
|
|
|
const fetchApi = new REST({ makeRequest: makeRequestMock }).setToken('A-Very-Fake-Token');
|
|
|
|
// @discordjs/rest uses the `content-type` header to detect whether to parse
|
|
// the response as JSON or as an ArrayBuffer.
|
|
const responseOptions: MockInterceptor.MockResponseOptions = {
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
},
|
|
};
|
|
|
|
let mockAgent: MockAgent;
|
|
let mockPool: Interceptable;
|
|
|
|
beforeEach(() => {
|
|
mockAgent = new MockAgent();
|
|
mockAgent.disableNetConnect(); // prevent actual requests to Discord
|
|
setGlobalDispatcher(mockAgent); // enabled the mock client to intercept requests
|
|
|
|
mockPool = mockAgent.get('https://discord.com');
|
|
api.setAgent(mockAgent);
|
|
fetchApi.setAgent(mockAgent);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await mockAgent.close();
|
|
});
|
|
|
|
test('simple GET', async () => {
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/simpleGet'),
|
|
method: 'GET',
|
|
})
|
|
.reply(() => ({
|
|
data: { test: true },
|
|
statusCode: 200,
|
|
responseOptions,
|
|
}));
|
|
|
|
expect(await api.get('/simpleGet')).toStrictEqual({ test: true });
|
|
});
|
|
|
|
test('simple DELETE', async () => {
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/simpleDelete'),
|
|
method: 'DELETE',
|
|
})
|
|
.reply(() => ({
|
|
data: { test: true },
|
|
statusCode: 200,
|
|
responseOptions,
|
|
}));
|
|
|
|
expect(await api.delete('/simpleDelete')).toStrictEqual({ test: true });
|
|
});
|
|
|
|
test('simple PATCH', async () => {
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/simplePatch'),
|
|
method: 'PATCH',
|
|
})
|
|
.reply(() => ({
|
|
data: { test: true },
|
|
statusCode: 200,
|
|
responseOptions,
|
|
}));
|
|
|
|
expect(await api.patch('/simplePatch')).toStrictEqual({ test: true });
|
|
});
|
|
|
|
test('simple PUT', async () => {
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/simplePut'),
|
|
method: 'PUT',
|
|
})
|
|
.reply(() => ({
|
|
data: { test: true },
|
|
statusCode: 200,
|
|
responseOptions,
|
|
}));
|
|
|
|
expect(await api.put('/simplePut')).toStrictEqual({ test: true });
|
|
});
|
|
|
|
test('simple POST', async () => {
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/simplePost'),
|
|
method: 'POST',
|
|
})
|
|
.reply(() => ({
|
|
data: { test: true },
|
|
statusCode: 200,
|
|
responseOptions,
|
|
}));
|
|
|
|
expect(await api.post('/simplePost')).toStrictEqual({ test: true });
|
|
});
|
|
|
|
test('simple POST with fetch', async () => {
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/fetchSimplePost'),
|
|
method: 'POST',
|
|
})
|
|
.reply(() => ({
|
|
data: { test: true },
|
|
statusCode: 200,
|
|
responseOptions,
|
|
}));
|
|
|
|
expect(await fetchApi.post('/fetchSimplePost')).toStrictEqual({ test: true });
|
|
expect(makeRequestMock).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
test('simple PUT 2', async () => {
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/simplePut'),
|
|
method: 'PUT',
|
|
})
|
|
.reply(() => ({
|
|
data: { test: true },
|
|
statusCode: 200,
|
|
responseOptions,
|
|
}));
|
|
|
|
expect(await api.put('/simplePut')).toStrictEqual({ test: true });
|
|
});
|
|
|
|
test('getQuery', async () => {
|
|
const query = new URLSearchParams([
|
|
['foo', 'bar'],
|
|
['hello', 'world'],
|
|
]);
|
|
|
|
mockPool
|
|
.intercept({
|
|
path: `${genPath('/getQuery')}?${query.toString()}`,
|
|
method: 'GET',
|
|
})
|
|
.reply(() => ({
|
|
data: { test: true },
|
|
statusCode: 200,
|
|
responseOptions,
|
|
}));
|
|
|
|
expect(
|
|
await api.get('/getQuery', {
|
|
query,
|
|
}),
|
|
).toStrictEqual({ test: true });
|
|
});
|
|
|
|
test('getAuth', async () => {
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/getAuth'),
|
|
method: 'GET',
|
|
})
|
|
.reply(
|
|
200,
|
|
(from) => ({ auth: (from.headers as unknown as Record<string, string | undefined>).Authorization ?? null }),
|
|
responseOptions,
|
|
)
|
|
.times(5);
|
|
|
|
// default
|
|
expect(await api.get('/getAuth')).toStrictEqual({ auth: 'Bot A-Very-Fake-Token' });
|
|
|
|
// unauthorized
|
|
expect(
|
|
await api.get('/getAuth', {
|
|
auth: false,
|
|
}),
|
|
).toStrictEqual({ auth: null });
|
|
|
|
// authorized
|
|
expect(
|
|
await api.get('/getAuth', {
|
|
auth: true,
|
|
}),
|
|
).toStrictEqual({ auth: 'Bot A-Very-Fake-Token' });
|
|
|
|
// Custom Bot Auth
|
|
expect(
|
|
await api.get('/getAuth', {
|
|
auth: { token: 'A-Very-Different-Fake-Token' },
|
|
}),
|
|
).toStrictEqual({ auth: 'Bot A-Very-Different-Fake-Token' });
|
|
|
|
// Custom Bearer Auth
|
|
expect(
|
|
await api.get('/getAuth', {
|
|
auth: { token: 'A-Bearer-Fake-Token', prefix: 'Bearer' },
|
|
}),
|
|
).toStrictEqual({ auth: 'Bearer A-Bearer-Fake-Token' });
|
|
});
|
|
|
|
test('getReason', async () => {
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/getReason'),
|
|
method: 'GET',
|
|
})
|
|
.reply(
|
|
200,
|
|
(from) => ({
|
|
reason: (from.headers as unknown as Record<string, string | undefined>)['X-Audit-Log-Reason'] ?? null,
|
|
}),
|
|
responseOptions,
|
|
)
|
|
.times(3);
|
|
|
|
// default
|
|
expect(await api.get('/getReason')).toStrictEqual({ reason: null });
|
|
|
|
// plain text
|
|
expect(
|
|
await api.get('/getReason', {
|
|
reason: 'Hello',
|
|
}),
|
|
).toStrictEqual({ reason: 'Hello' });
|
|
|
|
// encoded
|
|
expect(
|
|
await api.get('/getReason', {
|
|
reason: '😄',
|
|
}),
|
|
).toStrictEqual({ reason: '%F0%9F%98%84' });
|
|
});
|
|
|
|
test('urlEncoded', async () => {
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/urlEncoded'),
|
|
method: 'POST',
|
|
})
|
|
.reply((from) => ({
|
|
data: from.body!,
|
|
statusCode: 200,
|
|
}));
|
|
|
|
const body = new URLSearchParams([
|
|
['client_id', '1234567890123545678'],
|
|
['client_secret', 'totally-valid-secret'],
|
|
['redirect_uri', 'http://localhost'],
|
|
['grant_type', 'authorization_code'],
|
|
['code', 'very-invalid-code'],
|
|
]);
|
|
|
|
expect(
|
|
new Uint8Array(
|
|
(await api.post('/urlEncoded', {
|
|
body,
|
|
passThroughBody: true,
|
|
auth: false,
|
|
})) as ArrayBuffer,
|
|
),
|
|
).toStrictEqual(new Uint8Array(Buffer.from(body.toString())));
|
|
});
|
|
|
|
test('postEcho', async () => {
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/postEcho'),
|
|
method: 'POST',
|
|
})
|
|
.reply((from) => ({
|
|
data: from.body!,
|
|
statusCode: 200,
|
|
responseOptions,
|
|
}));
|
|
|
|
expect(await api.post('/postEcho', { body: { foo: 'bar' } })).toStrictEqual({ foo: 'bar' });
|
|
});
|
|
|
|
test('201 status code', async () => {
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/postNon200StatusCode'),
|
|
method: 'POST',
|
|
})
|
|
.reply((from) => ({
|
|
data: from.body!,
|
|
statusCode: 201,
|
|
responseOptions,
|
|
}));
|
|
|
|
expect(await api.post('/postNon200StatusCode', { body: { foo: 'bar' } })).toStrictEqual({ foo: 'bar' });
|
|
});
|
|
|
|
test('Old Message Delete Edge-Case: Old message', async () => {
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/channels/339942739275677727/messages/392063687801700356'),
|
|
method: 'DELETE',
|
|
})
|
|
.reply(() => ({
|
|
data: { test: true },
|
|
statusCode: 200,
|
|
responseOptions,
|
|
}));
|
|
|
|
expect(await api.delete(Routes.channelMessage('339942739275677727', '392063687801700356'))).toStrictEqual({
|
|
test: true,
|
|
});
|
|
});
|
|
|
|
test('Old Message Delete Edge-Case: Old message 2', async () => {
|
|
mockPool
|
|
.intercept({
|
|
path: genPath(`/channels/339942739275677727/messages/${newSnowflake}`),
|
|
method: 'DELETE',
|
|
})
|
|
.reply(() => ({
|
|
data: { test: true },
|
|
statusCode: 200,
|
|
responseOptions,
|
|
}));
|
|
|
|
expect(await api.delete(Routes.channelMessage('339942739275677727', newSnowflake))).toStrictEqual({ test: true });
|
|
});
|
|
|
|
test('postFile', async () => {
|
|
const mockData = {
|
|
statusCode: 200,
|
|
data: 'Hello',
|
|
};
|
|
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/postFileEmptyArray'),
|
|
method: 'POST',
|
|
})
|
|
.reply(({ body }) => {
|
|
expect(body).toBeNull();
|
|
return mockData;
|
|
});
|
|
|
|
// postFile empty
|
|
await api.post('/postFileEmptyArray', { files: [] });
|
|
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/postFileStringData'),
|
|
method: 'POST',
|
|
})
|
|
.reply(({ body }) => {
|
|
const fd = body as FormData;
|
|
|
|
expect(fd.get('files[0]')).toBeInstanceOf(File);
|
|
expect(fd.get('files[0]')).toHaveProperty('size', 5); // 'Hello'
|
|
|
|
return mockData;
|
|
});
|
|
|
|
// postFile file (string)
|
|
await api.post('/postFileStringData', {
|
|
files: [{ name: 'out.txt', data: 'Hello' }],
|
|
});
|
|
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/postFileBufferWithJson'),
|
|
method: 'POST',
|
|
})
|
|
.reply(({ body }) => {
|
|
const fd = body as FormData;
|
|
|
|
expect(fd.get('files[0]')).toBeInstanceOf(File);
|
|
expect(fd.get('files[0]')).toHaveProperty('size', 5); // Buffer.from('Hello')
|
|
expect(fd.get('payload_json')).toStrictEqual(JSON.stringify({ foo: 'bar' }));
|
|
|
|
return mockData;
|
|
});
|
|
|
|
// postFile file and JSON
|
|
await api.post('/postFileBufferWithJson', {
|
|
files: [{ name: 'out.txt', data: Buffer.from('Hello') }],
|
|
body: { foo: 'bar' },
|
|
});
|
|
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/postFilesAndJson'),
|
|
method: 'POST',
|
|
})
|
|
.reply(({ body }) => {
|
|
const fd = body as FormData;
|
|
|
|
expect(fd.get('files[0]')).toBeInstanceOf(File);
|
|
expect(fd.get('files[1]')).toBeInstanceOf(File);
|
|
expect(fd.get('files[0]')).toHaveProperty('size', 5); // Buffer.from('Hello')
|
|
expect(fd.get('files[1]')).toHaveProperty('size', 2); // Buffer.from('Hi')
|
|
expect(fd.get('payload_json')).toStrictEqual(JSON.stringify({ files: [{ id: 0, description: 'test' }] }));
|
|
|
|
return mockData;
|
|
});
|
|
|
|
// postFile files and JSON
|
|
await api.post('/postFilesAndJson', {
|
|
files: [
|
|
{ name: 'out.txt', data: Buffer.from('Hello') },
|
|
{ name: 'out.txt', data: Buffer.from('Hi') },
|
|
],
|
|
body: { files: [{ id: 0, description: 'test' }] },
|
|
});
|
|
|
|
mockPool
|
|
.intercept({
|
|
path: genPath('/postFileStickerAndJson'),
|
|
method: 'POST',
|
|
})
|
|
.reply(({ body }) => {
|
|
const fd = body as FormData;
|
|
|
|
expect(fd.get('file')).toBeInstanceOf(File);
|
|
expect(fd.get('file')).toHaveProperty('size', 7); // Buffer.from('Sticker')
|
|
expect(fd.get('foo')).toStrictEqual('bar');
|
|
|
|
return mockData;
|
|
});
|
|
|
|
// postFile sticker and JSON
|
|
await api.post('/postFileStickerAndJson', {
|
|
files: [{ key: 'file', name: 'sticker.png', data: Buffer.from('Sticker') }],
|
|
body: { foo: 'bar' },
|
|
appendToFormData: true,
|
|
});
|
|
});
|