Skip to content

Commit

Permalink
feat: add user registration commands (#732)
Browse files Browse the repository at this point in the history
Co-authored-by: maurice <mauricemscholtes@gmail.com>
  • Loading branch information
garrappachc and s-maurice authored Dec 28, 2024
1 parent 8d3d8b7 commit 0bbecae
Show file tree
Hide file tree
Showing 14 changed files with 492 additions and 8 deletions.
Binary file modified e2e/mumble-data/murmur.sqlite
Binary file not shown.
142 changes: 142 additions & 0 deletions e2e/registers-user.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { Client } from '../src';
import { waitABit } from './utils/wait-a-bit';
import { userRenameRegistered, userUnregister } from '../src/commands';
import { createCertificate as createCertificateCb } from 'pem';

interface MumbleCert {
key: string;
cert: Buffer;
}

const createMumbleCertificate = () =>
new Promise<MumbleCert>((resolve, reject) => {
createCertificateCb(
{
days: 1,
selfSigned: true,
},
(error, result) => {
if (error) {
reject(new Error(error));
} else {
resolve({ key: result.clientKey, cert: result.certificate });
}
},
);
});

describe('registers a user', () => {
let client1: Client;
let client2: Client;

beforeAll(async () => {
// client1 is always registered
client1 = new Client({
host: 'localhost',
port: 64738,
username: 'client1',
rejectUnauthorized: false,
...(await createMumbleCertificate()),
});

// client2 is unregistered
client2 = new Client({
host: 'localhost',
port: 64738,
username: 'client2',
rejectUnauthorized: false,
...(await createMumbleCertificate()),
});

await client1.connect();
await client2.connect();
await waitABit(100);
});

afterAll(async () => {
await waitABit(1000);
client1.disconnect();
client2.disconnect();
});

beforeEach(async () => {
if (!client1.user!.isRegistered) {
await client1.user!.register();
expect(client1.user!.isRegistered).toBe(true);
}
});

afterEach(async () => {
if (client2.user!.isRegistered) {
await client2.user!.deregister();
expect(client2.user!.isRegistered).toBe(false);
}
});

it('should register self and deregister', async () => {
expect(client2.user!.userId).toBeUndefined();
expect(client2.user!.isRegistered).toBe(false);

await client2.user!.register();
expect(client2.user!.userId).toBeTruthy();
expect(client2.user!.isRegistered).toBe(true);

const registeredUsers = await client2.fetchRegisteredUsers();
const user = registeredUsers.find(user => user.name === 'client2');
expect(user).toBeTruthy();
});

it('should rename', async () => {
await client1.user!.rename('client1_new_name');
expect(client1.user!.name).toBe('client1_new_name');

const registeredUsers = await client1.fetchRegisteredUsers();
const user = registeredUsers.find(user => user.name === 'client1_new_name');
expect(user).toBeTruthy();

await client1.user!.rename('client1');
});

it('should rename an offline user', async () => {
expect(client2.user!.userId).toBeUndefined();
expect(client2.user!.isRegistered).toBe(false);
await client2.user!.register();
client2.disconnect();
await waitABit(100);

// rename client2 while they are offline
const registeredUsers = await client1.fetchRegisteredUsers();
const user = registeredUsers.find(user => user.name === 'client2');
expect(user).toBeTruthy();
await userRenameRegistered(
client1.socket!,
user!.userId,
'client2_new_name',
);

await client2.connect();
expect(client2.user!.name).toEqual('client2_new_name');
expect(client2.user!.userId).toBeTruthy();
expect(client2.user!.isRegistered).toBe(true);

await client2.user!.rename('client2');
});

it('should unregister an offline user', async () => {
expect(client2.user!.userId).toBeUndefined();
expect(client2.user!.isRegistered).toBe(false);
await client2.user!.register();
client2.disconnect();
await waitABit(100);

// deregister client2 while they are offline
const registeredUsers = await client1.fetchRegisteredUsers();
const user = registeredUsers.find(user => user.name === 'client2');
expect(user).toBeTruthy();
await userUnregister(client1.socket!, user!.userId);

await client2.connect();
expect(client2.user!.userId).toBeFalsy();
expect(client2.user!.isRegistered).toBe(false);
});
});
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@
"@release-it/conventional-changelog": "9.0.4",
"@tsconfig/node16": "16.1.3",
"@types/node": "22.10.2",
"@types/pem": "1.14.4",
"@typescript-eslint/eslint-plugin": "8.18.2",
"@typescript-eslint/parser": "8.18.2",
"eslint": "9.17.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "5.2.1",
"pem": "1.14.8",
"prettier": "3.4.2",
"release-it": "17.11.0",
"trace-unhandled": "2.0.1",
Expand Down
54 changes: 54 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
Reject,
ServerConfig,
ServerSync,
UserList_User,
UserRemove,
Version,
} from '@tf2pickup-org/mumble-protocol';
Expand All @@ -29,13 +30,14 @@ import { ChannelManager } from './channel-manager';
import { UserManager } from './user-manager';
import { encodeMumbleVersion } from './encode-mumble-version';
import { ClientOptions } from './client-options';
import { ConnectionRejectedError } from './errors';
import { ClientDisconnectedError, ConnectionRejectedError } from './errors';
import { filterPacket } from './rxjs-operators/filter-packet';
import { platform, release } from 'os';
import { Permissions } from './permissions';
import { encodeMumbleVersionLegacy } from './encode-mumble-version-legacy';
import { TypedEventEmitter } from './typed-event-emitter';
import { Events } from './events';
import { fetchRegisteredUsers } from './commands';

const defaultOptions: Partial<ClientOptions> = {
port: 64738,
Expand Down Expand Up @@ -146,6 +148,14 @@ export class Client extends TypedEventEmitter<Events, Events> {
return this;
}

async fetchRegisteredUsers(): Promise<UserList_User[]> {
if (!this.socket) {
throw new ClientDisconnectedError();
}

return (await fetchRegisteredUsers(this.socket)).users;
}

/**
* @internal
*/
Expand Down
Loading

0 comments on commit 0bbecae

Please sign in to comment.