mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
chore: monorepo setup (#7175)
This commit is contained in:
769
packages/voice/__tests__/VoiceConnection.test.ts
Normal file
769
packages/voice/__tests__/VoiceConnection.test.ts
Normal file
@@ -0,0 +1,769 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/dot-notation */
|
||||
import {
|
||||
createVoiceConnection,
|
||||
VoiceConnection,
|
||||
VoiceConnectionConnectingState,
|
||||
VoiceConnectionDisconnectReason,
|
||||
VoiceConnectionReadyState,
|
||||
VoiceConnectionSignallingState,
|
||||
VoiceConnectionStatus,
|
||||
} from '../src/VoiceConnection';
|
||||
|
||||
import * as _DataStore from '../src/DataStore';
|
||||
import * as _Networking from '../src/networking/Networking';
|
||||
import * as _AudioPlayer from '../src/audio/AudioPlayer';
|
||||
import { PlayerSubscription as _PlayerSubscription } from '../src/audio/PlayerSubscription';
|
||||
import type { DiscordGatewayAdapterLibraryMethods } from '../src/util/adapter';
|
||||
import EventEmitter from 'node:events';
|
||||
|
||||
jest.mock('../audio/AudioPlayer');
|
||||
jest.mock('../audio/PlayerSubscription');
|
||||
jest.mock('../DataStore');
|
||||
jest.mock('../networking/Networking');
|
||||
|
||||
const DataStore = _DataStore as unknown as jest.Mocked<typeof _DataStore>;
|
||||
const Networking = _Networking as unknown as jest.Mocked<typeof _Networking>;
|
||||
const AudioPlayer = _AudioPlayer as unknown as jest.Mocked<typeof _AudioPlayer>;
|
||||
const PlayerSubscription = _PlayerSubscription as unknown as jest.Mock<_PlayerSubscription>;
|
||||
|
||||
Networking.Networking.mockImplementation(function mockedConstructor() {
|
||||
this.state = {};
|
||||
return this;
|
||||
});
|
||||
|
||||
function createFakeAdapter() {
|
||||
const sendPayload = jest.fn();
|
||||
sendPayload.mockReturnValue(true);
|
||||
const destroy = jest.fn();
|
||||
const libMethods: Partial<DiscordGatewayAdapterLibraryMethods> = {};
|
||||
return {
|
||||
sendPayload,
|
||||
destroy,
|
||||
libMethods,
|
||||
creator: jest.fn((methods) => {
|
||||
Object.assign(libMethods, methods);
|
||||
return {
|
||||
sendPayload,
|
||||
destroy,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function createJoinConfig() {
|
||||
return {
|
||||
channelId: '1',
|
||||
guildId: '2',
|
||||
selfDeaf: true,
|
||||
selfMute: false,
|
||||
group: 'default',
|
||||
};
|
||||
}
|
||||
|
||||
function createFakeVoiceConnection() {
|
||||
const adapter = createFakeAdapter();
|
||||
const joinConfig = createJoinConfig();
|
||||
const voiceConnection = new VoiceConnection(joinConfig, {
|
||||
debug: false,
|
||||
adapterCreator: adapter.creator,
|
||||
});
|
||||
return { adapter, joinConfig, voiceConnection };
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
DataStore.createJoinVoiceChannelPayload.mockReset();
|
||||
DataStore.getVoiceConnection.mockReset();
|
||||
DataStore.trackVoiceConnection.mockReset();
|
||||
DataStore.untrackVoiceConnection.mockReset();
|
||||
});
|
||||
|
||||
describe('createVoiceConnection', () => {
|
||||
test('New voice connection', () => {
|
||||
const mockPayload = Symbol('mock') as any;
|
||||
DataStore.createJoinVoiceChannelPayload.mockImplementation(() => mockPayload);
|
||||
const adapter = createFakeAdapter();
|
||||
const joinConfig = createJoinConfig();
|
||||
const voiceConnection = createVoiceConnection(joinConfig, {
|
||||
debug: false,
|
||||
adapterCreator: adapter.creator,
|
||||
});
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Signalling);
|
||||
expect(DataStore.getVoiceConnection).toHaveBeenCalledTimes(1);
|
||||
expect(DataStore.trackVoiceConnection).toHaveBeenCalledWith(voiceConnection);
|
||||
expect(DataStore.untrackVoiceConnection).not.toHaveBeenCalled();
|
||||
expect(adapter.sendPayload).toHaveBeenCalledWith(mockPayload);
|
||||
});
|
||||
|
||||
test('New voice connection with adapter failure', () => {
|
||||
const mockPayload = Symbol('mock') as any;
|
||||
DataStore.createJoinVoiceChannelPayload.mockImplementation(() => mockPayload);
|
||||
const adapter = createFakeAdapter();
|
||||
adapter.sendPayload.mockReturnValue(false);
|
||||
const joinConfig = createJoinConfig();
|
||||
const voiceConnection = createVoiceConnection(joinConfig, {
|
||||
debug: false,
|
||||
adapterCreator: adapter.creator,
|
||||
});
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Disconnected);
|
||||
expect(DataStore.getVoiceConnection).toHaveBeenCalledTimes(1);
|
||||
expect(DataStore.trackVoiceConnection).toHaveBeenCalledWith(voiceConnection);
|
||||
expect(DataStore.untrackVoiceConnection).not.toHaveBeenCalled();
|
||||
expect(adapter.sendPayload).toHaveBeenCalledWith(mockPayload);
|
||||
});
|
||||
|
||||
test('Reconfiguring existing connection', () => {
|
||||
const mockPayload = Symbol('mock') as any;
|
||||
|
||||
DataStore.createJoinVoiceChannelPayload.mockImplementation(() => mockPayload);
|
||||
|
||||
const existingAdapter = createFakeAdapter();
|
||||
const existingJoinConfig = createJoinConfig();
|
||||
const existingVoiceConnection = new VoiceConnection(existingJoinConfig, {
|
||||
debug: false,
|
||||
adapterCreator: existingAdapter.creator,
|
||||
});
|
||||
|
||||
const stateSetter = jest.spyOn(existingVoiceConnection, 'state', 'set');
|
||||
|
||||
DataStore.getVoiceConnection.mockImplementation((guildId) =>
|
||||
guildId === existingJoinConfig.guildId ? existingVoiceConnection : null,
|
||||
);
|
||||
|
||||
const newAdapter = createFakeAdapter();
|
||||
const newJoinConfig = createJoinConfig();
|
||||
const newVoiceConnection = createVoiceConnection(newJoinConfig, {
|
||||
debug: false,
|
||||
adapterCreator: newAdapter.creator,
|
||||
});
|
||||
expect(DataStore.getVoiceConnection).toHaveBeenCalledWith(newJoinConfig.guildId);
|
||||
expect(DataStore.trackVoiceConnection).not.toHaveBeenCalled();
|
||||
expect(DataStore.untrackVoiceConnection).not.toHaveBeenCalled();
|
||||
expect(newAdapter.creator).not.toHaveBeenCalled();
|
||||
expect(existingAdapter.sendPayload).toHaveBeenCalledWith(mockPayload);
|
||||
expect(newVoiceConnection).toBe(existingVoiceConnection);
|
||||
expect(stateSetter).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Calls rejoin() on existing disconnected connection', () => {
|
||||
const mockPayload = Symbol('mock') as any;
|
||||
|
||||
DataStore.createJoinVoiceChannelPayload.mockImplementation(() => mockPayload);
|
||||
|
||||
const existingAdapter = createFakeAdapter();
|
||||
const existingJoinConfig = createJoinConfig();
|
||||
const existingVoiceConnection = new VoiceConnection(existingJoinConfig, {
|
||||
debug: false,
|
||||
adapterCreator: existingAdapter.creator,
|
||||
});
|
||||
existingVoiceConnection.state = {
|
||||
status: VoiceConnectionStatus.Disconnected,
|
||||
adapter: existingAdapter,
|
||||
reason: VoiceConnectionDisconnectReason.EndpointRemoved,
|
||||
};
|
||||
|
||||
const rejoinSpy = jest.spyOn(existingVoiceConnection, 'rejoin');
|
||||
|
||||
DataStore.getVoiceConnection.mockImplementation((guildId) =>
|
||||
guildId === existingJoinConfig.guildId ? existingVoiceConnection : null,
|
||||
);
|
||||
|
||||
const newAdapter = createFakeAdapter();
|
||||
const newJoinConfig = createJoinConfig();
|
||||
const { guildId, group, ...rejoinConfig } = newJoinConfig;
|
||||
const newVoiceConnection = createVoiceConnection(newJoinConfig, {
|
||||
debug: false,
|
||||
adapterCreator: newAdapter.creator,
|
||||
});
|
||||
expect(DataStore.getVoiceConnection).toHaveBeenCalledWith(newJoinConfig.guildId);
|
||||
expect(DataStore.trackVoiceConnection).not.toHaveBeenCalled();
|
||||
expect(DataStore.untrackVoiceConnection).not.toHaveBeenCalled();
|
||||
expect(newAdapter.creator).not.toHaveBeenCalled();
|
||||
expect(rejoinSpy).toHaveBeenCalledWith(rejoinConfig);
|
||||
expect(newVoiceConnection).toBe(existingVoiceConnection);
|
||||
});
|
||||
|
||||
test('Reconfiguring existing connection with adapter failure', () => {
|
||||
const mockPayload = Symbol('mock') as any;
|
||||
|
||||
DataStore.createJoinVoiceChannelPayload.mockImplementation(() => mockPayload);
|
||||
|
||||
const existingAdapter = createFakeAdapter();
|
||||
const existingJoinConfig = createJoinConfig();
|
||||
const existingVoiceConnection = new VoiceConnection(existingJoinConfig, {
|
||||
debug: false,
|
||||
adapterCreator: existingAdapter.creator,
|
||||
});
|
||||
|
||||
DataStore.getVoiceConnection.mockImplementation((guildId) =>
|
||||
guildId === existingJoinConfig.guildId ? existingVoiceConnection : null,
|
||||
);
|
||||
|
||||
const newAdapter = createFakeAdapter();
|
||||
const newJoinConfig = createJoinConfig();
|
||||
existingAdapter.sendPayload.mockReturnValue(false);
|
||||
const newVoiceConnection = createVoiceConnection(newJoinConfig, {
|
||||
debug: false,
|
||||
adapterCreator: newAdapter.creator,
|
||||
});
|
||||
expect(DataStore.getVoiceConnection).toHaveBeenCalledWith(newJoinConfig.guildId);
|
||||
expect(DataStore.trackVoiceConnection).not.toHaveBeenCalled();
|
||||
expect(DataStore.untrackVoiceConnection).not.toHaveBeenCalled();
|
||||
expect(newAdapter.creator).not.toHaveBeenCalled();
|
||||
expect(existingAdapter.sendPayload).toHaveBeenCalledWith(mockPayload);
|
||||
expect(newVoiceConnection).toBe(existingVoiceConnection);
|
||||
expect(newVoiceConnection.state.status).toBe(VoiceConnectionStatus.Disconnected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('VoiceConnection#addServerPacket', () => {
|
||||
test('Stores the packet and attempts to configure networking', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
voiceConnection.configureNetworking = jest.fn();
|
||||
const dummy = {
|
||||
endpoint: 'discord.com',
|
||||
guild_id: 123,
|
||||
token: 'abc',
|
||||
} as any;
|
||||
voiceConnection['addServerPacket'](dummy);
|
||||
expect(voiceConnection['packets'].server).toBe(dummy);
|
||||
expect(voiceConnection.configureNetworking).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Overwrites existing packet', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
voiceConnection['packets'].server = Symbol('old') as any;
|
||||
voiceConnection.configureNetworking = jest.fn();
|
||||
const dummy = {
|
||||
endpoint: 'discord.com',
|
||||
guild_id: 123,
|
||||
token: 'abc',
|
||||
} as any;
|
||||
voiceConnection['addServerPacket'](dummy);
|
||||
expect(voiceConnection['packets'].server).toBe(dummy);
|
||||
expect(voiceConnection.configureNetworking).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Disconnects when given a null endpoint', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
voiceConnection['packets'].server = Symbol('old') as any;
|
||||
voiceConnection.configureNetworking = jest.fn();
|
||||
const dummy = {
|
||||
endpoint: null,
|
||||
guild_id: 123,
|
||||
token: 'abc',
|
||||
} as any;
|
||||
voiceConnection['addServerPacket'](dummy);
|
||||
expect(voiceConnection['packets'].server).toBe(dummy);
|
||||
expect(voiceConnection.configureNetworking).not.toHaveBeenCalled();
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Disconnected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('VoiceConnection#addStatePacket', () => {
|
||||
test('State is assigned to joinConfig', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
voiceConnection['addStatePacket']({
|
||||
self_deaf: true,
|
||||
self_mute: true,
|
||||
channel_id: '123',
|
||||
} as any);
|
||||
|
||||
expect(voiceConnection.joinConfig).toMatchObject({
|
||||
selfDeaf: true,
|
||||
selfMute: true,
|
||||
channelId: '123',
|
||||
});
|
||||
|
||||
voiceConnection['addStatePacket']({
|
||||
self_mute: false,
|
||||
} as any);
|
||||
|
||||
expect(voiceConnection.joinConfig).toMatchObject({
|
||||
selfDeaf: true,
|
||||
selfMute: false,
|
||||
channelId: '123',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('VoiceConnection#configureNetworking', () => {
|
||||
test('Only creates Networking instance when both packets are present and not destroyed', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Signalling);
|
||||
|
||||
voiceConnection.configureNetworking();
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Signalling);
|
||||
const adapter = (voiceConnection.state as VoiceConnectionSignallingState).adapter;
|
||||
|
||||
const state = {
|
||||
session_id: 'abc',
|
||||
user_id: '123',
|
||||
} as any;
|
||||
|
||||
const server = {
|
||||
endpoint: 'def',
|
||||
guild_id: '123',
|
||||
token: 'xyz',
|
||||
} as any;
|
||||
|
||||
Object.assign(voiceConnection['packets'], { state, server: undefined });
|
||||
voiceConnection.configureNetworking();
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Signalling);
|
||||
expect(Networking.Networking).toHaveBeenCalledTimes(0);
|
||||
|
||||
Object.assign(voiceConnection['packets'], { state: undefined, server });
|
||||
voiceConnection.configureNetworking();
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Signalling);
|
||||
expect(Networking.Networking).toHaveBeenCalledTimes(0);
|
||||
|
||||
Object.assign(voiceConnection['packets'], { state, server });
|
||||
voiceConnection.state = { status: VoiceConnectionStatus.Destroyed };
|
||||
voiceConnection.configureNetworking();
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Destroyed);
|
||||
expect(Networking.Networking).toHaveBeenCalledTimes(0);
|
||||
|
||||
voiceConnection.state = { status: VoiceConnectionStatus.Signalling, adapter };
|
||||
voiceConnection.configureNetworking();
|
||||
expect(Networking.Networking).toHaveBeenCalledTimes(1);
|
||||
expect(Networking.Networking).toHaveBeenCalledWith(
|
||||
{
|
||||
endpoint: server.endpoint,
|
||||
serverId: server.guild_id,
|
||||
token: server.token,
|
||||
sessionId: state.session_id,
|
||||
userId: state.user_id,
|
||||
},
|
||||
false,
|
||||
);
|
||||
expect(voiceConnection.state).toMatchObject({
|
||||
status: VoiceConnectionStatus.Connecting,
|
||||
adapter,
|
||||
});
|
||||
expect((voiceConnection.state as unknown as VoiceConnectionConnectingState).networking).toBeInstanceOf(
|
||||
Networking.Networking,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('VoiceConnection#onNetworkingClose', () => {
|
||||
test('Does nothing in destroyed state', () => {
|
||||
const { voiceConnection, adapter } = createFakeVoiceConnection();
|
||||
voiceConnection.state = {
|
||||
status: VoiceConnectionStatus.Destroyed,
|
||||
};
|
||||
voiceConnection['onNetworkingClose'](1000);
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Destroyed);
|
||||
expect(adapter.sendPayload).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Disconnects for code 4014', () => {
|
||||
const { voiceConnection, adapter } = createFakeVoiceConnection();
|
||||
voiceConnection['onNetworkingClose'](4014);
|
||||
expect(voiceConnection.state).toMatchObject({
|
||||
status: VoiceConnectionStatus.Disconnected,
|
||||
closeCode: 4014,
|
||||
});
|
||||
expect(adapter.sendPayload).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Attempts rejoin for codes != 4014', () => {
|
||||
const dummyPayload = Symbol('dummy') as any;
|
||||
const { voiceConnection, adapter, joinConfig } = createFakeVoiceConnection();
|
||||
DataStore.createJoinVoiceChannelPayload.mockImplementation((config) =>
|
||||
config === joinConfig ? dummyPayload : undefined,
|
||||
);
|
||||
voiceConnection['onNetworkingClose'](1234);
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Signalling);
|
||||
expect(adapter.sendPayload).toHaveBeenCalledWith(dummyPayload);
|
||||
expect(voiceConnection.rejoinAttempts).toBe(1);
|
||||
});
|
||||
|
||||
test('Attempts rejoin for codes != 4014 (with adapter failure)', () => {
|
||||
const dummyPayload = Symbol('dummy') as any;
|
||||
const { voiceConnection, adapter, joinConfig } = createFakeVoiceConnection();
|
||||
DataStore.createJoinVoiceChannelPayload.mockImplementation((config) =>
|
||||
config === joinConfig ? dummyPayload : undefined,
|
||||
);
|
||||
adapter.sendPayload.mockReturnValue(false);
|
||||
voiceConnection['onNetworkingClose'](1234);
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Disconnected);
|
||||
expect(adapter.sendPayload).toHaveBeenCalledWith(dummyPayload);
|
||||
expect(voiceConnection.rejoinAttempts).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('VoiceConnection#onNetworkingStateChange', () => {
|
||||
test('Does nothing when status code identical', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const stateSetter = jest.spyOn(voiceConnection, 'state', 'set');
|
||||
voiceConnection['onNetworkingStateChange'](
|
||||
{ code: _Networking.NetworkingStatusCode.Ready } as any,
|
||||
{ code: _Networking.NetworkingStatusCode.Ready } as any,
|
||||
);
|
||||
voiceConnection['onNetworkingStateChange'](
|
||||
{ code: _Networking.NetworkingStatusCode.Closed } as any,
|
||||
{ code: _Networking.NetworkingStatusCode.Closed } as any,
|
||||
);
|
||||
expect(stateSetter).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Does nothing when not in Ready or Connecting states', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const stateSetter = jest.spyOn(voiceConnection, 'state', 'set');
|
||||
const call = [
|
||||
{ code: _Networking.NetworkingStatusCode.Ready } as any,
|
||||
{ code: _Networking.NetworkingStatusCode.Closed } as any,
|
||||
];
|
||||
voiceConnection['_state'] = { status: VoiceConnectionStatus.Signalling } as any;
|
||||
voiceConnection['onNetworkingStateChange'](call[0], call[1]);
|
||||
voiceConnection['_state'] = { status: VoiceConnectionStatus.Disconnected } as any;
|
||||
voiceConnection['onNetworkingStateChange'](call[0], call[1]);
|
||||
voiceConnection['_state'] = { status: VoiceConnectionStatus.Destroyed } as any;
|
||||
voiceConnection['onNetworkingStateChange'](call[0], call[1]);
|
||||
expect(stateSetter).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Transitions to Ready', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const stateSetter = jest.spyOn(voiceConnection, 'state', 'set');
|
||||
voiceConnection['_state'] = {
|
||||
...(voiceConnection.state as VoiceConnectionSignallingState),
|
||||
status: VoiceConnectionStatus.Connecting,
|
||||
networking: new Networking.Networking({} as any, false),
|
||||
};
|
||||
|
||||
voiceConnection['onNetworkingStateChange'](
|
||||
{ code: _Networking.NetworkingStatusCode.Closed } as any,
|
||||
{ code: _Networking.NetworkingStatusCode.Ready } as any,
|
||||
);
|
||||
|
||||
expect(stateSetter).toHaveBeenCalledTimes(1);
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Ready);
|
||||
});
|
||||
|
||||
test('Transitions to Connecting', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const stateSetter = jest.spyOn(voiceConnection, 'state', 'set');
|
||||
voiceConnection['_state'] = {
|
||||
...(voiceConnection.state as VoiceConnectionSignallingState),
|
||||
status: VoiceConnectionStatus.Connecting,
|
||||
networking: new Networking.Networking({} as any, false),
|
||||
};
|
||||
|
||||
voiceConnection['onNetworkingStateChange'](
|
||||
{ code: _Networking.NetworkingStatusCode.Ready } as any,
|
||||
{ code: _Networking.NetworkingStatusCode.Identifying } as any,
|
||||
);
|
||||
|
||||
expect(stateSetter).toHaveBeenCalledTimes(1);
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Connecting);
|
||||
});
|
||||
});
|
||||
|
||||
describe('VoiceConnection#destroy', () => {
|
||||
test('Throws when in Destroyed state', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
voiceConnection.state = { status: VoiceConnectionStatus.Destroyed };
|
||||
expect(() => voiceConnection.destroy()).toThrow();
|
||||
});
|
||||
|
||||
test('Cleans up in a valid, destroyable state', () => {
|
||||
const { voiceConnection, joinConfig, adapter } = createFakeVoiceConnection();
|
||||
DataStore.getVoiceConnection.mockImplementation((guildId) =>
|
||||
joinConfig.guildId === guildId ? voiceConnection : undefined,
|
||||
);
|
||||
const dummy = Symbol('dummy');
|
||||
DataStore.createJoinVoiceChannelPayload.mockImplementation(() => dummy as any);
|
||||
voiceConnection.destroy();
|
||||
expect(DataStore.getVoiceConnection).toHaveReturnedWith(voiceConnection);
|
||||
expect(DataStore.untrackVoiceConnection).toHaveBeenCalledWith(voiceConnection);
|
||||
expect(DataStore.createJoinVoiceChannelPayload.mock.calls[0][0]).toMatchObject({
|
||||
channelId: null,
|
||||
guildId: joinConfig.guildId,
|
||||
});
|
||||
expect(adapter.sendPayload).toHaveBeenCalledWith(dummy);
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Destroyed);
|
||||
});
|
||||
});
|
||||
|
||||
describe('VoiceConnection#disconnect', () => {
|
||||
test('Fails in Destroyed and Signalling states', () => {
|
||||
const { voiceConnection, adapter } = createFakeVoiceConnection();
|
||||
voiceConnection.state = { status: VoiceConnectionStatus.Destroyed };
|
||||
expect(voiceConnection.disconnect()).toBe(false);
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Destroyed);
|
||||
|
||||
voiceConnection.state = { status: VoiceConnectionStatus.Signalling, adapter };
|
||||
expect(voiceConnection.disconnect()).toBe(false);
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Signalling);
|
||||
});
|
||||
|
||||
test('Disconnects - available adapter', () => {
|
||||
const { voiceConnection, adapter } = createFakeVoiceConnection();
|
||||
voiceConnection.state = {
|
||||
status: VoiceConnectionStatus.Ready,
|
||||
adapter,
|
||||
networking: new Networking.Networking({} as any, false),
|
||||
};
|
||||
const leavePayload = Symbol('dummy');
|
||||
DataStore.createJoinVoiceChannelPayload.mockImplementation(() => leavePayload as any);
|
||||
expect(voiceConnection.disconnect()).toBe(true);
|
||||
expect(voiceConnection.joinConfig).toMatchObject({
|
||||
channelId: null,
|
||||
guildId: '2',
|
||||
selfDeaf: true,
|
||||
selfMute: false,
|
||||
});
|
||||
expect(DataStore.createJoinVoiceChannelPayload).toHaveBeenCalledWith(voiceConnection.joinConfig);
|
||||
expect(adapter.sendPayload).toHaveBeenCalledWith(leavePayload);
|
||||
expect(voiceConnection.state).toMatchObject({
|
||||
status: VoiceConnectionStatus.Disconnected,
|
||||
reason: VoiceConnectionDisconnectReason.Manual,
|
||||
});
|
||||
});
|
||||
|
||||
test('Disconnects - unavailable adapter', () => {
|
||||
const { voiceConnection, adapter } = createFakeVoiceConnection();
|
||||
voiceConnection.state = {
|
||||
status: VoiceConnectionStatus.Ready,
|
||||
adapter,
|
||||
networking: new Networking.Networking({} as any, false),
|
||||
};
|
||||
adapter.sendPayload.mockImplementation(() => false);
|
||||
expect(voiceConnection.disconnect()).toBe(false);
|
||||
expect(voiceConnection.state).toMatchObject({
|
||||
status: VoiceConnectionStatus.Disconnected,
|
||||
reason: VoiceConnectionDisconnectReason.AdapterUnavailable,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('VoiceConnection#rejoin', () => {
|
||||
test('Rejoins in a disconnected state', () => {
|
||||
const dummy = Symbol('dummy') as any;
|
||||
DataStore.createJoinVoiceChannelPayload.mockImplementation(() => dummy);
|
||||
|
||||
const { voiceConnection, adapter } = createFakeVoiceConnection();
|
||||
voiceConnection.state = {
|
||||
...(voiceConnection.state as VoiceConnectionSignallingState),
|
||||
status: VoiceConnectionStatus.Disconnected,
|
||||
reason: VoiceConnectionDisconnectReason.WebSocketClose,
|
||||
closeCode: 1000,
|
||||
};
|
||||
expect(voiceConnection.rejoin()).toBe(true);
|
||||
expect(voiceConnection.rejoinAttempts).toBe(1);
|
||||
expect(adapter.sendPayload).toHaveBeenCalledWith(dummy);
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Signalling);
|
||||
});
|
||||
|
||||
test('Rejoins in a ready state', () => {
|
||||
const dummy = Symbol('dummy') as any;
|
||||
DataStore.createJoinVoiceChannelPayload.mockImplementation(() => dummy);
|
||||
|
||||
const { voiceConnection, adapter } = createFakeVoiceConnection();
|
||||
voiceConnection.state = {
|
||||
...(voiceConnection.state as VoiceConnectionReadyState),
|
||||
status: VoiceConnectionStatus.Ready,
|
||||
};
|
||||
expect(voiceConnection.rejoin()).toBe(true);
|
||||
expect(voiceConnection.rejoinAttempts).toBe(0);
|
||||
expect(adapter.sendPayload).toHaveBeenCalledWith(dummy);
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Ready);
|
||||
});
|
||||
|
||||
test('Stays in the disconnected state when the adapter fails', () => {
|
||||
const dummy = Symbol('dummy') as any;
|
||||
DataStore.createJoinVoiceChannelPayload.mockImplementation(() => dummy);
|
||||
|
||||
const { voiceConnection, adapter } = createFakeVoiceConnection();
|
||||
voiceConnection.state = {
|
||||
...(voiceConnection.state as VoiceConnectionSignallingState),
|
||||
status: VoiceConnectionStatus.Disconnected,
|
||||
reason: VoiceConnectionDisconnectReason.WebSocketClose,
|
||||
closeCode: 1000,
|
||||
};
|
||||
adapter.sendPayload.mockReturnValue(false);
|
||||
expect(voiceConnection.rejoin()).toBe(false);
|
||||
expect(voiceConnection.rejoinAttempts).toBe(1);
|
||||
expect(adapter.sendPayload).toHaveBeenCalledWith(dummy);
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Disconnected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('VoiceConnection#subscribe', () => {
|
||||
test('Does nothing in Destroyed state', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const player = new AudioPlayer.AudioPlayer();
|
||||
player['subscribe'] = jest.fn();
|
||||
voiceConnection.state = { status: VoiceConnectionStatus.Destroyed };
|
||||
expect(voiceConnection.subscribe(player)).toBeUndefined();
|
||||
expect(player['subscribe']).not.toHaveBeenCalled();
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Destroyed);
|
||||
});
|
||||
|
||||
test('Subscribes in a live state', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const adapter = (voiceConnection.state as VoiceConnectionSignallingState).adapter;
|
||||
const player = new AudioPlayer.AudioPlayer();
|
||||
const dummy = Symbol('dummy');
|
||||
player['subscribe'] = jest.fn().mockImplementation(() => dummy);
|
||||
expect(voiceConnection.subscribe(player)).toBe(dummy);
|
||||
expect(player['subscribe']).toHaveBeenCalledWith(voiceConnection);
|
||||
expect(voiceConnection.state).toMatchObject({
|
||||
status: VoiceConnectionStatus.Signalling,
|
||||
adapter,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('VoiceConnection#onSubscriptionRemoved', () => {
|
||||
test('Does nothing in Destroyed state', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const subscription = new PlayerSubscription(voiceConnection, new AudioPlayer.AudioPlayer());
|
||||
subscription.unsubscribe = jest.fn();
|
||||
|
||||
voiceConnection.state = { status: VoiceConnectionStatus.Destroyed };
|
||||
voiceConnection['onSubscriptionRemoved'](subscription);
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Destroyed);
|
||||
expect(subscription.unsubscribe).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Does nothing when subscription is not the same as the stored one', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const subscription = new PlayerSubscription(voiceConnection, new AudioPlayer.AudioPlayer());
|
||||
subscription.unsubscribe = jest.fn();
|
||||
|
||||
voiceConnection.state = { ...(voiceConnection.state as VoiceConnectionSignallingState), subscription };
|
||||
voiceConnection['onSubscriptionRemoved'](Symbol('new subscription') as any);
|
||||
expect(voiceConnection.state).toMatchObject({
|
||||
status: VoiceConnectionStatus.Signalling,
|
||||
subscription,
|
||||
});
|
||||
expect(subscription.unsubscribe).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Unsubscribes in a live state with matching subscription', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const subscription = new PlayerSubscription(voiceConnection, new AudioPlayer.AudioPlayer());
|
||||
subscription.unsubscribe = jest.fn();
|
||||
|
||||
voiceConnection.state = { ...(voiceConnection.state as VoiceConnectionSignallingState), subscription };
|
||||
voiceConnection['onSubscriptionRemoved'](subscription);
|
||||
expect(voiceConnection.state).toEqual({
|
||||
...voiceConnection.state,
|
||||
subscription: undefined,
|
||||
});
|
||||
expect(subscription.unsubscribe).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
describe('updateReceiveBindings', () => {
|
||||
test('Applies and removes udp listeners', () => {
|
||||
// Arrange
|
||||
const ws = new EventEmitter() as any;
|
||||
|
||||
const oldNetworking = new Networking.Networking({} as any, false);
|
||||
oldNetworking.state = {
|
||||
code: _Networking.NetworkingStatusCode.Ready,
|
||||
connectionData: {} as any,
|
||||
connectionOptions: {} as any,
|
||||
udp: new EventEmitter() as any,
|
||||
ws,
|
||||
};
|
||||
|
||||
const newNetworking = new Networking.Networking({} as any, false);
|
||||
newNetworking.state = {
|
||||
...oldNetworking.state,
|
||||
udp: new EventEmitter() as any,
|
||||
};
|
||||
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
|
||||
// Act
|
||||
voiceConnection['updateReceiveBindings'](newNetworking.state, oldNetworking.state);
|
||||
|
||||
// Assert
|
||||
expect(oldNetworking.state.udp.listenerCount('message')).toBe(0);
|
||||
expect(newNetworking.state.udp.listenerCount('message')).toBe(1);
|
||||
expect(voiceConnection.receiver.connectionData).toBe(newNetworking.state.connectionData);
|
||||
});
|
||||
|
||||
test('Applies and removes ws listeners', () => {
|
||||
// Arrange
|
||||
const udp = new EventEmitter() as any;
|
||||
|
||||
const oldNetworking = new Networking.Networking({} as any, false);
|
||||
oldNetworking.state = {
|
||||
code: _Networking.NetworkingStatusCode.Ready,
|
||||
connectionData: {} as any,
|
||||
connectionOptions: {} as any,
|
||||
udp,
|
||||
ws: new EventEmitter() as any,
|
||||
};
|
||||
|
||||
const newNetworking = new Networking.Networking({} as any, false);
|
||||
newNetworking.state = {
|
||||
...oldNetworking.state,
|
||||
ws: new EventEmitter() as any,
|
||||
};
|
||||
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
|
||||
// Act
|
||||
voiceConnection['updateReceiveBindings'](newNetworking.state, oldNetworking.state);
|
||||
|
||||
// Assert
|
||||
expect(oldNetworking.state.ws.listenerCount('packet')).toBe(0);
|
||||
expect(newNetworking.state.ws.listenerCount('packet')).toBe(1);
|
||||
expect(voiceConnection.receiver.connectionData).toBe(newNetworking.state.connectionData);
|
||||
});
|
||||
|
||||
test('Applies initial listeners', () => {
|
||||
// Arrange
|
||||
|
||||
const newNetworking = new Networking.Networking({} as any, false);
|
||||
newNetworking.state = {
|
||||
code: _Networking.NetworkingStatusCode.Ready,
|
||||
connectionData: {} as any,
|
||||
connectionOptions: {} as any,
|
||||
udp: new EventEmitter() as any,
|
||||
ws: new EventEmitter() as any,
|
||||
};
|
||||
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
|
||||
// Act
|
||||
voiceConnection['updateReceiveBindings'](newNetworking.state, undefined);
|
||||
|
||||
// Assert
|
||||
expect(newNetworking.state.ws.listenerCount('packet')).toBe(1);
|
||||
expect(newNetworking.state.udp.listenerCount('message')).toBe(1);
|
||||
expect(voiceConnection.receiver.connectionData).toBe(newNetworking.state.connectionData);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Adapter', () => {
|
||||
test('onVoiceServerUpdate', () => {
|
||||
const { adapter, voiceConnection } = createFakeVoiceConnection();
|
||||
voiceConnection['addServerPacket'] = jest.fn();
|
||||
const dummy = Symbol('dummy') as any;
|
||||
adapter.libMethods.onVoiceServerUpdate(dummy);
|
||||
expect(voiceConnection['addServerPacket']).toHaveBeenCalledWith(dummy);
|
||||
});
|
||||
|
||||
test('onVoiceStateUpdate', () => {
|
||||
const { adapter, voiceConnection } = createFakeVoiceConnection();
|
||||
voiceConnection['addStatePacket'] = jest.fn();
|
||||
const dummy = Symbol('dummy') as any;
|
||||
adapter.libMethods.onVoiceStateUpdate(dummy);
|
||||
expect(voiceConnection['addStatePacket']).toHaveBeenCalledWith(dummy);
|
||||
});
|
||||
|
||||
test('destroy', () => {
|
||||
const { adapter, voiceConnection } = createFakeVoiceConnection();
|
||||
adapter.libMethods.destroy();
|
||||
expect(voiceConnection.state.status).toBe(VoiceConnectionStatus.Destroyed);
|
||||
expect(adapter.sendPayload).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user