mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-04-27 07:48:01 -07:00
cleanup testing utilities, documentation, and AI commentary
This commit is contained in:
parent
bd2382c94e
commit
ef6cea6f6c
150 changed files with 891 additions and 1233 deletions
|
|
@ -29,13 +29,14 @@ vi.mock('./services/ProtobufService', () => ({
|
|||
import { WebClient } from './WebClient';
|
||||
import { WebSocketService } from './services/WebSocketService';
|
||||
import { ProtobufService } from './services/ProtobufService';
|
||||
import { StatusEnum } from './interfaces/StatusEnum';
|
||||
import { StatusEnum } from './types/StatusEnum';
|
||||
import { Subject } from 'rxjs';
|
||||
import { Mock } from 'vitest';
|
||||
import { SocketTransport } from './services/ProtobufService';
|
||||
import { WebSocketServiceConfig } from './services/WebSocketService';
|
||||
import type { IWebClientResponse, IWebClientRequest } from './interfaces';
|
||||
import type { ConnectTarget } from './interfaces/WebClientConfig';
|
||||
import type { IWebClientResponse } from './types/WebClientResponse';
|
||||
import type { IWebClientRequest } from './types/WebClientRequest';
|
||||
import type { ConnectTarget } from './types/WebClientConfig';
|
||||
import { installMockWebSocket } from './__mocks__/helpers';
|
||||
|
||||
function makeMockResponse(): IWebClientResponse {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
import { ping } from './commands/session';
|
||||
import { CLIENT_OPTIONS } from './config';
|
||||
import type {
|
||||
ConnectTarget,
|
||||
IWebClientRequest,
|
||||
IWebClientResponse,
|
||||
} from './interfaces';
|
||||
import { StatusEnum } from './interfaces';
|
||||
import type { ConnectTarget } from './types/WebClientConfig';
|
||||
import type { IWebClientRequest } from './types/WebClientRequest';
|
||||
import type { IWebClientResponse } from './types/WebClientResponse';
|
||||
import { StatusEnum } from './types/StatusEnum';
|
||||
import { ProtobufService } from './services/ProtobufService';
|
||||
import { WebSocketService } from './services/WebSocketService';
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,6 @@
|
|||
* property, not a getter that throws.
|
||||
*/
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// response.session (ISessionResponse)
|
||||
// ---------------------------------------------------------------------------
|
||||
const session = {
|
||||
initialized: vi.fn(),
|
||||
connectionAttempted: vi.fn(),
|
||||
|
|
@ -80,9 +77,6 @@ const session = {
|
|||
replayDownloaded: vi.fn(),
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// response.room (IRoomResponse)
|
||||
// ---------------------------------------------------------------------------
|
||||
const room = {
|
||||
clearStore: vi.fn(),
|
||||
joinRoom: vi.fn(),
|
||||
|
|
@ -97,9 +91,6 @@ const room = {
|
|||
joinedGame: vi.fn(),
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// response.game (IGameResponse)
|
||||
// ---------------------------------------------------------------------------
|
||||
const game = {
|
||||
clearStore: vi.fn(),
|
||||
gameStateChanged: vi.fn(),
|
||||
|
|
@ -133,9 +124,6 @@ const game = {
|
|||
zonePropertiesChanged: vi.fn(),
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// response.admin (IAdminResponse)
|
||||
// ---------------------------------------------------------------------------
|
||||
const admin = {
|
||||
adjustMod: vi.fn(),
|
||||
reloadConfig: vi.fn(),
|
||||
|
|
@ -143,9 +131,6 @@ const admin = {
|
|||
updateServerMessage: vi.fn(),
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// response.moderator (IModeratorResponse)
|
||||
// ---------------------------------------------------------------------------
|
||||
const moderator = {
|
||||
banFromServer: vi.fn(),
|
||||
banHistory: vi.fn(),
|
||||
|
|
@ -159,9 +144,6 @@ const moderator = {
|
|||
updateAdminNotes: vi.fn(),
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Exported mock — replaces the real WebClient module for all consumers.
|
||||
// ---------------------------------------------------------------------------
|
||||
export const WebClient = {
|
||||
_instance: null as any,
|
||||
instance: {
|
||||
|
|
|
|||
|
|
@ -1,27 +1,6 @@
|
|||
/**
|
||||
* Shared mock factories for websocket layer unit tests.
|
||||
* Import the helpers you need in each spec file via:
|
||||
* import { makeMockWebSocket, useWebClientCleanup } from '../__mocks__/helpers';
|
||||
*/
|
||||
import { WebClient } from '../WebClient';
|
||||
|
||||
/**
|
||||
* Resets the WebClient singleton to null. Call directly, or use
|
||||
* `useWebClientCleanup()` to register automatic beforeEach/afterEach hooks.
|
||||
*/
|
||||
export function resetWebClientSingleton() {
|
||||
(WebClient as unknown as { _instance: WebClient | null })._instance = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers beforeEach/afterEach hooks that reset the WebClient singleton.
|
||||
* Call at describe-level or file-level in any spec that mocks WebClient.
|
||||
* Prevents isolate:false singleton leakage between spec files.
|
||||
*/
|
||||
export function useWebClientCleanup() {
|
||||
beforeEach(() => resetWebClientSingleton());
|
||||
afterEach(() => resetWebClientSingleton());
|
||||
}
|
||||
|
||||
/** Builds a mock WebSocket instance */
|
||||
export function makeMockWebSocketInstance() {
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@ const { invokeOnSuccess } = makeCallbackHelpers(
|
|||
2
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// adjustMod
|
||||
// ----------------------------------------------------------------
|
||||
describe('adjustMod', () => {
|
||||
|
||||
it('calls sendAdminCommand with Command_AdjustMod extension and fields', () => {
|
||||
|
|
@ -41,9 +38,6 @@ describe('adjustMod', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// reloadConfig
|
||||
// ----------------------------------------------------------------
|
||||
describe('reloadConfig', () => {
|
||||
|
||||
it('calls sendAdminCommand with Command_ReloadConfig extension', () => {
|
||||
|
|
@ -62,9 +56,6 @@ describe('reloadConfig', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// shutdownServer
|
||||
// ----------------------------------------------------------------
|
||||
describe('shutdownServer', () => {
|
||||
|
||||
it('calls sendAdminCommand with Command_ShutdownServer extension and fields', () => {
|
||||
|
|
@ -83,9 +74,6 @@ describe('shutdownServer', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// updateServerMessage
|
||||
// ----------------------------------------------------------------
|
||||
describe('updateServerMessage', () => {
|
||||
|
||||
it('calls sendAdminCommand with Command_UpdateServerMessage extension', () => {
|
||||
|
|
|
|||
|
|
@ -39,9 +39,6 @@ const { invokeOnSuccess } = makeCallbackHelpers(
|
|||
2
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// banFromServer
|
||||
// ----------------------------------------------------------------
|
||||
describe('banFromServer', () => {
|
||||
|
||||
it('calls sendModeratorCommand with Command_BanFromServer', () => {
|
||||
|
|
@ -60,9 +57,6 @@ describe('banFromServer', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// forceActivateUser
|
||||
// ----------------------------------------------------------------
|
||||
describe('forceActivateUser', () => {
|
||||
|
||||
it('calls sendModeratorCommand with Command_ForceActivateUser', () => {
|
||||
|
|
@ -79,9 +73,6 @@ describe('forceActivateUser', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// getAdminNotes
|
||||
// ----------------------------------------------------------------
|
||||
describe('getAdminNotes', () => {
|
||||
|
||||
it('calls sendModeratorCommand with Command_GetAdminNotes', () => {
|
||||
|
|
@ -101,9 +92,6 @@ describe('getAdminNotes', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// getBanHistory
|
||||
// ----------------------------------------------------------------
|
||||
describe('getBanHistory', () => {
|
||||
|
||||
it('calls sendModeratorCommand with Command_GetBanHistory', () => {
|
||||
|
|
@ -123,9 +111,6 @@ describe('getBanHistory', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// getWarnHistory
|
||||
// ----------------------------------------------------------------
|
||||
describe('getWarnHistory', () => {
|
||||
|
||||
it('calls sendModeratorCommand with Command_GetWarnHistory', () => {
|
||||
|
|
@ -145,9 +130,6 @@ describe('getWarnHistory', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// getWarnList
|
||||
// ----------------------------------------------------------------
|
||||
describe('getWarnList', () => {
|
||||
|
||||
it('calls sendModeratorCommand with Command_GetWarnList', () => {
|
||||
|
|
@ -167,9 +149,6 @@ describe('getWarnList', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// grantReplayAccess
|
||||
// ----------------------------------------------------------------
|
||||
describe('grantReplayAccess', () => {
|
||||
|
||||
it('calls sendModeratorCommand with Command_GrantReplayAccess', () => {
|
||||
|
|
@ -186,9 +165,6 @@ describe('grantReplayAccess', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// updateAdminNotes
|
||||
// ----------------------------------------------------------------
|
||||
describe('updateAdminNotes', () => {
|
||||
|
||||
it('calls sendModeratorCommand with Command_UpdateAdminNotes', () => {
|
||||
|
|
@ -205,9 +181,6 @@ describe('updateAdminNotes', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// viewLogHistory
|
||||
// ----------------------------------------------------------------
|
||||
describe('viewLogHistory', () => {
|
||||
|
||||
it('calls sendModeratorCommand with Command_ViewLogHistory', () => {
|
||||
|
|
@ -229,9 +202,6 @@ describe('viewLogHistory', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// warnUser
|
||||
// ----------------------------------------------------------------
|
||||
describe('warnUser', () => {
|
||||
|
||||
it('calls sendModeratorCommand with Command_WarnUser', () => {
|
||||
|
|
|
|||
|
|
@ -24,9 +24,6 @@ const { invokeOnSuccess } = makeCallbackHelpers(
|
|||
3
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// createGame
|
||||
// ----------------------------------------------------------------
|
||||
describe('createGame', () => {
|
||||
|
||||
it('calls sendRoomCommand with Command_CreateGame', () => {
|
||||
|
|
@ -43,9 +40,6 @@ describe('createGame', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// joinGame
|
||||
// ----------------------------------------------------------------
|
||||
describe('joinGame', () => {
|
||||
|
||||
it('calls sendRoomCommand with Command_JoinGame', () => {
|
||||
|
|
@ -62,9 +56,6 @@ describe('joinGame', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// leaveRoom
|
||||
// ----------------------------------------------------------------
|
||||
describe('leaveRoom', () => {
|
||||
|
||||
it('calls sendRoomCommand with Command_LeaveRoom', () => {
|
||||
|
|
@ -81,9 +72,6 @@ describe('leaveRoom', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// roomSay
|
||||
// ----------------------------------------------------------------
|
||||
describe('roomSay', () => {
|
||||
|
||||
it('calls sendRoomCommand with trimmed message', () => {
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import {
|
|||
type ActivateParams,
|
||||
} from '@app/generated';
|
||||
|
||||
import { StatusEnum } from '../../interfaces/StatusEnum';
|
||||
import { StatusEnum } from '../../types/StatusEnum';
|
||||
import { CLIENT_CONFIG } from '../../config';
|
||||
import { WebClient } from '../../WebClient';
|
||||
import type { ConnectTarget } from '../../interfaces/WebClientConfig';
|
||||
import type { ConnectTarget } from '../../types/WebClientConfig';
|
||||
import { disconnect, login, updateStatus } from './';
|
||||
|
||||
export function activate(options: ConnectTarget & ActivateParams, password?: string, passwordSalt?: string): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { WebClient } from '../../WebClient';
|
||||
import type { ConnectTarget } from '../../interfaces/WebClientConfig';
|
||||
import type { ConnectTarget } from '../../types/WebClientConfig';
|
||||
|
||||
export function connect(target: ConnectTarget): void {
|
||||
WebClient.instance.connect(target);
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import {
|
|||
type ForgotPasswordChallengeParams,
|
||||
} from '@app/generated';
|
||||
|
||||
import { StatusEnum } from '../../interfaces/StatusEnum';
|
||||
import { StatusEnum } from '../../types/StatusEnum';
|
||||
import { CLIENT_CONFIG } from '../../config';
|
||||
import { WebClient } from '../../WebClient';
|
||||
import type { ConnectTarget } from '../../interfaces/WebClientConfig';
|
||||
import type { ConnectTarget } from '../../types/WebClientConfig';
|
||||
import { disconnect, updateStatus } from './';
|
||||
|
||||
export function forgotPasswordChallenge(options: ConnectTarget & ForgotPasswordChallengeParams): void {
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import {
|
|||
type ForgotPasswordRequestParams,
|
||||
} from '@app/generated';
|
||||
|
||||
import { StatusEnum } from '../../interfaces/StatusEnum';
|
||||
import { StatusEnum } from '../../types/StatusEnum';
|
||||
import { CLIENT_CONFIG } from '../../config';
|
||||
import { WebClient } from '../../WebClient';
|
||||
import type { ConnectTarget } from '../../interfaces/WebClientConfig';
|
||||
import type { ConnectTarget } from '../../types/WebClientConfig';
|
||||
import { disconnect, updateStatus } from './';
|
||||
|
||||
export function forgotPasswordRequest(options: ConnectTarget & ForgotPasswordRequestParams): void {
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import {
|
|||
type ForgotPasswordResetParams,
|
||||
} from '@app/generated';
|
||||
|
||||
import { StatusEnum } from '../../interfaces/StatusEnum';
|
||||
import { StatusEnum } from '../../types/StatusEnum';
|
||||
import { CLIENT_CONFIG } from '../../config';
|
||||
import { WebClient } from '../../WebClient';
|
||||
import type { ConnectTarget } from '../../interfaces/WebClientConfig';
|
||||
import type { ConnectTarget } from '../../types/WebClientConfig';
|
||||
import { hashPassword } from '../../utils';
|
||||
import { disconnect, updateStatus } from '.';
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ import {
|
|||
type LoginParams,
|
||||
} from '@app/generated';
|
||||
|
||||
import { StatusEnum } from '../../interfaces/StatusEnum';
|
||||
import { StatusEnum } from '../../types/StatusEnum';
|
||||
import { CLIENT_CONFIG } from '../../config';
|
||||
import { WebClient } from '../../WebClient';
|
||||
import type { ConnectTarget } from '../../interfaces/WebClientConfig';
|
||||
import type { ConnectTarget } from '../../types/WebClientConfig';
|
||||
import { hashPassword } from '../../utils';
|
||||
import {
|
||||
disconnect,
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ import {
|
|||
type RegisterParams,
|
||||
} from '@app/generated';
|
||||
|
||||
import { StatusEnum } from '../../interfaces/StatusEnum';
|
||||
import { StatusEnum } from '../../types/StatusEnum';
|
||||
import { CLIENT_CONFIG } from '../../config';
|
||||
import { WebClient } from '../../WebClient';
|
||||
import type { ConnectTarget } from '../../interfaces/WebClientConfig';
|
||||
import type { ConnectTarget } from '../../types/WebClientConfig';
|
||||
import { hashPassword } from '../../utils';
|
||||
import { login, disconnect, updateStatus } from './';
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ import {
|
|||
type RequestPasswordSaltParams,
|
||||
} from '@app/generated';
|
||||
|
||||
import { StatusEnum } from '../../interfaces/StatusEnum';
|
||||
import { StatusEnum } from '../../types/StatusEnum';
|
||||
import { CLIENT_CONFIG } from '../../config';
|
||||
import { WebClient } from '../../WebClient';
|
||||
import type { ConnectTarget } from '../../interfaces/WebClientConfig';
|
||||
import type { ConnectTarget } from '../../types/WebClientConfig';
|
||||
import { updateStatus } from './';
|
||||
|
||||
export function requestPasswordSalt(
|
||||
|
|
|
|||
|
|
@ -18,8 +18,16 @@ import { Mock } from 'vitest';
|
|||
import { makeCallbackHelpers } from '../../__mocks__/callbackHelpers';
|
||||
import { WebClient } from '../../WebClient';
|
||||
import * as SessionIndexMocks from './';
|
||||
import { Enriched } from '@app/types';
|
||||
import { StatusEnum } from '../../interfaces/StatusEnum';
|
||||
import {
|
||||
WebSocketConnectReason,
|
||||
type LoginConnectOptions,
|
||||
type RegisterConnectOptions,
|
||||
type ActivateConnectOptions,
|
||||
type PasswordResetRequestConnectOptions,
|
||||
type PasswordResetChallengeConnectOptions,
|
||||
type PasswordResetConnectOptions,
|
||||
} from '../../types/ConnectOptions';
|
||||
import { StatusEnum } from '../../types/StatusEnum';
|
||||
import {
|
||||
Command_Activate_ext,
|
||||
Command_ForgotPasswordChallenge_ext,
|
||||
|
|
@ -56,50 +64,50 @@ const { invokeOnSuccess, invokeResponseCode, invokeOnError } = makeCallbackHelpe
|
|||
);
|
||||
|
||||
const baseTransport = { host: 'h', port: '1' };
|
||||
const makeLoginOpts = (overrides: Partial<Enriched.LoginConnectOptions> = {}): Enriched.LoginConnectOptions => ({
|
||||
const makeLoginOpts = (overrides: Partial<LoginConnectOptions> = {}): LoginConnectOptions => ({
|
||||
...baseTransport,
|
||||
userName: 'alice',
|
||||
reason: Enriched.WebSocketConnectReason.LOGIN,
|
||||
reason: WebSocketConnectReason.LOGIN,
|
||||
...overrides,
|
||||
});
|
||||
const makeRegisterOpts = (
|
||||
overrides: Partial<Enriched.RegisterConnectOptions> = {}
|
||||
): Enriched.RegisterConnectOptions => ({
|
||||
overrides: Partial<RegisterConnectOptions> = {}
|
||||
): RegisterConnectOptions => ({
|
||||
...baseTransport,
|
||||
userName: 'alice',
|
||||
password: 'pw',
|
||||
email: 'a@b.com',
|
||||
country: 'US',
|
||||
realName: 'Al',
|
||||
reason: Enriched.WebSocketConnectReason.REGISTER,
|
||||
reason: WebSocketConnectReason.REGISTER,
|
||||
...overrides,
|
||||
});
|
||||
const makeActivateOpts = (
|
||||
overrides: Partial<Enriched.ActivateConnectOptions> = {}
|
||||
): Enriched.ActivateConnectOptions => ({
|
||||
overrides: Partial<ActivateConnectOptions> = {}
|
||||
): ActivateConnectOptions => ({
|
||||
...baseTransport,
|
||||
userName: 'alice',
|
||||
token: 'tok',
|
||||
reason: Enriched.WebSocketConnectReason.ACTIVATE_ACCOUNT,
|
||||
reason: WebSocketConnectReason.ACTIVATE_ACCOUNT,
|
||||
...overrides,
|
||||
});
|
||||
const makeForgotRequestOpts = (): Enriched.PasswordResetRequestConnectOptions => ({
|
||||
const makeForgotRequestOpts = (): PasswordResetRequestConnectOptions => ({
|
||||
...baseTransport,
|
||||
userName: 'alice',
|
||||
reason: Enriched.WebSocketConnectReason.PASSWORD_RESET_REQUEST,
|
||||
reason: WebSocketConnectReason.PASSWORD_RESET_REQUEST,
|
||||
});
|
||||
const makeForgotChallengeOpts = (): Enriched.PasswordResetChallengeConnectOptions => ({
|
||||
const makeForgotChallengeOpts = (): PasswordResetChallengeConnectOptions => ({
|
||||
...baseTransport,
|
||||
userName: 'alice',
|
||||
email: 'a@b.com',
|
||||
reason: Enriched.WebSocketConnectReason.PASSWORD_RESET_CHALLENGE,
|
||||
reason: WebSocketConnectReason.PASSWORD_RESET_CHALLENGE,
|
||||
});
|
||||
const makeForgotResetOpts = (): Enriched.PasswordResetConnectOptions => ({
|
||||
const makeForgotResetOpts = (): PasswordResetConnectOptions => ({
|
||||
...baseTransport,
|
||||
userName: 'alice',
|
||||
token: 'tok',
|
||||
newPassword: 'newpw',
|
||||
reason: Enriched.WebSocketConnectReason.PASSWORD_RESET,
|
||||
reason: WebSocketConnectReason.PASSWORD_RESET,
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -109,9 +117,6 @@ beforeEach(() => {
|
|||
(passwordSaltSupported as Mock).mockReturnValue(0);
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// connect.ts
|
||||
// ----------------------------------------------------------------
|
||||
describe('connect', () => {
|
||||
|
||||
it('calls WebClient.instance.connect with the target', () => {
|
||||
|
|
@ -128,9 +133,6 @@ describe('testConnect', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// updateStatus.ts
|
||||
// ----------------------------------------------------------------
|
||||
describe('updateStatus', () => {
|
||||
|
||||
it('calls WebClient.instance.response.session.updateStatus and WebClient.instance.updateStatus', () => {
|
||||
|
|
@ -140,9 +142,6 @@ describe('updateStatus', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// login.ts
|
||||
// ----------------------------------------------------------------
|
||||
describe('login', () => {
|
||||
|
||||
it('sends Command_Login with plain password when no salt', () => {
|
||||
|
|
@ -194,7 +193,7 @@ describe('login', () => {
|
|||
});
|
||||
|
||||
it('onSuccess passes hashedPassword to loginSuccessful when salt is used', () => {
|
||||
login({ host: 'h', port: '1', userName: 'alice', reason: Enriched.WebSocketConnectReason.LOGIN }, 'pw', 'salt');
|
||||
login({ host: 'h', port: '1', userName: 'alice', reason: WebSocketConnectReason.LOGIN }, 'pw', 'salt');
|
||||
const loginResp = { buddyList: [], ignoreList: [], userInfo: { name: 'alice' } };
|
||||
invokeOnSuccess(loginResp, { responseCode: 0 });
|
||||
const calledWith = (WebClient.instance.response.session.loginSuccessful as Mock).mock.calls[0][0];
|
||||
|
|
@ -266,9 +265,6 @@ describe('login', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// register.ts
|
||||
// ----------------------------------------------------------------
|
||||
describe('register', () => {
|
||||
|
||||
it('sends Command_Register with plain password when no salt', () => {
|
||||
|
|
@ -371,9 +367,6 @@ describe('register', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// activate.ts
|
||||
// ----------------------------------------------------------------
|
||||
describe('activate', () => {
|
||||
|
||||
it('sends Command_Activate with userName and token, not password', () => {
|
||||
|
|
@ -405,9 +398,6 @@ describe('activate', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// forgotPasswordChallenge.ts
|
||||
// ----------------------------------------------------------------
|
||||
describe('forgotPasswordChallenge', () => {
|
||||
|
||||
it('sends Command_ForgotPasswordChallenge', () => {
|
||||
|
|
@ -432,9 +422,6 @@ describe('forgotPasswordChallenge', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// forgotPasswordRequest.ts
|
||||
// ----------------------------------------------------------------
|
||||
describe('forgotPasswordRequest', () => {
|
||||
|
||||
it('sends Command_ForgotPasswordRequest', () => {
|
||||
|
|
@ -470,9 +457,6 @@ describe('forgotPasswordRequest', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// forgotPasswordReset.ts
|
||||
// ----------------------------------------------------------------
|
||||
describe('forgotPasswordReset', () => {
|
||||
|
||||
it('sends Command_ForgotPasswordReset with plain newPassword when no salt', () => {
|
||||
|
|
@ -508,9 +492,6 @@ describe('forgotPasswordReset', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// requestPasswordSalt.ts
|
||||
// ----------------------------------------------------------------
|
||||
describe('requestPasswordSalt', () => {
|
||||
|
||||
it('sends Command_RequestPasswordSalt', () => {
|
||||
|
|
|
|||
|
|
@ -92,7 +92,6 @@ beforeEach(() => {
|
|||
(passwordSaltSupported as Mock).mockReturnValue(0);
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
describe('accountEdit', () => {
|
||||
it('sends Command_AccountEdit with correct params', () => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { StatusEnum } from '../../interfaces/StatusEnum';
|
||||
import { StatusEnum } from '../../types/StatusEnum';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function updateStatus(status: StatusEnum, description: string): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_AttachCard } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function attachCard(data: Event_AttachCard, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_ChangeZoneProperties } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function changeZoneProperties(data: Event_ChangeZoneProperties, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_CreateArrow } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function createArrow(data: Event_CreateArrow, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_CreateCounter } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function createCounter(data: Event_CreateCounter, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_CreateToken } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function createToken(data: Event_CreateToken, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_DelCounter } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function delCounter(data: Event_DelCounter, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_DeleteArrow } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function deleteArrow(data: Event_DeleteArrow, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_DestroyCard } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function destroyCard(data: Event_DestroyCard, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_DrawCards } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function drawCards(data: Event_DrawCards, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_DumpZone } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function dumpZone(data: Event_DumpZone, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_FlipCard } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function flipCard(data: Event_FlipCard, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function gameClosed(_data: {}, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_GameSay } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function gameSay(data: Event_GameSay, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_GameStateChanged } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function gameStateChanged(data: Event_GameStateChanged, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import {
|
|||
Event_ReverseTurn_ext,
|
||||
} from '@app/generated';
|
||||
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
|
||||
import { attachCard } from './attachCard';
|
||||
import { changeZoneProperties } from './changeZoneProperties';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_Join } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function kicked(_data: {}, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function leaveGame(data: { reason: number }, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_MoveCard } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function moveCard(data: Event_MoveCard, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_PlayerPropertiesChanged } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function playerPropertiesChanged(data: Event_PlayerPropertiesChanged, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_RevealCards } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function revealCards(data: Event_RevealCards, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_ReverseTurn } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function reverseTurn(data: Event_ReverseTurn, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_RollDie } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function rollDie(data: Event_RollDie, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_SetActivePhase } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function setActivePhase(data: Event_SetActivePhase, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_SetActivePlayer } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function setActivePlayer(data: Event_SetActivePlayer, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_SetCardAttr } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function setCardAttr(data: Event_SetCardAttr, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_SetCardCounter } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function setCardCounter(data: Event_SetCardCounter, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_SetCounter } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function setCounter(data: Event_SetCounter, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Event_Shuffle } from '@app/generated';
|
||||
import type { GameEventMeta } from '../../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../../types/WebSocketConfig';
|
||||
import { WebClient } from '../../WebClient';
|
||||
|
||||
export function shuffle(data: Event_Shuffle, meta: GameEventMeta): void {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { Event_ConnectionClosed_CloseReason, type Event_ConnectionClosed } from '@app/generated';
|
||||
import { StatusEnum } from '../../interfaces/StatusEnum';
|
||||
import { StatusEnum } from '../../types/StatusEnum';
|
||||
import { updateStatus } from '../../commands/session';
|
||||
|
||||
export function connectionClosed({ reason, reasonStr, endTime }: Event_ConnectionClosed): void {
|
||||
let message: string;
|
||||
|
||||
// @TODO (5)
|
||||
if (reasonStr) {
|
||||
message = reasonStr;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import type { Event_ServerIdentification } from '@app/generated';
|
||||
import { WebClient } from '../../WebClient';
|
||||
import { StatusEnum } from '../../interfaces/StatusEnum';
|
||||
import { StatusEnum } from '../../types/StatusEnum';
|
||||
import { PROTOCOL_VERSION } from '../../config';
|
||||
import { consumePendingOptions } from '../../utils/connectionState';
|
||||
import { WebSocketConnectReason } from '../../interfaces/ConnectOptions';
|
||||
import { WebSocketConnectReason } from '../../types/ConnectOptions';
|
||||
import { generateSalt, passwordSaltSupported } from '../../utils';
|
||||
import * as SessionCommands from '../../commands/session';
|
||||
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ import * as Config from '../../config';
|
|||
import * as SessionCmds from '../../commands/session';
|
||||
import { consumePendingOptions } from '../../utils/connectionState';
|
||||
import { passwordSaltSupported } from '../../utils';
|
||||
import { WebSocketConnectReason } from '../../interfaces/ConnectOptions';
|
||||
import { StatusEnum } from '../../interfaces/StatusEnum';
|
||||
import { WebSocketConnectReason } from '../../types/ConnectOptions';
|
||||
import { StatusEnum } from '../../types/StatusEnum';
|
||||
import { Mock } from 'vitest';
|
||||
import { gameJoined } from './gameJoined';
|
||||
import { notifyUser } from './notifyUser';
|
||||
|
|
@ -78,9 +78,6 @@ import { serverIdentification } from './serverIdentification';
|
|||
|
||||
const ConfigMock = Config as { -readonly [K in keyof typeof Config]: (typeof Config)[K] };
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// gameJoined
|
||||
// ----------------------------------------------------------------
|
||||
describe('gameJoined', () => {
|
||||
|
||||
it('calls WebClient.instance.response.session.gameJoined', () => {
|
||||
|
|
@ -90,9 +87,6 @@ describe('gameJoined', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// notifyUser
|
||||
// ----------------------------------------------------------------
|
||||
describe('notifyUser', () => {
|
||||
|
||||
it('calls WebClient.instance.response.session.notifyUser', () => {
|
||||
|
|
@ -102,9 +96,6 @@ describe('notifyUser', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// replayAdded
|
||||
// ----------------------------------------------------------------
|
||||
describe('replayAdded', () => {
|
||||
|
||||
it('calls WebClient.instance.response.session.replayAdded with matchInfo', () => {
|
||||
|
|
@ -116,9 +107,6 @@ describe('replayAdded', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// serverCompleteList
|
||||
// ----------------------------------------------------------------
|
||||
describe('serverCompleteList', () => {
|
||||
|
||||
it('calls WebClient.instance.response.session.updateUsers and WebClient.instance.response.room.updateRooms', () => {
|
||||
|
|
@ -129,9 +117,6 @@ describe('serverCompleteList', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// serverMessage
|
||||
// ----------------------------------------------------------------
|
||||
describe('serverMessage', () => {
|
||||
|
||||
it('calls WebClient.instance.response.session.serverMessage with message', () => {
|
||||
|
|
@ -140,9 +125,6 @@ describe('serverMessage', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// serverShutdown
|
||||
// ----------------------------------------------------------------
|
||||
describe('serverShutdown', () => {
|
||||
|
||||
it('calls WebClient.instance.response.session.serverShutdown', () => {
|
||||
|
|
@ -152,9 +134,6 @@ describe('serverShutdown', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// userJoined
|
||||
// ----------------------------------------------------------------
|
||||
describe('userJoined', () => {
|
||||
|
||||
it('calls WebClient.instance.response.session.userJoined with userInfo', () => {
|
||||
|
|
@ -166,9 +145,6 @@ describe('userJoined', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// userLeft
|
||||
// ----------------------------------------------------------------
|
||||
describe('userLeft', () => {
|
||||
|
||||
it('calls WebClient.instance.response.session.userLeft with name', () => {
|
||||
|
|
@ -177,9 +153,6 @@ describe('userLeft', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// userMessage
|
||||
// ----------------------------------------------------------------
|
||||
describe('userMessage', () => {
|
||||
|
||||
it('calls WebClient.instance.response.session.userMessage', () => {
|
||||
|
|
@ -189,9 +162,6 @@ describe('userMessage', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// addToList
|
||||
// ----------------------------------------------------------------
|
||||
describe('addToList', () => {
|
||||
let logSpy: ReturnType<typeof vi.spyOn>;
|
||||
beforeEach(() => {
|
||||
|
|
@ -225,9 +195,6 @@ describe('addToList', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// removeFromList
|
||||
// ----------------------------------------------------------------
|
||||
describe('removeFromList', () => {
|
||||
|
||||
it('buddy list → removeFromBuddyList', () => {
|
||||
|
|
@ -248,9 +215,6 @@ describe('removeFromList', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// listRooms
|
||||
// ----------------------------------------------------------------
|
||||
describe('listRooms', () => {
|
||||
|
||||
it('calls WebClient.instance.response.room.updateRooms', () => {
|
||||
|
|
@ -279,9 +243,6 @@ describe('listRooms', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// connectionClosed
|
||||
// ----------------------------------------------------------------
|
||||
describe('connectionClosed', () => {
|
||||
|
||||
it('uses reasonStr when provided', () => {
|
||||
|
|
@ -371,9 +332,6 @@ describe('connectionClosed', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// serverIdentification
|
||||
// ----------------------------------------------------------------
|
||||
describe('serverIdentification', () => {
|
||||
const makeInfo = (overrides: Record<string, unknown> = {}) =>
|
||||
create(Event_ServerIdentificationSchema, {
|
||||
|
|
|
|||
|
|
@ -1,32 +1,10 @@
|
|||
export * from './commands';
|
||||
export * from './interfaces';
|
||||
|
||||
export { WebClient } from './WebClient';
|
||||
export { StatusEnum } from './interfaces/StatusEnum';
|
||||
export type { WebClientConfig, ConnectTarget } from './interfaces/WebClientConfig';
|
||||
export type {
|
||||
KeyOf,
|
||||
GameEventMeta,
|
||||
WebSocketSessionResponseOverrides,
|
||||
WebSocketRoomResponseOverrides,
|
||||
} from './interfaces/WebSocketConfig';
|
||||
|
||||
export { SessionEvents } from './events/session';
|
||||
export { RoomEvents } from './events/room';
|
||||
export { GameEvents } from './events/game';
|
||||
|
||||
export { generateSalt, passwordSaltSupported, hashPassword } from './utils';
|
||||
|
||||
export { WebSocketConnectReason } from './interfaces/ConnectOptions';
|
||||
export type {
|
||||
LoginConnectOptions,
|
||||
RegisterConnectOptions,
|
||||
ActivateConnectOptions,
|
||||
PasswordResetRequestConnectOptions,
|
||||
PasswordResetChallengeConnectOptions,
|
||||
PasswordResetConnectOptions,
|
||||
TestConnectionOptions,
|
||||
WebSocketConnectOptions,
|
||||
} from './interfaces/ConnectOptions';
|
||||
|
||||
export { setPendingOptions, consumePendingOptions } from './utils/connectionState';
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
import type {
|
||||
GameEventContext,
|
||||
Response_Login,
|
||||
Response,
|
||||
Event_RoomSay,
|
||||
ResponseMap,
|
||||
RoomEventMap,
|
||||
} from '@app/generated';
|
||||
|
||||
// ── KeyOf utility ────────────────────────────────────────────────────────────
|
||||
// Derives a type map key from a generated type. Allows interface methods to
|
||||
// reference generated types instead of hardcoded string keys.
|
||||
//
|
||||
// T[KeyOf<ResponseMap, Response_Login>]
|
||||
// ↓ resolves to ↓
|
||||
// T['Response_Login']
|
||||
|
||||
export type KeyOf<Map, V> = { [K in keyof Map]: Map[K] extends V ? K : never }[keyof Map];
|
||||
|
||||
// ── GameEventMeta ────────────────────────────────────────────────────────────
|
||||
// Per-container metadata passed to every game event handler alongside the
|
||||
// event payload. Constructed by ProtobufService.processGameEvent from the
|
||||
// GameEventContainer fields. Structurally identical to Enriched.GameEventMeta.
|
||||
|
||||
export interface GameEventMeta {
|
||||
gameId: number;
|
||||
playerId: number;
|
||||
context: GameEventContext | null;
|
||||
secondsElapsed: number;
|
||||
forcedByJudge: number;
|
||||
}
|
||||
|
||||
// ── Websocket-layer enrichments ──────────────────────────────────────────────
|
||||
// Protocol-level enrichments of proto types — these are websocket concerns,
|
||||
// not app concerns. Used as the DEFAULT generic on the response interfaces.
|
||||
|
||||
export interface WebSocketSessionResponseOverrides extends ResponseMap {
|
||||
Response_Login: Response_Login & { hashedPassword?: string };
|
||||
Response: Response & { host: string; port: string; userName: string };
|
||||
}
|
||||
|
||||
export interface WebSocketRoomResponseOverrides extends RoomEventMap {
|
||||
Event_RoomSay: Event_RoomSay & { timeReceived: number };
|
||||
}
|
||||
|
|
@ -510,10 +510,6 @@ describe('ProtobufService', () => {
|
|||
|
||||
});
|
||||
|
||||
// ── Real protobuf round-trip test ─────────────────────────────────────────────
|
||||
// This describe block does NOT mock @bufbuild/protobuf so it exercises real
|
||||
// binary serialization. It proves that the schemas ProtobufService uses
|
||||
// survive a toBinary → fromBinary cycle without data loss.
|
||||
describe('ProtobufService protobuf round-trip (real @bufbuild/protobuf)', () => {
|
||||
it('CommandContainer round-trips cmdId through toBinary → fromBinary', async () => {
|
||||
const { create, toBinary, fromBinary: realFromBinary } =
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import {
|
|||
import { GameEvents } from '../events/game';
|
||||
import { RoomEvents } from '../events/room';
|
||||
import { SessionEvents } from '../events/session';
|
||||
import type { GameEventMeta } from '../interfaces/WebSocketConfig';
|
||||
import type { GameEventMeta } from '../types/WebSocketConfig';
|
||||
import { type CommandOptions, handleResponse } from './command-options';
|
||||
|
||||
export interface SocketTransport {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ vi.mock('../config', () => ({
|
|||
import { WebSocketService } from './WebSocketService';
|
||||
import type { WebSocketServiceConfig } from './WebSocketService';
|
||||
import { KeepAliveService } from './KeepAliveService';
|
||||
import { StatusEnum } from '../interfaces/StatusEnum';
|
||||
import { StatusEnum } from '../types/StatusEnum';
|
||||
|
||||
type WebSocketInternal = WebSocketService & {
|
||||
keepAliveService: KeepAliveService;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { Subject } from 'rxjs';
|
||||
|
||||
import { StatusEnum } from '../interfaces/StatusEnum';
|
||||
import { StatusEnum } from '../types/StatusEnum';
|
||||
import { KeepAliveService } from './KeepAliveService';
|
||||
import { CLIENT_OPTIONS } from '../config';
|
||||
import type { ConnectTarget } from '../interfaces/WebClientConfig';
|
||||
import type { ConnectTarget } from '../types/WebClientConfig';
|
||||
|
||||
export interface WebSocketServiceConfig {
|
||||
keepAliveFn: (pingReceived: () => void) => void;
|
||||
|
|
@ -16,7 +16,7 @@ export class WebSocketService {
|
|||
|
||||
private config: WebSocketServiceConfig;
|
||||
private keepAliveService: KeepAliveService;
|
||||
private errorFired = false;
|
||||
private hasReportedError = false;
|
||||
|
||||
public message$: Subject<MessageEvent> = new Subject();
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ export class WebSocketService {
|
|||
|
||||
socket.onopen = () => {
|
||||
clearTimeout(connectionTimer);
|
||||
this.errorFired = false;
|
||||
this.hasReportedError = false;
|
||||
this.config.onStatusChange(StatusEnum.CONNECTED, 'Connected');
|
||||
|
||||
this.keepAliveService.startPingLoop(this.keepalive, (pingReceived: () => void) => {
|
||||
|
|
@ -77,16 +77,17 @@ export class WebSocketService {
|
|||
};
|
||||
|
||||
socket.onclose = () => {
|
||||
// dont overwrite failure messages
|
||||
if (!this.errorFired) {
|
||||
// @critical onerror + onclose both fire on failed connects; don't overwrite the richer error status.
|
||||
// See .github/instructions/webclient.instructions.md#websocket-lifecycle.
|
||||
if (!this.hasReportedError) {
|
||||
this.config.onStatusChange(StatusEnum.DISCONNECTED, 'Connection Closed');
|
||||
}
|
||||
this.errorFired = false;
|
||||
this.hasReportedError = false;
|
||||
this.keepAliveService.endPingLoop();
|
||||
};
|
||||
|
||||
socket.onerror = () => {
|
||||
this.errorFired = true;
|
||||
this.hasReportedError = true;
|
||||
this.config.onStatusChange(StatusEnum.DISCONNECTED, 'Connection Failed');
|
||||
this.config.onConnectionFailed();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,12 +10,6 @@ import { create, getExtension } from '@bufbuild/protobuf';
|
|||
|
||||
import { handleResponse } from './command-options';
|
||||
|
||||
// NOTE: do NOT call `vi.resetAllMocks()` here — under `isolate: false` it
|
||||
// resets `vi.fn()` implementations set inside other files' `vi.mock(...)`
|
||||
// factories, which breaks any spec that relied on those factory defaults
|
||||
// (e.g. ProtobufService.spec.ts expects `hasExtension` to return `false`).
|
||||
// The root `setupTests.ts` afterEach already calls `vi.clearAllMocks()`.
|
||||
|
||||
describe('handleResponse', () => {
|
||||
it('calls onResponse and returns early when provided', () => {
|
||||
const onResponse = vi.fn();
|
||||
|
|
|
|||
|
|
@ -10,12 +10,6 @@ export enum WebSocketConnectReason {
|
|||
TEST_CONNECTION,
|
||||
}
|
||||
|
||||
// ── Connect options ───────────────────────────────────────────────────────────
|
||||
// Each variant is the enriched input for one session flow: the network
|
||||
// transport fields (host/port) + the subset of proto Command_* fields the UI
|
||||
// actually produces (user-entered credentials, tokens, email, etc.) + a
|
||||
// `reason` discriminator so the websocket layer can route.
|
||||
|
||||
interface ConnectTransport extends ConnectTarget {
|
||||
keepalive?: number;
|
||||
autojoinrooms?: boolean;
|
||||
17
webclient/src/websocket/types/SignalContexts.ts
Normal file
17
webclient/src/websocket/types/SignalContexts.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* Context preserved through the ACCOUNT_AWAITING_ACTIVATION signal so the
|
||||
* activation dialog can resubmit against the same host/user without re-entering them.
|
||||
*/
|
||||
export interface PendingActivationContext {
|
||||
host: string;
|
||||
port: string;
|
||||
userName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Payload for the LOGIN_SUCCESSFUL signal. Only carries what the UI needs to
|
||||
* persist into the selected host record (hashedPassword for "remember me").
|
||||
*/
|
||||
export interface LoginSuccessContext {
|
||||
hashedPassword?: string;
|
||||
}
|
||||
|
|
@ -39,7 +39,6 @@ import type {
|
|||
import type { ConnectTarget } from './WebClientConfig';
|
||||
import type { KeyOf } from './WebSocketConfig';
|
||||
|
||||
// ── Auth request type map ────────────────────────────────────────────────────
|
||||
// Keys = generated *Params type names composed with ConnectTarget.
|
||||
// @app/api overrides these with Enriched connect option types.
|
||||
|
||||
28
webclient/src/websocket/types/WebSocketConfig.ts
Normal file
28
webclient/src/websocket/types/WebSocketConfig.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import type {
|
||||
GameEventContext,
|
||||
Response_Login,
|
||||
Response,
|
||||
Event_RoomSay,
|
||||
ResponseMap,
|
||||
RoomEventMap,
|
||||
} from '@app/generated';
|
||||
|
||||
// `KeyOf<ResponseMap, Response_Login>` resolves to `'Response_Login'`.
|
||||
export type KeyOf<Map, V> = { [K in keyof Map]: Map[K] extends V ? K : never }[keyof Map];
|
||||
|
||||
export interface GameEventMeta {
|
||||
gameId: number;
|
||||
playerId: number;
|
||||
context: GameEventContext | null;
|
||||
secondsElapsed: number;
|
||||
forcedByJudge: number;
|
||||
}
|
||||
|
||||
export interface WebSocketSessionResponseOverrides extends ResponseMap {
|
||||
Response_Login: Response_Login & { hashedPassword?: string };
|
||||
Response: Response & { host: string; port: string; userName: string };
|
||||
}
|
||||
|
||||
export interface WebSocketRoomResponseOverrides extends RoomEventMap {
|
||||
Event_RoomSay: Event_RoomSay & { timeReceived: number };
|
||||
}
|
||||
1
webclient/src/websocket/types/index.ts
Normal file
1
webclient/src/websocket/types/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * as WebsocketTypes from './namespace';
|
||||
|
|
@ -21,3 +21,5 @@ export type {
|
|||
export * from './WebClientConfig';
|
||||
export * from './WebSocketConfig';
|
||||
export * from './StatusEnum';
|
||||
export * from './ConnectOptions';
|
||||
export * from './SignalContexts';
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import type { WebSocketConnectOptions } from '../interfaces/ConnectOptions';
|
||||
import type { WebSocketConnectOptions } from '../types/ConnectOptions';
|
||||
|
||||
let pendingOptions: WebSocketConnectOptions | null = null;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ const SALT_LENGTH = 16;
|
|||
export const hashPassword = (salt: string, password: string): string => {
|
||||
let hashedPassword = salt + password;
|
||||
for (let i = 0; i < HASH_ROUNDS; i++) {
|
||||
// WHY DO WE DO IT THIS WAY?
|
||||
hashedPassword = sha512(hashedPassword);
|
||||
}
|
||||
|
||||
|
|
@ -27,6 +26,6 @@ export const generateSalt = (): string => {
|
|||
}
|
||||
|
||||
export const passwordSaltSupported = (serverOptions: number): number => {
|
||||
// Intentional use of Bitwise operator b/c of how Servatrice Enums work
|
||||
// @critical Servatrice ServerOptions is a bitmask. See .github/instructions/webclient.instructions.md#protocol-quirks.
|
||||
return serverOptions & Event_ServerIdentification_ServerOptions.SupportsPasswordHash;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue