refactor(WebSocketManager)!: remove deprecated rest option (#10998)

BREAKING CHANGE: The `rest` option in the `WebSocketManager` constructor has been removed. Pass a `fetchGatewayInformation` function instead.
This commit is contained in:
Almeida
2025-07-27 11:49:29 +01:00
committed by GitHub
parent 2e9bfba5f4
commit 08a61ca8e3
9 changed files with 98 additions and 281 deletions

View File

@@ -1,107 +1,55 @@
import { REST } from '@discordjs/rest';
import type { RESTGetAPIGatewayBotResult, APIGatewayBotInfo, GatewaySendPayload } from 'discord-api-types/v10';
import { GatewayOpcodes, Routes } from 'discord-api-types/v10';
import { MockAgent, type Interceptable } from 'undici';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import type { GatewaySendPayload } from 'discord-api-types/v10';
import { GatewayOpcodes } from 'discord-api-types/v10';
import { describe, expect, test, vi } from 'vitest';
import { WebSocketManager, type IShardingStrategy } from '../../src/index.js';
import { mockGatewayInformation } from '../gateway.mock.js';
vi.useFakeTimers();
let mockAgent: MockAgent;
let mockPool: Interceptable;
beforeEach(() => {
mockAgent = new MockAgent();
mockAgent.disableNetConnect();
mockPool = mockAgent.get('https://discord.com');
});
const NOW = vi.fn().mockReturnValue(Date.now());
global.Date.now = NOW;
test('fetch gateway information', async () => {
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token');
const fetchGatewayInformation = vi.fn(async () => mockGatewayInformation);
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
async fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
},
fetchGatewayInformation,
});
const data: APIGatewayBotInfo = {
shards: 1,
session_start_limit: {
max_concurrency: 3,
reset_after: 60,
remaining: 3,
total: 3,
},
url: 'wss://gateway.discord.gg',
};
const fetch = vi.fn(() => ({
data,
statusCode: 200,
responseOptions: {
headers: {
'content-type': 'application/json',
},
},
}));
mockPool
.intercept({
path: '/api/v10/gateway/bot',
method: 'GET',
})
.reply(fetch);
const initial = await manager.fetchGatewayInformation();
expect(initial).toEqual(data);
expect(fetch).toHaveBeenCalledOnce();
expect(initial).toEqual(mockGatewayInformation);
expect(fetchGatewayInformation).toHaveBeenCalledOnce();
fetch.mockClear();
fetchGatewayInformation.mockClear();
const cached = await manager.fetchGatewayInformation();
expect(cached).toEqual(data);
expect(fetch).not.toHaveBeenCalled();
expect(cached).toEqual(mockGatewayInformation);
expect(fetchGatewayInformation).not.toHaveBeenCalled();
fetch.mockClear();
mockPool
.intercept({
path: '/api/v10/gateway/bot',
method: 'GET',
})
.reply(fetch);
fetchGatewayInformation.mockClear();
const forced = await manager.fetchGatewayInformation(true);
expect(forced).toEqual(data);
expect(fetch).toHaveBeenCalledOnce();
expect(forced).toEqual(mockGatewayInformation);
expect(fetchGatewayInformation).toHaveBeenCalledOnce();
fetch.mockClear();
mockPool
.intercept({
path: '/api/v10/gateway/bot',
method: 'GET',
})
.reply(fetch);
fetchGatewayInformation.mockClear();
NOW.mockReturnValue(Number.POSITIVE_INFINITY);
const cacheExpired = await manager.fetchGatewayInformation();
expect(cacheExpired).toEqual(data);
expect(fetch).toHaveBeenCalledOnce();
expect(cacheExpired).toEqual(mockGatewayInformation);
expect(fetchGatewayInformation).toHaveBeenCalledOnce();
});
describe('get shard count', () => {
test('with shard count', async () => {
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token');
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
shardCount: 2,
async fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
return mockGatewayInformation;
},
});
@@ -109,14 +57,13 @@ describe('get shard count', () => {
});
test('with shard ids array', async () => {
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token');
const shardIds = [5, 9];
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
shardIds,
async fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
return mockGatewayInformation;
},
});
@@ -124,14 +71,13 @@ describe('get shard count', () => {
});
test('with shard id range', async () => {
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token');
const shardIds = { start: 5, end: 9 };
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
shardIds,
async fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
return mockGatewayInformation;
},
});
@@ -140,62 +86,26 @@ describe('get shard count', () => {
});
test('update shard count', async () => {
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token');
const fetchGatewayInformation = vi.fn(async () => mockGatewayInformation);
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
shardCount: 2,
async fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
},
fetchGatewayInformation,
});
const data: APIGatewayBotInfo = {
shards: 1,
session_start_limit: {
max_concurrency: 3,
reset_after: 60,
remaining: 3,
total: 3,
},
url: 'wss://gateway.discord.gg',
};
const fetch = vi.fn(() => ({
data,
statusCode: 200,
responseOptions: {
headers: {
'content-type': 'application/json',
},
},
}));
mockPool
.intercept({
path: '/api/v10/gateway/bot',
method: 'GET',
})
.reply(fetch);
expect(await manager.getShardCount()).toBe(2);
expect(fetch).not.toHaveBeenCalled();
expect(fetchGatewayInformation).not.toHaveBeenCalled();
fetch.mockClear();
mockPool
.intercept({
path: '/api/v10/gateway/bot',
method: 'GET',
})
.reply(fetch);
fetchGatewayInformation.mockClear();
await manager.updateShardCount(3);
expect(await manager.getShardCount()).toBe(3);
expect(fetch).toHaveBeenCalled();
expect(fetchGatewayInformation).toHaveBeenCalled();
});
test('it handles passing in both shardIds and shardCount', async () => {
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token');
const shardIds = { start: 2, end: 3 };
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
@@ -203,7 +113,7 @@ test('it handles passing in both shardIds and shardCount', async () => {
shardIds,
shardCount: 4,
async fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
return mockGatewayInformation;
},
});
@@ -226,44 +136,18 @@ test('strategies', async () => {
const strategy = new MockStrategy();
const rest = new REST().setAgent(mockAgent).setToken('A-Very-Fake-Token');
const shardIds = [0, 1, 2];
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
rest,
shardIds,
async fetchGatewayInformation() {
return mockGatewayInformation;
},
buildStrategy: () => strategy,
});
const data: APIGatewayBotInfo = {
shards: 1,
session_start_limit: {
max_concurrency: 3,
reset_after: 60,
remaining: 3,
total: 3,
},
url: 'wss://gateway.discord.gg',
};
const fetch = vi.fn(() => ({
data,
statusCode: 200,
responseOptions: {
headers: {
'content-type': 'application/json',
},
},
}));
mockPool
.intercept({
path: '/api/v10/gateway/bot',
method: 'GET',
})
.reply(fetch);
await manager.connect();
expect(strategy.spawn).toHaveBeenCalledWith(shardIds);
expect(strategy.connect).toHaveBeenCalled();